我有一个C ++ S-Function,它通过std :: thread,std :: async和callbacks处理一组线程操作。问题是其中一个回调是一个S函数,它在捕获列表中有一个缓冲区。此缓冲区位于Simulink模型的PWork中。但是,一旦我尝试写入它,似乎Matlab崩溃了。
下面是我的S-Function(只有mdlStart函数)的最小崩溃示例,其中包含相关代码:
static void mdlStart(SimStruct *S)
{
ssGetPWork(S)[0] = (void *) new ThreadedDataServer();
ssGetPWork(S)[1] = (void *) new DatagramAssembler();
ssGetPWork(S)[2] = (void *) new MyBufferType(); // actually an std::array<char, LARGENUMBER>
auto server = (ThreadedDataServer *) ssGetPWork(S)[0];
auto assembler = (DatagramAssembler*) ssGetPWork(S)[1];
auto copy_buffer_ptr = (MyBufferType *) ssGetPWork(S)[2];
server->attachDataHandler([©_buffer_ptr, &assembler](const ThreadedDataServer::buffer_t & buffer, size_t num_bytes)
{
/* Minimal crashing action */
copy_buffer_ptr->at(5) = 'b'; // Any index != 0
/* Original code */
//std::copy(buffer.begin(), buffer.begin() + num_bytes, copy_buffer_ptr->data());
//assembler->feedData(*copy_buffer_ptr, num_bytes);
});
}
从数据服务器工作线程调用处理程序(与Simulink主线程不同)。回调函数内的其他操作可以顺利进行(读取参数,执行其他操作......)。
任何暗示为什么会这样?在将其集成到Simulink中之前,相同的代码在独立的可执行文件中工作。
您正在通过引用捕获copy_buffer_ptr
(堆栈局部变量)。一旦mdlStart
返回,该引用将悬空,之后调用lambda是未定义的行为。 (这也适用于assembler
)。
解决方法是简单地按值捕获copy_buffer_ptr
和assembler
(它们是简单的指针,你可以只复制它们而不会出现问题):
server->attachDataHandler([copy_buffer_ptr, assembler](const ThreadedDataServer::buffer_t & buffer, size_t num_bytes)
{
/* etc. */
});
如果你有一个比当前范围更长的lambda,那么在通过引用捕获任何东西之前要仔细思考 - 如果它是堆栈本地的,你可能会被烧毁。
感谢@ max-langhof回答(悬空指针)和一些额外的工作,我终于达成了一个解决方案:
static void mdlStart(SimStruct *S)
{
ssGetPWork(S)[0] = (void *) new ThreadedDataServer();
ssGetPWork(S)[1] = (void *) new DatagramAssembler();
ssGetPWork(S)[2] = (void *) new MyBufferType(); // actually an std::array<char, LARGENUMBER>
ssGetPWork(S)[3] = (void *) new std::mutex();
auto server = (ThreadedDataServer *) ssGetPWork(S)[0];
auto assembler = (DatagramAssembler*) ssGetPWork(S)[1];
auto copy_buffer_ptr = (MyBufferType *) ssGetPWork(S)[2];
auto assembly_mutex = (std::mutex *) ssGetPWork(S)[3];
server->attachDataHandler([copy_buffer_ptr, assembler, assembly_mtx](const ThreadedDataServer::buffer_t & buffer, size_t num_bytes)
{
// Mutex scoped lock
{
std::lock_guard<std::mutex> lckg(assembly_mutex);
std::copy(buffer.begin(), buffer.begin() + num_bytes, copy_buffer_ptr.data());
assembler.feedData(copy_buffer_ptr, num_bytes);
}
});
该实现解决了两个问题:
它现在正在工作:-)