我们有一个实时操作系统,它通过所谓的邮箱提供Inter-Task-Communication。 邮箱由RTKMailbox类型的句柄描述。 API看起来像:
int RTKPut(RTKMailbox h, const void* data);
int RTKGet(RTKMailbox h, void* data);
邮箱知道数据的大小。数据传输可以被认为是从发送者到接收者的记忆。
想象一下,我有一个Producer-Task和一个Consumer-Task;这个系统发送一个shared_ptr是个好主意吗?
由于邮箱不知道shared_ptr,我的想法是将shared_ptr包装在传输结构中。
代码可能如下所示:
class MyData {
//...
};
struct TransportWrapper {
void BeforePut();
void AfterGet();
std::shared_ptr<MyData> Data;
TransportWrapper() {}
TransportWrapper(std::shared_ptr<MyData>& _data) : Data(_data)
{}
};
void Send(RTKMailbox mbHandle, std::shared_ptr<MyData>& data)
{
TransportWrapper wrap(data);
wrap.BeforePut();
RTKPut(mbHandle, &wrap);
}
std::shared_ptr<MyData> Receive(RTKMailbox mbHandle)
{
TransportWrapper wrap;
RTKGet(mbHandle, &wrap);
wrap.AfterGet();
return wrap.Data;
}
如果包装器的生命周期结束,我在BeforePut中需要做什么来防止删除shared_ptr?
在AfterGet中我需要做什么才能将shared_ptr恢复到Put之前的状态?
关心安德烈亚斯
答案 0 :(得分:0)
您的示例代码不起作用,您不能只memcpy
shared_ptr
因为所有这一切都是复制它包含的指针,它不会创建{{{{ 1}}并增加引用计数。您不能将shared_ptr
用于具有非平凡构造函数或析构函数的对象。
假设发件人和收件人共享一个地址空间(因为这通过你的邮箱API几乎无法做到,你需要共享内存),你需要增加memcpy
对发件人的引用次数确保发送方不会丢弃其对拥有对象的最后一次引用,并在接收方收到之前将其删除。然后接收器必须减少引用计数,因此它们需要协调。
如果传递到邮箱是异步的(即发送方在传输完成并且接收方已收到数据之前没有阻塞),则无法使用shared_ptr
函数中的局部变量执行此操作,因为这些变量一旦RTKPut调用返回,它将超出范围,这将在接收器获得之前减少引用计数(并且可能破坏数据)。
解决这个问题的最简单方法是在堆上创建一个新的Send
并传输其地址。
shared_ptr
这假设如果void Send(RTKMailbox mbHandle, const std::shared_ptr<MyData>& data)
{
std::shared_ptr<MyData>* p = new std::shared_ptr<MyData>(data);
if (RTKPut(mbHandle, &p) != success)
{
delete p;
// deal with it
}
}
std::shared_ptr<MyData> Receive(RTKMailbox mbHandle)
{
std::shared_ptr<MyData>* p = nullptr;
if (RTKGet(mbHandle, &p) == success)
{
auto sp = *p;
delete p;
return sp;
}
// else deal with it
}
成功返回,则传递不会失败,否则会泄漏堆上创建的RTKPut
,并且永远不会删除它拥有的对象。