c++ - Boost Asio io_service 析构函数卡在 OS X 上

标签 c++ multithreading macos boost boost-asio

我在 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()可能关闭。

我在上面的代码中做错了什么吗?

我在带有 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

Fix occasional close() system call hang on MacOS.

Repeated re-registration of kqueue event filters seems to behave as though there is some kind of "leak" on MacOS, culminating in a suspended close() system call and an unkillable process. To avoid this, we will register a descriptor's kqueue event filters once only i.e. when the descriptor is first created.

关于c++ - Boost Asio io_service 析构函数卡在 OS X 上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21835749/

相关文章:

c++ - 如何在C++中制作一棵树?

java - 线程 "something thread"java.lang.OutOfMemoryError : Java heap space. 中的异常我该怎么办?

objective-c - 具有长输出的 NSTask (OSX)

c++ - 使用保存的客户端 ip 地址将数据包从服务器发送到特定客户端?

c++ - 在初始化函数中初始化静态类成员

c++ - 创建 __stdcall 的映射

python:读取线程中的子进程输出

java - 为什么对于 Thread 的子类来说吞咽 InterruptedException 是可以的?

c - 使用调试器 gdb 时出现未知结束信号

macos - osx 音频单元多 channel 输出