Following the question from here, which explains to some degree why getting a std::stop_token
internally instead of getting it as parameter to the thread wrapper function leads to a race-condition. I want to understand more exactly what is the problem. Looking at the implementation of the jthread ctor in the msvc, it seems that the member _Ssource
is actually constructed before conditionally invoking the correct _Start
, depending if a std::stop_token
a parameter is the first parameter.
jthread() noexcept : _Impl{}, _Ssource{nostopstate} {}
template <class _Fn, class... _Args, enable_if_t<!is_same_v<remove_cvref_t<_Fn>, jthread>, int> = 0>
_NODISCARD_CTOR explicit jthread(_Fn&& _Fx, _Args&&... _Ax) {
if constexpr (is_invocable_v<decay_t<_Fn>, stop_token, decay_t<_Args>...>) {
_Impl._Start(_STD forward<_Fn>(_Fx), _Ssource.get_token(), _STD forward<_Args>(_Ax)...);
} else {
_Impl._Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
}
}
Before the _Start
we are still on the caller thread and all the memory should be visible to
the new thread, which has not yet started. Then on the new thread you would call get_stop_token
auto t = std::jthread{ [&]() {
auto token = t.get_stop_token();
which is implemented like this and should return a copy of the token from the _Ssource
:
_NODISCARD stop_token get_stop_token() const noexcept {
return _Ssource.get_token();
}
Where is the race condition? Is the _Source modified after the new thread was actually started but before the _Start
returns on the original thread?