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
错误,而不是更有用的堆栈溢出错误。
我最近从 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
错误,而不是更有用的堆栈溢出错误。