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