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