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 <__thread/support.h> |
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(ex: [&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 | |