Unspecified order of std::jthread stop callbacks on request_stop()
0 reputation · 19 Apr 2026, 13:42 UTC
0 reputation · 19 Apr 2026, 13:42 UTC
When a std::jthread registers multiple stop callbacks via std::stop_token::register_callback, the C++20 standard does not specify the order in which those callbacks are invoked when request_stop() is called.
Additionally, the standard does not mandate whether the callbacks execute in the thread that calls request_stop() or in separate, implementation‑defined threads. Many compilers run callbacks in the requesting thread, but this is not guaranteed.
Because the order and execution context are implementation‑dependent, developers cannot rely on a deterministic cleanup sequence when multiple callbacks are registered; this may cause race conditions or deadlocks if callbacks perform blocking operations.
request_stop() is called?request_stop() block until all registered callbacks have completed?34775 reputation · 20 Apr 2026, 00:35 UTC
The C++20 standard does not specify the order in which multiple std::stop_callback objects are invoked when request_stop() is called, nor does it guarantee that the callbacks run on any particular thread. However, the standard does require that the callbacks are invoked synchronously on the thread that calls request_stop(). Therefore, request_stop() will not return until every registered callback has finished (unless a callback throws, which causes std::terminate).
request_stop() performs the callbacks synchronously in the thread that invoked it; no additional threads are created for this purpose.request_stop() blocks until all callbacks have completed (or until one throws, triggering std::terminate).request_stop() will appear to block for the duration of that operation because it waits for the callback to return.std::vector.request_stop() on the associated std::jthread, then join() the thread.Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 19 Apr 2026, 18:43 UTC
A detail not covered above: if std::stop_callback is constructed after request_stop() has already been called on the associated std::stop_source, the callback is invoked synchronously on the thread that constructs it, before the stop_callback constructor returns. This applies even when the stop request originated from a different thread.
Consequently, code that lazily registers a cleanup callback after checking stop_requested() will still execute that callback immediately, which can surprise developers who assume the callback only runs on the original request_stop() caller's thread.
This behavior is mandated by the standard ([thread.stopcallback]/4) and is consistent across libstdc++, libc++, and MSVC STL.