CLang:std::thread 中的 运行 函数在结构创建时导致 BAD_ACCESS

CLang: Running function in std::thread causes BAD_ACCESS on struct creation

我最近从 Kubuntu 16.04 切换到 Mac OS High Sierra,并开始定期移植我使用的部分代码。其中有一个用于 libsctp-dev 的 C++ 包装器库。

我正在使用 SCTP kernel extension 来获得与我的 Linux 机器上相同的 SCTP API。虽然 API 调用不会产生问题,但使用标准 C++ 库提供的线程现在似乎会产生问题。可以在以下代码片段中找到问题:

void Server::start(int32_t port) {
    //This allocates the socket with the SCTP protocol assigned
    Receiver::start();
    //This function now binds the desired port to the created socket
    this->_bind(port);

    //Here I register handlers for network events that can occur
    this->notificationHandler.setAssocChangeHandler(std::bind(&Server::handleAssocChange, this, _1));
    this->notificationHandler.setShutdownEventHandler(std::bind(&Server::handleShutdownEvent, this, _1));
    this->notificationHandler.setSendFailedHandler(std::bind(&Server::handleSendFailed, this, _1));

    //This is the interesting part - this will lead to BAD_ACCESS
    //exceptions in the receive function
    dummy = this;
    this->receiveThread = std::thread(dummyReceive);

    //However, if I run it in the same thread, everything works fine
    //(except that I need the receive loop to run in a separate thread)
    //dummyReceive();

    //This is the original call, I just used the dummy function to be
    //sure that the bind function does not create the problem
    //this->receiveThread = std::thread(std::bind(&Server::receive, this));
}

这是定义 dummyReceive 函数的部分:

Server *dummy = NULL;
void dummyReceive(){
    dummy->receive();
}

最后是receive方法的代码(Server是Receiver的子类,Receiver又是Endpoint的子类):

void Receiver::receive() {

    uint8_t buffer[this->max_buffer_size];
    uint32_t buffer_size = 0;

    struct sockaddr_in peer_addr = {};
    socklen_t peer_addr_size = sizeof(peer_addr);

    struct sctp_sndrcvinfo info = {};
    int32_t flags = 0;

    while (this->can_receive) {
        buffer_size = Endpoint::receive(buffer, max_buffer_size, peer_addr, peer_addr_size, info, flags);
        if (buffer_size == 0) {
            // Notification was sent
        } else if (buffer_size == -1) {
            CERR("Endpoint::receive(...) returned -1" << std::endl);
        } else {
            this->receiveCallback(buffer, buffer_size, peer_addr, peer_addr_size, info);
        }
    }
}

奇怪的是"peer_addr"初始化时出现BAD_ACCESS异常:

struct sockaddr_in peer_addr = {};

这是 CLion 给我的错误消息:

EXC_BAD_ACCESS (code=1, address=0x70000807bb88)

我可以通过在函数开头初始化 "peer_addr" 和 "info" 结构来避免这种情况。但是,随后对 "Endpoint::receive" 的调用再次崩溃并出现 BAD_ACCESS 异常。这次使用以下参数:

EXC_BAD_ACCESS (code=1, address=0x70000c4adb90)

有人知道这里出了什么问题吗?我正在使用 Xcode 工具链 9.4.1(据我所知,它在内部使用 clang)和 CMake 3.12.0(我将 CLion 用作 IDE)。如果有人需要完整的库代码,我可以将其上传到 git 并共享一个 link(目前它仅在私人 git 服务器上)。

最佳 帕斯卡

如果 max_buffer_size 很大,您可能会遇到堆栈溢出。

堆栈的大小非常有限,OSX 上的堆栈可能比 Linux 上的堆栈小(例如,默认的 pthread 堆栈大小仅为 512K https://developer.apple.com/library/archive/qa/qa1419/_index.html)。

大型缓冲区应该分配在堆上而不是分配在堆栈上。

OSX 不太擅长检测堆栈溢出,并且经常引发令人困惑的 EXC_BAD_ACCESS 错误,而不是更有用的堆栈溢出错误。