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