chriskohlhoff / asio

Asio C++ Library
http://think-async.com/Asio
4.97k stars 1.22k forks source link

`boost::asio::thread_pool` `attach()`/`join()` race condition #1534

Open jivancic opened 1 month ago

jivancic commented 1 month ago

This is somewhat related to an issue I reported a while ago: https://github.com/chriskohlhoff/asio/pull/1230

The following code has a potential deadlock:

asio::thread_pool pool(0);
std::thread thread([&pool]() { pool.attach(); });
pool.join();
thread.join();

The reason is the implementation of thread_pool::join():

void thread_pool::join()
{
  if (num_threads_)
    scheduler_.work_finished();

  if (!threads_.empty())
    threads_.join();
}

The problem is the first conditional -- as attach() happens in a separate thread, it can happen after the first if check. In that case num_threads_ was 0 & scheduler_.work_finished() never got called. Both threads get stuck because attach() never returns.

Now, calling pool.wait() instead of pool.join() fixes the problem as it calls scheduler_.work_finished() unconditionally.

Documentation for both join() & wait() is the same, but from the implementation it looks like

I'm having trouble understanding this if condition. Would it be wrong to call scheduler_.work_finished() even if number of threads seems to be 0?

Ideally for me the implementation for join would be:

void thread_pool::join() { wait(); }