FreeRTOS中的共享指针和队列

8
一个围绕FreeRTOS队列的C++封装可以简化为以下内容:

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;
}

xQueueSendToBack的文档说明:

该项通过复制而非引用进行排队。

不幸的是,它实际上是通过复制来完成的,因为最终都会使用memcpy,这在C API中很有意义。虽然这对于普通数据很有效,但对于以下事件消息等更复杂的项目会带来严重问题。

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;
};

问题显然出在`std::shared_ptr`上,因为它与`memcpy`根本不兼容,由于复制到队列时未调用其复制构造函数/赋值运算符,导致当事件消息(因此是shared_ptr)超出范围时,所持有的对象被过早地删除。
我可以通过使用动态分配的`T`实例并将队列更改为仅包含指向该实例的指针来解决这个问题,但我不想这样做,因为这将在嵌入式系统上运行,并且我非常希望在运行时保持内存静态。
我的当前计划是将队列更改为包含指向包装类中本地保留的内存区域的指针,在其中可以实现完整的C++对象复制,但是因为我还需要保护该内存区域免受多线程访问,这实际上破坏了已经线程安全的FreeRTOS队列实现(肯定比我自己编写的任何实现都更有效),我可能会跳过它们。
最后,问题是:
在我实现自己的队列之前,是否有任何技巧可以使FreeRTOS队列与C ++对象实例(特别是`std::shared_ptr`)一起使用?

描述此问题的标准方式是,您的C样式队列仅与std :: is_trivial <T> :: value为true的C ++类型兼容。 C ++可能会在将其复制到队列中时勤奋地增加shared_ptr引用计数,但是您的队列永远不会将其减少,从而导致泄漏。 这是因为C样式代码不调用构造函数,析构函数或赋值运算符。 TL; DR:向此对象发送shared_ptr没有意义。 - hoodaticus
是的,这样用更少的词概括了它 :) - Per
是的,但恐怕这就是所有可以做的了。您已经确定了唯一真正的解决方案,即发送原始指针,然后在其他地方自己管理内存 :/ - hoodaticus
是的,我也相信这是情况。另一方面,我之前曾经被 Stack Overflow 上某些人的创造力惊艳到,所以可能会有人有一些很酷的技巧。 - Per
1个回答

0
问题在于将指针放入队列后原始数据会发生什么变化。 复制看起来很简单,但并不是最优解。
为了解决这个问题,我使用一个邮箱而不是队列:
T* data = (T*) osMailAlloc(m_mail, osWaitForever);
...
osMailPut (m_mail, data);

在开始时显式分配指针的位置。然后将指针添加到邮箱中。

要检索:

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

所有的内容都可以整洁地包装成C++模板方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接