c++c++20stdthread

Using external stop control in std::jthread to avoid duplication


Unlike std::thread, the jthread logically holds an internal private member of type std::stop_source, which maintains a shared stop-state. (cppreference link)

I want to use an external std::stop_source to control the stop behaviour of multiple threads in a coordinated way.

Example:

void thread_function(std::stop_token stoken)
{
  while (!stoken.stop_requested())
  {
    // Do something
  }
}

int main()
{
  std::stop_source ssource;  // common stop source
  std::jthread t1(thread_function, ssource.get_token());
  std::jthread t2(thread_function, ssource.get_token());

  // ...

  ssource.request_stop();

  // ...
}

If I provide an std::stop_token to a jthread's constructor, does this prevent the jthread from creating its own internal std::stop_source, since an external stop_source already exists?


Solution

  • The jthread constructor merely forwards additional arguments to the function to be called. It does not do anything else with those parameters. In your code there are 3 stop sources, the ones of t1 and t2 are just not used to stop them.

    From cppreference:

    The new thread of execution starts executing:

    std::invoke(decay-copy(std::forward<F>(f)), get_stop_token(),
                decay-copy(std::forward<Args>(args))...)  (until C++23)
    
    std::invoke(auto(std::forward<F>(f)), get_stop_token(),
            auto(std::forward<Args>(args))...)    (since C++23)
    

    if the expression above is well-formed, otherwise starts executing:

    std::invoke(decay-copy(std::forward<F>(f)),
                decay-copy(std::forward<Args>(args))...).     (until C++23)
    
    std::invoke(auto(std::forward<F>(f)),
               auto(std::forward<Args>(args))...).    (since C++23)
    

    In different words: Either the callable you pass as first constructor argument has one parameter more than the number of arguments you pass and can accept a stop_token. Then the constructor will pass its own stop_token. If not, the jthread constructor will not pass its own stop_token but only forwards the arguments you passed.

    In your example, the first variant is not well-formed because your function does take a single stop_token which is included in Args.... Though, the two threads t1 and t2 still have a stop_source you can get via get_stop_source (or their tokens via get_stop_token).