1 | //===------------------------- mutex.cpp ----------------------------------===// |
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" |
10 | #include "limits" |
11 | #include "system_error" |
12 | #include "include/atomic_support.h" |
13 | #include "__undef_macros" |
14 | |
15 | #ifndef _LIBCPP_HAS_NO_THREADS |
16 | #if defined(__ELF__) && defined(_LIBCPP_LINK_PTHREAD_LIB) |
17 | #pragma comment(lib, "pthread") |
18 | #endif |
19 | #endif |
20 | |
21 | _LIBCPP_BEGIN_NAMESPACE_STD |
22 | #ifndef _LIBCPP_HAS_NO_THREADS |
23 | |
24 | const defer_lock_t defer_lock{}; |
25 | const try_to_lock_t try_to_lock{}; |
26 | const adopt_lock_t adopt_lock{}; |
27 | |
28 | // ~mutex is defined elsewhere |
29 | |
30 | void |
31 | mutex::lock() |
32 | { |
33 | int ec = __libcpp_mutex_lock(&__m_); |
34 | if (ec) |
35 | __throw_system_error(ec, "mutex lock failed" ); |
36 | } |
37 | |
38 | bool |
39 | mutex::try_lock() _NOEXCEPT |
40 | { |
41 | return __libcpp_mutex_trylock(&__m_); |
42 | } |
43 | |
44 | void |
45 | mutex::unlock() _NOEXCEPT |
46 | { |
47 | int ec = __libcpp_mutex_unlock(&__m_); |
48 | (void)ec; |
49 | _LIBCPP_ASSERT(ec == 0, "call to mutex::unlock failed" ); |
50 | } |
51 | |
52 | // recursive_mutex |
53 | |
54 | recursive_mutex::recursive_mutex() |
55 | { |
56 | int ec = __libcpp_recursive_mutex_init(&__m_); |
57 | if (ec) |
58 | __throw_system_error(ec, "recursive_mutex constructor failed" ); |
59 | } |
60 | |
61 | recursive_mutex::~recursive_mutex() |
62 | { |
63 | int e = __libcpp_recursive_mutex_destroy(&__m_); |
64 | (void)e; |
65 | _LIBCPP_ASSERT(e == 0, "call to ~recursive_mutex() failed" ); |
66 | } |
67 | |
68 | void |
69 | recursive_mutex::lock() |
70 | { |
71 | int ec = __libcpp_recursive_mutex_lock(&__m_); |
72 | if (ec) |
73 | __throw_system_error(ec, "recursive_mutex lock failed" ); |
74 | } |
75 | |
76 | void |
77 | recursive_mutex::unlock() _NOEXCEPT |
78 | { |
79 | int e = __libcpp_recursive_mutex_unlock(&__m_); |
80 | (void)e; |
81 | _LIBCPP_ASSERT(e == 0, "call to recursive_mutex::unlock() failed" ); |
82 | } |
83 | |
84 | bool |
85 | recursive_mutex::try_lock() _NOEXCEPT |
86 | { |
87 | return __libcpp_recursive_mutex_trylock(&__m_); |
88 | } |
89 | |
90 | // timed_mutex |
91 | |
92 | timed_mutex::timed_mutex() |
93 | : __locked_(false) |
94 | { |
95 | } |
96 | |
97 | timed_mutex::~timed_mutex() |
98 | { |
99 | lock_guard<mutex> _(__m_); |
100 | } |
101 | |
102 | void |
103 | timed_mutex::lock() |
104 | { |
105 | unique_lock<mutex> lk(__m_); |
106 | while (__locked_) |
107 | __cv_.wait(lk); |
108 | __locked_ = true; |
109 | } |
110 | |
111 | bool |
112 | timed_mutex::try_lock() _NOEXCEPT |
113 | { |
114 | unique_lock<mutex> lk(__m_, try_to_lock); |
115 | if (lk.owns_lock() && !__locked_) |
116 | { |
117 | __locked_ = true; |
118 | return true; |
119 | } |
120 | return false; |
121 | } |
122 | |
123 | void |
124 | timed_mutex::unlock() _NOEXCEPT |
125 | { |
126 | lock_guard<mutex> _(__m_); |
127 | __locked_ = false; |
128 | __cv_.notify_one(); |
129 | } |
130 | |
131 | // recursive_timed_mutex |
132 | |
133 | recursive_timed_mutex::recursive_timed_mutex() |
134 | : __count_(0), |
135 | __id_{} |
136 | { |
137 | } |
138 | |
139 | recursive_timed_mutex::~recursive_timed_mutex() |
140 | { |
141 | lock_guard<mutex> _(__m_); |
142 | } |
143 | |
144 | void |
145 | recursive_timed_mutex::lock() |
146 | { |
147 | __thread_id id = this_thread::get_id(); |
148 | unique_lock<mutex> lk(__m_); |
149 | if (id ==__id_) |
150 | { |
151 | if (__count_ == numeric_limits<size_t>::max()) |
152 | __throw_system_error(EAGAIN, "recursive_timed_mutex lock limit reached" ); |
153 | ++__count_; |
154 | return; |
155 | } |
156 | while (__count_ != 0) |
157 | __cv_.wait(lk); |
158 | __count_ = 1; |
159 | __id_ = id; |
160 | } |
161 | |
162 | bool |
163 | recursive_timed_mutex::try_lock() _NOEXCEPT |
164 | { |
165 | __thread_id id = this_thread::get_id(); |
166 | unique_lock<mutex> lk(__m_, try_to_lock); |
167 | if (lk.owns_lock() && (__count_ == 0 || id == __id_)) |
168 | { |
169 | if (__count_ == numeric_limits<size_t>::max()) |
170 | return false; |
171 | ++__count_; |
172 | __id_ = id; |
173 | return true; |
174 | } |
175 | return false; |
176 | } |
177 | |
178 | void |
179 | recursive_timed_mutex::unlock() _NOEXCEPT |
180 | { |
181 | unique_lock<mutex> lk(__m_); |
182 | if (--__count_ == 0) |
183 | { |
184 | __id_.__reset(); |
185 | lk.unlock(); |
186 | __cv_.notify_one(); |
187 | } |
188 | } |
189 | |
190 | #endif // !_LIBCPP_HAS_NO_THREADS |
191 | |
192 | // If dispatch_once_f ever handles C++ exceptions, and if one can get to it |
193 | // without illegal macros (unexpected macros not beginning with _UpperCase or |
194 | // __lowercase), and if it stops spinning waiting threads, then call_once should |
195 | // call into dispatch_once_f instead of here. Relevant radar this code needs to |
196 | // keep in sync with: 7741191. |
197 | |
198 | #ifndef _LIBCPP_HAS_NO_THREADS |
199 | _LIBCPP_SAFE_STATIC static __libcpp_mutex_t mut = _LIBCPP_MUTEX_INITIALIZER; |
200 | _LIBCPP_SAFE_STATIC static __libcpp_condvar_t cv = _LIBCPP_CONDVAR_INITIALIZER; |
201 | #endif |
202 | |
203 | void __call_once(volatile once_flag::_State_type& flag, void* arg, |
204 | void (*func)(void*)) |
205 | { |
206 | #if defined(_LIBCPP_HAS_NO_THREADS) |
207 | if (flag == 0) |
208 | { |
209 | #ifndef _LIBCPP_NO_EXCEPTIONS |
210 | try |
211 | { |
212 | #endif // _LIBCPP_NO_EXCEPTIONS |
213 | flag = 1; |
214 | func(arg); |
215 | flag = ~once_flag::_State_type(0); |
216 | #ifndef _LIBCPP_NO_EXCEPTIONS |
217 | } |
218 | catch (...) |
219 | { |
220 | flag = 0; |
221 | throw; |
222 | } |
223 | #endif // _LIBCPP_NO_EXCEPTIONS |
224 | } |
225 | #else // !_LIBCPP_HAS_NO_THREADS |
226 | __libcpp_mutex_lock(&mut); |
227 | while (flag == 1) |
228 | __libcpp_condvar_wait(&cv, &mut); |
229 | if (flag == 0) |
230 | { |
231 | #ifndef _LIBCPP_NO_EXCEPTIONS |
232 | try |
233 | { |
234 | #endif // _LIBCPP_NO_EXCEPTIONS |
235 | __libcpp_relaxed_store(&flag, once_flag::_State_type(1)); |
236 | __libcpp_mutex_unlock(&mut); |
237 | func(arg); |
238 | __libcpp_mutex_lock(&mut); |
239 | __libcpp_atomic_store(&flag, ~once_flag::_State_type(0), |
240 | _AO_Release); |
241 | __libcpp_mutex_unlock(&mut); |
242 | __libcpp_condvar_broadcast(&cv); |
243 | #ifndef _LIBCPP_NO_EXCEPTIONS |
244 | } |
245 | catch (...) |
246 | { |
247 | __libcpp_mutex_lock(&mut); |
248 | __libcpp_relaxed_store(&flag, once_flag::_State_type(0)); |
249 | __libcpp_mutex_unlock(&mut); |
250 | __libcpp_condvar_broadcast(&cv); |
251 | throw; |
252 | } |
253 | #endif // _LIBCPP_NO_EXCEPTIONS |
254 | } |
255 | else |
256 | __libcpp_mutex_unlock(&mut); |
257 | #endif // !_LIBCPP_HAS_NO_THREADS |
258 | } |
259 | |
260 | _LIBCPP_END_NAMESPACE_STD |
261 | |