【问题标题】:Boost Asio io_service destructor hangs on OS XBoost Asio io_service 析构函数在 OS X 上挂起
【发布时间】:2014-02-17 17:39:28
【问题描述】:

我在 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 析构函数无限期挂起,特别是在kqueue_reactor 析构过程中的pipe_select_interrupter 析构函数中。这会在管道读取描述符上的系统调用 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 上重现(使用相同的可执行文件)。

【问题讨论】:

  • 已确认。我尝试使用循环(一次性验证)且没有 gmtime_r 的简化版本:link
  • @ruslo:太棒了,谢谢 - 到目前为止,如果没有 gmtime_r 调用,我无法重现,但我会再试一次。
  • @ruslo:有趣的是,如果我使用脚本运行(我运行了大约一个小时),没有 gmtime_r 调用就无法重现。但是,如果我像您的示例一样使用 for 循环运行,即使没有 gmtime_r 调用,我也能够重现它。

标签: c++ multithreading macos boost boost-asio


【解决方案1】:

已于 2014 年 4 月 29 日在主分支中向 Asio 提交了针对此问题的修复

修复 MacOS 上偶尔的 close() 系统调用挂起问题。

kqueue 事件过滤器的重复重新注册似乎表现为 尽管 MacOS 上存在某种“泄漏”,最终导致暂停 close() 系统调用和无法杀死的进程。为了避免这种情况,我们将 只注册一次描述符的 kqueue 事件过滤器,即当 首先创建描述符。

【讨论】:

  • 啊,是的。感谢您花时间写这个答案 - 我忘了在这里发布这个信息。请注意,此修复也在 Boost 1.56 及更高版本中(但如果我没记错的话,它会在某些操作系统上引起问题,然后在 1.57 中修复)。
猜你喜欢
  • 2011-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-01
  • 2011-06-16
  • 1970-01-01
  • 2017-09-17
相关资源
最近更新 更多