Simulink“访问冲突”写入C ++ lambda函数捕获列表中的PWork变量

问题描述 投票:1回答:2

我有一个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([&copy_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中之前,相同的代码在独立的可执行文件中工作。

c++ matlab c++11 simulink
2个回答
1
投票

您正在通过引用捕获copy_buffer_ptr(堆栈局部变量)。一旦mdlStart返回,该引用将悬空,之后调用lambda是未定义的行为。 (这也适用于assembler)。

解决方法是简单地按值捕获copy_buffer_ptrassembler(它们是简单的指针,你可以只复制它们而不会出现问题):

server->attachDataHandler([copy_buffer_ptr, assembler](const ThreadedDataServer::buffer_t & buffer, size_t num_bytes)
{
  /* etc. */
});

如果你有一个比当前范围更长的lambda,那么在通过引用捕获任何东西之前要仔细思考 - 如果它是堆栈本地的,你可能会被烧毁。


0
投票

感谢@ 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);
        }
    });

该实现解决了两个问题:

  • 捕获列表中的变量引用了堆栈中的指针,该指针以悬空指针结束并导致崩溃。
  • 对数据处理程序的几次调用同时访问缓冲区和Assembler对象,显然导致了不同的访问冲突。

它现在正在工作:-)

© www.soinside.com 2019 - 2024. All rights reserved.