lundi 22 juin 2020

ASIO signal_set not reliable with multiple IO threads, depending on code order?

I am using (standalone) ASIO in a program, and in order to shutdown gracefully on Ctrl+C, I use a signal_set. Everything works well when only my main thread calls io_context.run().

Then, I added an option to use several threads for IO. It looks something like this:

// begin block 1
asio::signal_set signals(io_context, SIGINT, SIGTERM);
signals.async_wait([&server, &signals] (const asio::error_code& ec, int signal) {
    std::cerr << "Received signal " << signal << ", exiting" << std::endl;
    server.shutdown();
    signals.clear();
});
// end block 1

// begin block 2
std::vector<std::thread> io_threads;
if (num_io_threads > 1) {
    for (int i = 1; i < num_io_threads; ++i) {
        io_threads.emplace_back([&io_context] () {io_context.run();});
    }
}
// end block 2

io_context.run();

for (auto& thread: io_threads) {
    thread.join();
}

However, when I ran with num_io_threads > 1 and press Ctrl+C, the programs stopped abruptly instead of shutting down gracefully. I thought it may be because the extra threads "stole" the signal, since I have not masked any signal out in those threads.

Then I had a hunch, and reordered the code, moving block 1 below block 2, and sure enough, graceful shutdown works reliably again.

Is this behavior something I can rely on? Specifically, is it because that I created the signal_set and called its async_wait method after creating all threads, that the signal callback is triggered reliably, or is it because of something else? If it's something else, what's the proper solution of having the signal callback triggered reliably?

I tried to find relevant documentation, but couldn't find any. The docs only say that programs must ensure that any signals registered using signal_set objects are unblocked in at least one thread.

Everything is on CentOS 7 with g++ 4.8.5.

Aucun commentaire:

Enregistrer un commentaire