vil*_*pam 8 c++ macos multithreading boost boost-asio
我在OS X上遇到了Boost Asio的问题,其中io_service析构函数有时会无限期挂起.我有一个相对简单的repro案例:
#include <boost/asio.hpp>
#include <boost/thread.hpp>
int main(int argc, char* argv[]) {
timeval tv;
gettimeofday(&tv, 0);
std::time_t t = tv.tv_sec;
std::tm curr;
// The call to gmtime_r _seems_ innocent, but I cannot reproduce without this
std::tm* curr_ptr = gmtime_r(&t, &curr);
{
boost::asio::io_service ioService;
boost::asio::deadline_timer timer(ioService);
ioService.post([&](){
// This will also call gmtime_r, but just calling that is not enough
timer.expires_from_now(boost::posix_time::milliseconds(1));
timer.async_wait([](const boost::system::error_code &) {});
});
ioService.post([&](){
ioService.post([&](){});
});
// Run some threads
boost::thread_group workers;
for (auto i=0; i<3; ++i) {
workers.create_thread([&](){ ioService.run(); });
}
workers.join_all();
} // hangs here in the io_service destructor
return 0;
}
Run Code Online (Sandbox Code Playgroud)
基本上,这只是在队列上发布两个处理程序,其中一个调度计时器而另一个只发布另一个处理程序.有时这个简单的程序会导致io_service
析构函数无限期地挂起,特别是在pipe_select_interrupter
销毁过程中的kqueue_reactor
析构函数中.这会阻塞系统调用close()
管道读取描述符.
要触发错误,我使用shell脚本在循环中调用程序(但也可以在上面的示例中使用循环触发):
#!/bin/csh
set yname="foo"
while ( $yname != "" )
date
./hangtest
end
Run Code Online (Sandbox Code Playgroud)
如果我:我不再能够复制:
gmtime_r()
在开头删除呼叫(!).编辑:如果我使用脚本运行,这似乎只适用.如果我在程序本身中添加一个循环,我可以在没有该调用的情况下重现它,根据ruslo的注释.async_wait()
处理程序中对计时器的调用,或将计时器设置移到处理程序之外.post()
第二个处理程序.kqueue_reactor::interrupt()
.该函数是从和调用async_wait()
的post()
,并且kevent()
使用读取描述符调用,然后无法关闭.我在上面的代码中做错了吗?
我使用Boost 1.54在OS X 10.8.5上运行并使用clang -stdlib=libc++ -std=c++11
.我还可以使用Boost 1.55中的Boost Asio重现(其余的Boost 1.54保持原样).
编辑:我也可以在OS X 10.9.1上重现(使用相同的可执行文件).
此问题的修复已于 2014 年 4 月 29 日提交给主分支中的 Asio
修复 MacOS 上偶尔挂起的 close() 系统调用。
重复重新注册 kqueue 事件过滤器的行为似乎在 MacOS 上存在某种“泄漏”,最终导致挂起的 close() 系统调用和不可终止的进程。为了避免这种情况,我们将仅注册描述符的 kqueue 事件过滤器一次,即在首次创建描述符时。