[WebAssembly] Add new target feature in support of 'extended-const' proposal
[llvm-project.git] / libcxx / src / mutex.cpp
blob01b7420fa3b8a9b56198dcb46b53e5b03b97939d
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 //===----------------------------------------------------------------------===//
9 #include <__assert>
10 #include <limits>
11 #include <mutex>
12 #include <system_error>
14 #include "include/atomic_support.h"
16 #ifndef _LIBCPP_HAS_NO_THREADS
17 # if defined(__ELF__) && defined(_LIBCPP_LINK_PTHREAD_LIB)
18 # pragma comment(lib, "pthread")
19 # endif
20 #endif
22 _LIBCPP_PUSH_MACROS
23 #include <__undef_macros>
25 _LIBCPP_BEGIN_NAMESPACE_STD
27 #ifndef _LIBCPP_HAS_NO_THREADS
29 const defer_lock_t defer_lock{};
30 const try_to_lock_t try_to_lock{};
31 const adopt_lock_t adopt_lock{};
33 // ~mutex is defined elsewhere
35 void
36 mutex::lock()
38 int ec = __libcpp_mutex_lock(&__m_);
39 if (ec)
40 __throw_system_error(ec, "mutex lock failed");
43 bool
44 mutex::try_lock() noexcept
46 return __libcpp_mutex_trylock(&__m_);
49 void
50 mutex::unlock() noexcept
52 int ec = __libcpp_mutex_unlock(&__m_);
53 (void)ec;
54 _LIBCPP_ASSERT(ec == 0, "call to mutex::unlock failed");
57 // recursive_mutex
59 recursive_mutex::recursive_mutex()
61 int ec = __libcpp_recursive_mutex_init(&__m_);
62 if (ec)
63 __throw_system_error(ec, "recursive_mutex constructor failed");
66 recursive_mutex::~recursive_mutex()
68 int e = __libcpp_recursive_mutex_destroy(&__m_);
69 (void)e;
70 _LIBCPP_ASSERT(e == 0, "call to ~recursive_mutex() failed");
73 void
74 recursive_mutex::lock()
76 int ec = __libcpp_recursive_mutex_lock(&__m_);
77 if (ec)
78 __throw_system_error(ec, "recursive_mutex lock failed");
81 void
82 recursive_mutex::unlock() noexcept
84 int e = __libcpp_recursive_mutex_unlock(&__m_);
85 (void)e;
86 _LIBCPP_ASSERT(e == 0, "call to recursive_mutex::unlock() failed");
89 bool
90 recursive_mutex::try_lock() noexcept
92 return __libcpp_recursive_mutex_trylock(&__m_);
95 // timed_mutex
97 timed_mutex::timed_mutex()
98 : __locked_(false)
102 timed_mutex::~timed_mutex()
104 lock_guard<mutex> _(__m_);
107 void
108 timed_mutex::lock()
110 unique_lock<mutex> lk(__m_);
111 while (__locked_)
112 __cv_.wait(lk);
113 __locked_ = true;
116 bool
117 timed_mutex::try_lock() noexcept
119 unique_lock<mutex> lk(__m_, try_to_lock);
120 if (lk.owns_lock() && !__locked_)
122 __locked_ = true;
123 return true;
125 return false;
128 void
129 timed_mutex::unlock() noexcept
131 lock_guard<mutex> _(__m_);
132 __locked_ = false;
133 __cv_.notify_one();
136 // recursive_timed_mutex
138 recursive_timed_mutex::recursive_timed_mutex()
139 : __count_(0),
140 __id_{}
144 recursive_timed_mutex::~recursive_timed_mutex()
146 lock_guard<mutex> _(__m_);
149 void
150 recursive_timed_mutex::lock()
152 __thread_id id = this_thread::get_id();
153 unique_lock<mutex> lk(__m_);
154 if (id ==__id_)
156 if (__count_ == numeric_limits<size_t>::max())
157 __throw_system_error(EAGAIN, "recursive_timed_mutex lock limit reached");
158 ++__count_;
159 return;
161 while (__count_ != 0)
162 __cv_.wait(lk);
163 __count_ = 1;
164 __id_ = id;
167 bool
168 recursive_timed_mutex::try_lock() noexcept
170 __thread_id id = this_thread::get_id();
171 unique_lock<mutex> lk(__m_, try_to_lock);
172 if (lk.owns_lock() && (__count_ == 0 || id == __id_))
174 if (__count_ == numeric_limits<size_t>::max())
175 return false;
176 ++__count_;
177 __id_ = id;
178 return true;
180 return false;
183 void
184 recursive_timed_mutex::unlock() noexcept
186 unique_lock<mutex> lk(__m_);
187 if (--__count_ == 0)
189 __id_.__reset();
190 lk.unlock();
191 __cv_.notify_one();
195 #endif // !_LIBCPP_HAS_NO_THREADS
197 // If dispatch_once_f ever handles C++ exceptions, and if one can get to it
198 // without illegal macros (unexpected macros not beginning with _UpperCase or
199 // __lowercase), and if it stops spinning waiting threads, then call_once should
200 // call into dispatch_once_f instead of here. Relevant radar this code needs to
201 // keep in sync with: 7741191.
203 #ifndef _LIBCPP_HAS_NO_THREADS
204 static constinit __libcpp_mutex_t mut = _LIBCPP_MUTEX_INITIALIZER;
205 static constinit __libcpp_condvar_t cv = _LIBCPP_CONDVAR_INITIALIZER;
206 #endif
208 void __call_once(volatile once_flag::_State_type& flag, void* arg,
209 void (*func)(void*))
211 #if defined(_LIBCPP_HAS_NO_THREADS)
212 if (flag == 0)
214 #ifndef _LIBCPP_NO_EXCEPTIONS
217 #endif // _LIBCPP_NO_EXCEPTIONS
218 flag = 1;
219 func(arg);
220 flag = ~once_flag::_State_type(0);
221 #ifndef _LIBCPP_NO_EXCEPTIONS
223 catch (...)
225 flag = 0;
226 throw;
228 #endif // _LIBCPP_NO_EXCEPTIONS
230 #else // !_LIBCPP_HAS_NO_THREADS
231 __libcpp_mutex_lock(&mut);
232 while (flag == 1)
233 __libcpp_condvar_wait(&cv, &mut);
234 if (flag == 0)
236 #ifndef _LIBCPP_NO_EXCEPTIONS
239 #endif // _LIBCPP_NO_EXCEPTIONS
240 __libcpp_relaxed_store(&flag, once_flag::_State_type(1));
241 __libcpp_mutex_unlock(&mut);
242 func(arg);
243 __libcpp_mutex_lock(&mut);
244 __libcpp_atomic_store(&flag, ~once_flag::_State_type(0),
245 _AO_Release);
246 __libcpp_mutex_unlock(&mut);
247 __libcpp_condvar_broadcast(&cv);
248 #ifndef _LIBCPP_NO_EXCEPTIONS
250 catch (...)
252 __libcpp_mutex_lock(&mut);
253 __libcpp_relaxed_store(&flag, once_flag::_State_type(0));
254 __libcpp_mutex_unlock(&mut);
255 __libcpp_condvar_broadcast(&cv);
256 throw;
258 #endif // _LIBCPP_NO_EXCEPTIONS
260 else
261 __libcpp_mutex_unlock(&mut);
262 #endif // !_LIBCPP_HAS_NO_THREADS
265 _LIBCPP_END_NAMESPACE_STD
267 _LIBCPP_POP_MACROS