xref: /freebsd/contrib/llvm-project/libcxx/src/call_once.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
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