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 != 0) + return true; + flag = 1; + 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 == 1) + __libcpp_condvar_wait(&cv, &mut); + + if (flag != 0) { + __libcpp_mutex_unlock(&mut); + return true; + } + + __libcpp_relaxed_store(&flag, once_flag::_State_type(1)); + __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::_State_type(0); +#else // !_LIBCPP_HAS_NO_THREADS + __libcpp_mutex_lock(&mut); + __libcpp_atomic_store(&flag, ~once_flag::_State_type(0), _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 = 0; +#else // !_LIBCPP_HAS_NO_THREADS + __libcpp_mutex_lock(&mut); + __libcpp_relaxed_store(&flag, once_flag::_State_type(0)); + __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