diff --git a/libcxx/include/mutex b/libcxx/include/mutex --- a/libcxx/include/mutex +++ b/libcxx/include/mutex @@ -573,16 +573,19 @@ struct _LIBCPP_TEMPLATE_VIS once_flag { - _LIBCPP_INLINE_VISIBILITY - _LIBCPP_CONSTEXPR - once_flag() _NOEXCEPT : __state_(0) {} - #if defined(_LIBCPP_ABI_MICROSOFT) - typedef uintptr_t _State_type; + enum _State_type : uintptr_t { #else - typedef unsigned long _State_type; + enum _State_type : unsigned long { #endif + UNSET, + PENDING, + COMPLETE = ~_State_type(0) + }; + _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR + once_flag() _NOEXCEPT : __state_(UNSET) {} private: once_flag(const once_flag&); // = delete; @@ -668,7 +671,7 @@ void call_once(once_flag& __flag, _Callable&& __func, _Args&&... __args) { - if (__libcpp_acquire_load(&__flag.__state_) != ~once_flag::_State_type(0)) + if (__libcpp_acquire_load(&__flag.__state_) != once_flag::COMPLETE) { typedef tuple<_Callable&&, _Args&&...> _Gp; _Gp __f(_VSTD::forward<_Callable>(__func), _VSTD::forward<_Args>(__args)...); @@ -684,7 +687,7 @@ void call_once(once_flag& __flag, _Callable& __func) { - if (__libcpp_acquire_load(&__flag.__state_) != ~once_flag::_State_type(0)) + if (__libcpp_acquire_load(&__flag.__state_) != once_flag::COMPLETE) { __call_once_param<_Callable> __p(__func); __call_once(__flag.__state_, &__p, &__call_once_proxy<_Callable>); @@ -696,7 +699,7 @@ void call_once(once_flag& __flag, const _Callable& __func) { - if (__libcpp_acquire_load(&__flag.__state_) != ~once_flag::_State_type(0)) + if (__libcpp_acquire_load(&__flag.__state_) != once_flag::COMPLETE) { __call_once_param __p(__func); __call_once(__flag.__state_, &__p, &__call_once_proxy); diff --git a/libcxx/src/mutex.cpp b/libcxx/src/mutex.cpp --- a/libcxx/src/mutex.cpp +++ b/libcxx/src/mutex.cpp @@ -200,61 +200,65 @@ _LIBCPP_SAFE_STATIC static __libcpp_condvar_t cv = _LIBCPP_CONDVAR_INITIALIZER; #endif -void __call_once(volatile once_flag::_State_type& flag, void* arg, - void (*func)(void*)) -{ -#if defined(_LIBCPP_HAS_NO_THREADS) - if (flag == 0) - { -#ifndef _LIBCPP_NO_EXCEPTIONS - try - { -#endif // _LIBCPP_NO_EXCEPTIONS - flag = 1; - func(arg); - flag = ~once_flag::_State_type(0); -#ifndef _LIBCPP_NO_EXCEPTIONS - } - catch (...) - { - flag = 0; - throw; - } -#endif // _LIBCPP_NO_EXCEPTIONS - } +/// Returns true if it's already been called, and false otherwise +static inline bool call_once_prep(volatile once_flag::_State_type& flag) { +#ifdef _LIBCPP_HAS_NO_THREADS + if (flag != once_flag::UNSET) + return true; + flag = once_flag::PENDING; + return false; #else // !_LIBCPP_HAS_NO_THREADS - __libcpp_mutex_lock(&mut); - while (flag == 1) - __libcpp_condvar_wait(&cv, &mut); - if (flag == 0) - { -#ifndef _LIBCPP_NO_EXCEPTIONS - try - { -#endif // _LIBCPP_NO_EXCEPTIONS - __libcpp_relaxed_store(&flag, once_flag::_State_type(1)); - __libcpp_mutex_unlock(&mut); - func(arg); - __libcpp_mutex_lock(&mut); - __libcpp_atomic_store(&flag, ~once_flag::_State_type(0), - _AO_Release); - __libcpp_mutex_unlock(&mut); - __libcpp_condvar_broadcast(&cv); -#ifndef _LIBCPP_NO_EXCEPTIONS - } - catch (...) - { - __libcpp_mutex_lock(&mut); - __libcpp_relaxed_store(&flag, once_flag::_State_type(0)); - __libcpp_mutex_unlock(&mut); - __libcpp_condvar_broadcast(&cv); - throw; - } -#endif // _LIBCPP_NO_EXCEPTIONS - } - else - __libcpp_mutex_unlock(&mut); + __libcpp_mutex_lock(&mut); + while (flag == once_flag::PENDING) + __libcpp_condvar_wait(&cv, &mut); + + if (flag != once_flag::UNSET) { + __libcpp_mutex_unlock(&mut); + return true; + } + + __libcpp_relaxed_store(&flag, once_flag::PENDING); + __libcpp_mutex_unlock(&mut); + return false; #endif // !_LIBCPP_HAS_NO_THREADS } +static inline void call_once_finish(volatile once_flag::_State_type& flag) { +#ifdef _LIBCPP_HAS_NO_THREADS + flag = once_flag::COMPLETE; +#else // !_LIBCPP_HAS_NO_THREADS + __libcpp_mutex_lock(&mut); + __libcpp_atomic_store(&flag, once_flag::COMPLETE, _AO_Release); + __libcpp_mutex_unlock(&mut); + __libcpp_condvar_broadcast(&cv); +#endif // !_LIBCPP_HAS_NO_THREADS +} + +static inline void call_once_abort(volatile once_flag::_State_type& flag) { +#ifdef _LIBCPP_HAS_NO_THREADS + flag = once_flag::UNSET; +#else // !_LIBCPP_HAS_NO_THREADS + __libcpp_mutex_lock(&mut); + __libcpp_relaxed_store(&flag, once_flag::UNSET); + __libcpp_mutex_unlock(&mut); + __libcpp_condvar_broadcast(&cv); +#endif // !_LIBCPP_HAS_NO_THREADS +} + +void __call_once(volatile once_flag::_State_type& flag, void* arg, void (*func)(void*)) { + if (call_once_prep(flag)) + return; +#ifdef _LIBCPP_NO_EXCEPTIONS + func(arg); +#else // !_LIBCPP_NO_EXCEPTIONS + try { + func(arg); + } catch (...) { + call_once_abort(flag); + throw; + } +#endif // !_LIBCPP_NO_EXCEPTIONS + call_once_finish(flag); +} + _LIBCPP_END_NAMESPACE_STD