c++heap-memorystack-memorymemory-reallocation

Reallocate memory for both stack and heap pointer


I developped a blocking queue class as follow

class Blocking_queue
{
public:
    Blocking_queue();

    int put(void* elem, size_t elem_size);
    int take(void* event);

    unsigned int get_size();

private:

    typedef struct element
    {
        void* elem;
        size_t elem_size;
        struct element* next;
    }element_t;

    std::mutex m_lock;
    std::condition_variable m_condition;
    unsigned int m_size;
    element_t* m_head;
    element_t* m_tail;

};

I want the class to be as generic as possible so I'm using a void pointer which is allocated when the element is added to the queue and freed when removed from it.

int Blocking_queue::take(void* event)
{
    element_t* new_head = NULL;
    int ret = 0;

    // Queue empty
    if(nullptr == m_head)
    {
        // Wait for an element to be added to the queue
        std::unique_lock<std::mutex> unique_lock(m_lock);
        m_condition.wait(unique_lock);
    }

    if(nullptr == realloc(event, m_head->elem_size))
    {
        ret = -1;
    }
    else
    {
        // Take element from queue
        memcpy(event, m_head->elem, m_head->elem_size);
        ret = m_head->elem_size;
        new_head = m_head->next;
        free(m_head->elem);
        free(m_head);
        m_head = new_head;
        if(nullptr == m_head)
        {
            m_tail = nullptr;
        }
        m_size -= 1;
    }
    return ret;
}

If the queue is empty, take() function waits on m_condition until a new element is added.

A pointer event has to be given to copy element's content before freeing it.

To be sure that the given pointer has the right size to copy element's content I reallocate the pointer with its size.

The problem I have with this is that it doesn't allow to pass a function's locale variable because it's allocated on the stack.

So if I do something like this

void function()
{
    unsigned int event = 0;

    queue->take(&event);
}

I'll have a invalid old size error on realloc.

So if I pass a null pointer or a heap allocated variable it'll work but if I pass a stack variable address it won't.

Is there a way to allow stack variable address to be passed to take() function ?


Solution

  • Is there a way to allow stack variable address to be passed to take() function ?

    The short answer is no. malloc()/free()/realloc() can only work with heap-allocated memory; they will not work with stack-allocated memory.

    As for how you might work around this problem, I think it will require some redesign. My first suggestion is to run as far away as possible from (void *) -- void-pointers are extremely unsafe and difficult to use correctly, because the compiler knows nothing about what they point to, and therefore cannot generate errors when the programmer does something incorrectly; this leads to lots of runtime problems. They are more of a C-language construct, still supported in C++ to provide C compatibility, but C++ has better and safer ways to do the same things.

    In particular, if all of the data-elements of your queue are expected to be the same type, then the obvious thing to do would be to make your Blocking_queue class templated with that type as a template-argument; then the user can specify e.g. Blocking_queue<MyFavoriteDataType> and use whatever type he likes, and provide easy-to-use by-value semantics (similar to those provided by e.g. std::vector and friends)

    If you want to allow mixing data-elements of different types, then the best thing to do would be the above again, but define a common base-class for the objects, and then you can instantiate a Blocking_queue<std::shared_ptr<TheCommonBaseClass> > object that will accept shared-pointers to any heap-allocated object of any subclass of that base class. (If you really need to pass shared-pointers to stack-allocated objects, you can do that by defining a custom allocator for the shared pointer, but note that doing so opens the door to object-lifetime-mismatch issues, since the stack objects may be destroyed before they are removed from the queue)