Is there a race condition in GCC's implementation of std::call_once?

3 hours ago 1
ARTICLE AD BOX

The main lines of GCC's implementation of std::call_once are these ones (with _GLIBCXX_HAS_GTHREADS):

'libstdc++-v3/include/std/mutex:939'

enum _Bits : int { _Init = 0, _Active = 1, _Done = 2 };

'libstdc++-v3/include/std/mutex:955-967'

// RAII helper to call _M_finish. struct _Active_execution { explicit _Active_execution(once_flag& __flag) : _M_flag(__flag) { } ~_Active_execution() { _M_flag._M_finish(_M_returning); } _Active_execution(const _Active_execution&) = delete; _Active_execution& operator=(const _Active_execution&) = delete; once_flag& _M_flag; bool _M_returning = false; };

'libstdc++-v3/include/std/mutex:976-1017'

inline bool once_flag::_M_passive() const noexcept { return _M_once == _Bits::_Done; } inline bool once_flag::_M_activate() { if (_M_once == _Bits::_Init) [[__likely__]] { _M_once = _Bits::_Active; return true; } else if (_M_passive()) // Caller should have checked this already. return false; else __throw_system_error(EDEADLK); } inline void once_flag::_M_finish(bool __returning) noexcept { _M_once = __returning ? _Bits::_Done : _Bits::_Init; } /// Invoke a callable and synchronize with other calls using the same flag template<typename _Callable, typename... _Args> inline void call_once(once_flag& __once, _Callable&& __f, _Args&&... __args) { if (__once._M_passive()) return; else if (__once._M_activate()) // <====== Here (1) { once_flag::_Active_execution __exec(__once); // <====== And here (2) // _GLIBCXX_RESOLVE_LIB_DEFECTS // 2442. call_once() shouldn't DECAY_COPY() std::__invoke(std::forward<_Callable>(__f), std::forward<_Args>(__args)...); // __f(__args...) did not throw __exec._M_returning = true; } }

Is'nt it possible that there is a race condition when a thread is entering the block indicated by (1) above if another thread has already entered that block but has not executed the constructor of __exec at(2) yet?

Read Entire Article