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