Per
Per

Reputation: 1084

Shared pointers and queues in FreeRTOS

A C++ wapper around a FreeRTOS queue can be simplified into something like this:

template<typename T>
class Queue<T>
{
    public:
    bool push(const T& item)
    {
        return xQueueSendToBack(handle, &item, 0) == pdTRUE;
    }

    bool pop(T& target)
    {
        return xQueueReceive(handle, &target, 0) == pdTRUE;
    }
    private:
    QueueHandle_t handle;
}

The documentation of xQueueSendToBack states:

The item is queued by copy, not by reference.

Unfortunately, it is literally by copy, because it all ends in a memcpy, which makes sense since it is a C API. While this works well for plain old data, more complex items such as the following event message give serious problems.

class ConnectionStatusEvent
{
    public:
        ConnectionStatusEvent() = default;
        ConnectionStatusEvent(std::shared_ptr<ISocket> sock)
            : sock(sock)
              {
              }

              const std::shared_ptr<ISocket>& get_socket() const
              {
                  return sock;
              }

    private:
        const std::shared_ptr<ISocket> sock;
        bool connected;
};

The problem is obviously the std::shared_ptr which doesn't work at all with a memcpy since its copy constructor/assignment operator isn't called when copied onto the queue, resulting in premature deletion of the held object when the event message, and thus the shared_ptr, goes out of scope.

I could solve this by using dynamically allocated T-instances and change the queues to only contain pointers to the instance, but I'd rather not do that since this shall run on an embedded system and I very much want to keep the memory static at run-time.

My current plan is to change the queue to contain pointers to a locally held memory area in the wrapper class in which I can implement full C++ object-copy, but as I'd also need to protect that memory area against multiple thread access, it essentially defeats the already thread-safe implementation of the FreeRTOS queues (which surely are more efficient than any implementation I can write myself) I might as well skip them entirely.

Finally, the question:

Before I implement my own queue, are there any tricks I can use to make the FreeRTOS queues function with C++ object instances, in particular std::shared_ptr?

Upvotes: 8

Views: 1993

Answers (1)

David
David

Reputation: 1

The issue is what happens to the original once you put the pointer into the queue. Copying seems trivial but not optimal.

To get around this issue i use a mailbox instead of a queue:

T* data = (T*) osMailAlloc(m_mail, osWaitForever);
...
osMailPut (m_mail, data);

Where you allocate the pointer explicitly to begin with. And just add the pointer to the mailbox.

And to retrieve:

osEvent ev = osMailGet(m_mail, osWaitForever);
...
osStatus freeStatus = osMailFree(m_mail, p);

All can be neatly warpend into c++ template methods.

Upvotes: 0

Related Questions