我在OS X上遇到Boost Asio的问题,其中io_service析构函数有时无限期挂起。我有一个相对简单的责备案例:
#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;
}
基本上,这只是在队列上发布两个处理程序,其中一个调度计时器而另一个只发布另一个处理程序。有时这个简单的程序导致io_service
析构函数无限期挂起,特别是在pipe_select_interrupter
销毁期间kqueue_reactor
析构函数中。这会阻塞管道读取描述符上的系统调用close()
。
要触发错误,我使用shell脚本在循环中调用程序(但也可以在上面的示例中使用循环触发):
#!/bin/csh
set yname="foo"
while ( $yname != "" )
date
./hangtest
end
如果我:
,我将无法复制gmtime_r()
的调用(!)。编辑:如果我使用脚本运行,这似乎只适用。如果我在程序中添加循环,我可以在没有调用的情况下重现它,根据ruslo的注释。async_wait()
的调用,或将计时器设置移到处理程序之外。post()
。kqueue_reactor::interrupt()
中放置一个互斥锁。从async_wait()
和post()
调用此函数,并使用无法关闭的读取描述符调用kevent()
。我在上面的代码中做错了什么?
我在OS X 10.8.5上使用Boost 1.54运行并使用clang -stdlib=libc++ -std=c++11
进行编译。我还可以使用Boost 1.55中的Boost Asio进行复制(其余的Boost 1.54保持原样)。
编辑:我也可以在OS X 10.9.1上重现(使用相同的可执行文件)。
答案 0 :(得分:1)
对此的修复是在2014年4月29日的主分支中致力于Asio
修复偶尔在MacOS上挂起的close()系统调用。
重复重新注册kqueue事件过滤器似乎表现为 尽管MacOS上存在某种“泄密”,但最终导致暂停 close()系统调用和不可杀死的进程。为了避免这种情况,我们会 只注册描述符的kqueue事件过滤器一次,即 首先创建描述符。