1*5f757f3fSDimitry Andric //===----------------------------------------------------------------------===// 2*5f757f3fSDimitry Andric // 3*5f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*5f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*5f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*5f757f3fSDimitry Andric // 7*5f757f3fSDimitry Andric //===----------------------------------------------------------------------===// 8*5f757f3fSDimitry Andric 9*5f757f3fSDimitry Andric #include <__mutex/once_flag.h> 10*5f757f3fSDimitry Andric #include <__utility/exception_guard.h> 11*5f757f3fSDimitry Andric 12*5f757f3fSDimitry Andric #ifndef _LIBCPP_HAS_NO_THREADS 13*5f757f3fSDimitry Andric # include <__threading_support> 14*5f757f3fSDimitry Andric #endif 15*5f757f3fSDimitry Andric 16*5f757f3fSDimitry Andric #include "include/atomic_support.h" 17*5f757f3fSDimitry Andric 18*5f757f3fSDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD 19*5f757f3fSDimitry Andric 20*5f757f3fSDimitry Andric // If dispatch_once_f ever handles C++ exceptions, and if one can get to it 21*5f757f3fSDimitry Andric // without illegal macros (unexpected macros not beginning with _UpperCase or 22*5f757f3fSDimitry Andric // __lowercase), and if it stops spinning waiting threads, then call_once should 23*5f757f3fSDimitry Andric // call into dispatch_once_f instead of here. Relevant radar this code needs to 24*5f757f3fSDimitry Andric // keep in sync with: 7741191. 25*5f757f3fSDimitry Andric 26*5f757f3fSDimitry Andric #ifndef _LIBCPP_HAS_NO_THREADS 27*5f757f3fSDimitry Andric static constinit __libcpp_mutex_t mut = _LIBCPP_MUTEX_INITIALIZER; 28*5f757f3fSDimitry Andric static constinit __libcpp_condvar_t cv = _LIBCPP_CONDVAR_INITIALIZER; 29*5f757f3fSDimitry Andric #endif 30*5f757f3fSDimitry Andric 31*5f757f3fSDimitry Andric void __call_once(volatile once_flag::_State_type& flag, void* arg, 32*5f757f3fSDimitry Andric void (*func)(void*)) 33*5f757f3fSDimitry Andric { 34*5f757f3fSDimitry Andric #if defined(_LIBCPP_HAS_NO_THREADS) 35*5f757f3fSDimitry Andric 36*5f757f3fSDimitry Andric if (flag == once_flag::_Unset) { 37*5f757f3fSDimitry Andric auto guard = std::__make_exception_guard([&flag] { flag = once_flag::_Unset; }); 38*5f757f3fSDimitry Andric flag = once_flag::_Pending; 39*5f757f3fSDimitry Andric func(arg); 40*5f757f3fSDimitry Andric flag = once_flag::_Complete; 41*5f757f3fSDimitry Andric guard.__complete(); 42*5f757f3fSDimitry Andric } 43*5f757f3fSDimitry Andric 44*5f757f3fSDimitry Andric #else // !_LIBCPP_HAS_NO_THREADS 45*5f757f3fSDimitry Andric 46*5f757f3fSDimitry Andric __libcpp_mutex_lock(&mut); 47*5f757f3fSDimitry Andric while (flag == once_flag::_Pending) 48*5f757f3fSDimitry Andric __libcpp_condvar_wait(&cv, &mut); 49*5f757f3fSDimitry Andric if (flag == once_flag::_Unset) { 50*5f757f3fSDimitry Andric auto guard = std::__make_exception_guard([&flag] { 51*5f757f3fSDimitry Andric __libcpp_mutex_lock(&mut); 52*5f757f3fSDimitry Andric __libcpp_relaxed_store(&flag, once_flag::_Unset); 53*5f757f3fSDimitry Andric __libcpp_mutex_unlock(&mut); 54*5f757f3fSDimitry Andric __libcpp_condvar_broadcast(&cv); 55*5f757f3fSDimitry Andric }); 56*5f757f3fSDimitry Andric 57*5f757f3fSDimitry Andric __libcpp_relaxed_store(&flag, once_flag::_Pending); 58*5f757f3fSDimitry Andric __libcpp_mutex_unlock(&mut); 59*5f757f3fSDimitry Andric func(arg); 60*5f757f3fSDimitry Andric __libcpp_mutex_lock(&mut); 61*5f757f3fSDimitry Andric __libcpp_atomic_store(&flag, once_flag::_Complete, _AO_Release); 62*5f757f3fSDimitry Andric __libcpp_mutex_unlock(&mut); 63*5f757f3fSDimitry Andric __libcpp_condvar_broadcast(&cv); 64*5f757f3fSDimitry Andric guard.__complete(); 65*5f757f3fSDimitry Andric } else { 66*5f757f3fSDimitry Andric __libcpp_mutex_unlock(&mut); 67*5f757f3fSDimitry Andric } 68*5f757f3fSDimitry Andric 69*5f757f3fSDimitry Andric #endif // !_LIBCPP_HAS_NO_THREADS 70*5f757f3fSDimitry Andric } 71*5f757f3fSDimitry Andric 72*5f757f3fSDimitry Andric _LIBCPP_END_NAMESPACE_STD 73