1 | // -*- C++ -*- |
2 | //===----------------------------------------------------------------------===// |
3 | // |
4 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
5 | // See https://llvm.org/LICENSE.txt for license information. |
6 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
7 | // |
8 | //===----------------------------------------------------------------------===// |
9 | |
10 | #ifndef _LIBCPP_SHARED_MUTEX |
11 | #define _LIBCPP_SHARED_MUTEX |
12 | |
13 | /* |
14 | shared_mutex synopsis |
15 | |
16 | // C++1y |
17 | |
18 | namespace std |
19 | { |
20 | |
21 | class shared_mutex // C++17 |
22 | { |
23 | public: |
24 | shared_mutex(); |
25 | ~shared_mutex(); |
26 | |
27 | shared_mutex(const shared_mutex&) = delete; |
28 | shared_mutex& operator=(const shared_mutex&) = delete; |
29 | |
30 | // Exclusive ownership |
31 | void lock(); // blocking |
32 | bool try_lock(); |
33 | void unlock(); |
34 | |
35 | // Shared ownership |
36 | void lock_shared(); // blocking |
37 | bool try_lock_shared(); |
38 | void unlock_shared(); |
39 | |
40 | typedef implementation-defined native_handle_type; // See 30.2.3 |
41 | native_handle_type native_handle(); // See 30.2.3 |
42 | }; |
43 | |
44 | class shared_timed_mutex |
45 | { |
46 | public: |
47 | shared_timed_mutex(); |
48 | ~shared_timed_mutex(); |
49 | |
50 | shared_timed_mutex(const shared_timed_mutex&) = delete; |
51 | shared_timed_mutex& operator=(const shared_timed_mutex&) = delete; |
52 | |
53 | // Exclusive ownership |
54 | void lock(); // blocking |
55 | bool try_lock(); |
56 | template <class Rep, class Period> |
57 | bool try_lock_for(const chrono::duration<Rep, Period>& rel_time); |
58 | template <class Clock, class Duration> |
59 | bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time); |
60 | void unlock(); |
61 | |
62 | // Shared ownership |
63 | void lock_shared(); // blocking |
64 | bool try_lock_shared(); |
65 | template <class Rep, class Period> |
66 | bool |
67 | try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time); |
68 | template <class Clock, class Duration> |
69 | bool |
70 | try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time); |
71 | void unlock_shared(); |
72 | }; |
73 | |
74 | template <class Mutex> |
75 | class shared_lock |
76 | { |
77 | public: |
78 | typedef Mutex mutex_type; |
79 | |
80 | // Shared locking |
81 | shared_lock() noexcept; |
82 | explicit shared_lock(mutex_type& m); // blocking |
83 | shared_lock(mutex_type& m, defer_lock_t) noexcept; |
84 | shared_lock(mutex_type& m, try_to_lock_t); |
85 | shared_lock(mutex_type& m, adopt_lock_t); |
86 | template <class Clock, class Duration> |
87 | shared_lock(mutex_type& m, |
88 | const chrono::time_point<Clock, Duration>& abs_time); |
89 | template <class Rep, class Period> |
90 | shared_lock(mutex_type& m, |
91 | const chrono::duration<Rep, Period>& rel_time); |
92 | ~shared_lock(); |
93 | |
94 | shared_lock(shared_lock const&) = delete; |
95 | shared_lock& operator=(shared_lock const&) = delete; |
96 | |
97 | shared_lock(shared_lock&& u) noexcept; |
98 | shared_lock& operator=(shared_lock&& u) noexcept; |
99 | |
100 | void lock(); // blocking |
101 | bool try_lock(); |
102 | template <class Rep, class Period> |
103 | bool try_lock_for(const chrono::duration<Rep, Period>& rel_time); |
104 | template <class Clock, class Duration> |
105 | bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time); |
106 | void unlock(); |
107 | |
108 | // Setters |
109 | void swap(shared_lock& u) noexcept; |
110 | mutex_type* release() noexcept; |
111 | |
112 | // Getters |
113 | bool owns_lock() const noexcept; |
114 | explicit operator bool () const noexcept; |
115 | mutex_type* mutex() const noexcept; |
116 | }; |
117 | |
118 | template <class Mutex> |
119 | void swap(shared_lock<Mutex>& x, shared_lock<Mutex>& y) noexcept; |
120 | |
121 | } // std |
122 | |
123 | */ |
124 | |
125 | #if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS) |
126 | # include <__cxx03/__config> |
127 | #else |
128 | # include <__config> |
129 | |
130 | # if _LIBCPP_HAS_THREADS |
131 | |
132 | # include <__chrono/duration.h> |
133 | # include <__chrono/steady_clock.h> |
134 | # include <__chrono/time_point.h> |
135 | # include <__condition_variable/condition_variable.h> |
136 | # include <__memory/addressof.h> |
137 | # include <__mutex/mutex.h> |
138 | # include <__mutex/tag_types.h> |
139 | # include <__mutex/unique_lock.h> |
140 | # include <__system_error/throw_system_error.h> |
141 | # include <__utility/swap.h> |
142 | # include <cerrno> |
143 | # include <version> |
144 | |
145 | _LIBCPP_PUSH_MACROS |
146 | # include <__undef_macros> |
147 | |
148 | # if _LIBCPP_STD_VER >= 14 |
149 | |
150 | # if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) |
151 | # pragma GCC system_header |
152 | # endif |
153 | |
154 | _LIBCPP_BEGIN_NAMESPACE_STD |
155 | |
156 | struct _LIBCPP_EXPORTED_FROM_ABI __shared_mutex_base { |
157 | mutex __mut_; |
158 | condition_variable __gate1_; |
159 | condition_variable __gate2_; |
160 | unsigned __state_; |
161 | |
162 | static const unsigned __write_entered_ = 1U << (sizeof(unsigned) * __CHAR_BIT__ - 1); |
163 | static const unsigned __n_readers_ = ~__write_entered_; |
164 | |
165 | __shared_mutex_base(); |
166 | _LIBCPP_HIDE_FROM_ABI ~__shared_mutex_base() = default; |
167 | |
168 | __shared_mutex_base(const __shared_mutex_base&) = delete; |
169 | __shared_mutex_base& operator=(const __shared_mutex_base&) = delete; |
170 | |
171 | // Exclusive ownership |
172 | void lock(); // blocking |
173 | bool try_lock(); |
174 | void unlock(); |
175 | |
176 | // Shared ownership |
177 | void lock_shared(); // blocking |
178 | bool try_lock_shared(); |
179 | void unlock_shared(); |
180 | |
181 | // typedef implementation-defined native_handle_type; // See 30.2.3 |
182 | // native_handle_type native_handle(); // See 30.2.3 |
183 | }; |
184 | |
185 | # if _LIBCPP_STD_VER >= 17 |
186 | class _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_THREAD_SAFETY_ANNOTATION(__capability__("shared_mutex" )) shared_mutex { |
187 | __shared_mutex_base __base_; |
188 | |
189 | public: |
190 | _LIBCPP_HIDE_FROM_ABI shared_mutex() : __base_() {} |
191 | _LIBCPP_HIDE_FROM_ABI ~shared_mutex() = default; |
192 | |
193 | shared_mutex(const shared_mutex&) = delete; |
194 | shared_mutex& operator=(const shared_mutex&) = delete; |
195 | |
196 | // Exclusive ownership |
197 | _LIBCPP_HIDE_FROM_ABI void lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(__acquire_capability__()) { |
198 | return __base_.lock(); |
199 | } |
200 | _LIBCPP_HIDE_FROM_ABI bool try_lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_capability__(true)) { |
201 | return __base_.try_lock(); |
202 | } |
203 | _LIBCPP_HIDE_FROM_ABI void unlock() _LIBCPP_THREAD_SAFETY_ANNOTATION(__release_capability__()) { |
204 | return __base_.unlock(); |
205 | } |
206 | |
207 | // Shared ownership |
208 | _LIBCPP_HIDE_FROM_ABI void lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(__acquire_shared_capability__()) { |
209 | return __base_.lock_shared(); |
210 | } |
211 | _LIBCPP_HIDE_FROM_ABI bool try_lock_shared() |
212 | _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_shared_capability__(true)) { |
213 | return __base_.try_lock_shared(); |
214 | } |
215 | _LIBCPP_HIDE_FROM_ABI void unlock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(__release_shared_capability__()) { |
216 | return __base_.unlock_shared(); |
217 | } |
218 | |
219 | // typedef __shared_mutex_base::native_handle_type native_handle_type; |
220 | // _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return __base::unlock_shared(); } |
221 | }; |
222 | # endif |
223 | |
224 | class _LIBCPP_EXPORTED_FROM_ABI |
225 | _LIBCPP_THREAD_SAFETY_ANNOTATION(__capability__("shared_timed_mutex" )) shared_timed_mutex { |
226 | __shared_mutex_base __base_; |
227 | |
228 | public: |
229 | shared_timed_mutex(); |
230 | _LIBCPP_HIDE_FROM_ABI ~shared_timed_mutex() = default; |
231 | |
232 | shared_timed_mutex(const shared_timed_mutex&) = delete; |
233 | shared_timed_mutex& operator=(const shared_timed_mutex&) = delete; |
234 | |
235 | // Exclusive ownership |
236 | void lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(__acquire_capability__()); |
237 | bool try_lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_capability__(true)); |
238 | template <class _Rep, class _Period> |
239 | _LIBCPP_HIDE_FROM_ABI bool try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time) |
240 | _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_capability__(true)) { |
241 | return try_lock_until(chrono::steady_clock::now() + __rel_time); |
242 | } |
243 | |
244 | template <class _Clock, class _Duration> |
245 | _LIBCPP_HIDE_FROM_ABI bool try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time) |
246 | _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_capability__(true)) { |
247 | unique_lock<mutex> __lk(__base_.__mut_); |
248 | if (__base_.__state_ & __base_.__write_entered_) { |
249 | while (true) { |
250 | cv_status __status = __base_.__gate1_.wait_until(__lk, __abs_time); |
251 | if ((__base_.__state_ & __base_.__write_entered_) == 0) |
252 | break; |
253 | if (__status == cv_status::timeout) |
254 | return false; |
255 | } |
256 | } |
257 | __base_.__state_ |= __base_.__write_entered_; |
258 | if (__base_.__state_ & __base_.__n_readers_) { |
259 | while (true) { |
260 | cv_status __status = __base_.__gate2_.wait_until(__lk, __abs_time); |
261 | if ((__base_.__state_ & __base_.__n_readers_) == 0) |
262 | break; |
263 | if (__status == cv_status::timeout) { |
264 | __base_.__state_ &= ~__base_.__write_entered_; |
265 | __base_.__gate1_.notify_all(); |
266 | return false; |
267 | } |
268 | } |
269 | } |
270 | return true; |
271 | } |
272 | |
273 | void unlock() _LIBCPP_THREAD_SAFETY_ANNOTATION(__release_capability__()); |
274 | |
275 | // Shared ownership |
276 | void lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(__acquire_shared_capability__()); |
277 | bool try_lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_shared_capability__(true)); |
278 | template <class _Rep, class _Period> |
279 | _LIBCPP_HIDE_FROM_ABI bool try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time) |
280 | _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_shared_capability__(true)) { |
281 | return try_lock_shared_until(chrono::steady_clock::now() + __rel_time); |
282 | } |
283 | |
284 | template <class _Clock, class _Duration> |
285 | _LIBCPP_HIDE_FROM_ABI bool try_lock_shared_until(const chrono::time_point<_Clock, _Duration>& __abs_time) |
286 | _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_shared_capability__(true)) { |
287 | unique_lock<mutex> __lk(__base_.__mut_); |
288 | if ((__base_.__state_ & __base_.__write_entered_) || |
289 | (__base_.__state_ & __base_.__n_readers_) == __base_.__n_readers_) { |
290 | while (true) { |
291 | cv_status __status = __base_.__gate1_.wait_until(__lk, __abs_time); |
292 | if ((__base_.__state_ & __base_.__write_entered_) == 0 && |
293 | (__base_.__state_ & __base_.__n_readers_) < __base_.__n_readers_) |
294 | break; |
295 | if (__status == cv_status::timeout) |
296 | return false; |
297 | } |
298 | } |
299 | unsigned __num_readers = (__base_.__state_ & __base_.__n_readers_) + 1; |
300 | __base_.__state_ &= ~__base_.__n_readers_; |
301 | __base_.__state_ |= __num_readers; |
302 | return true; |
303 | } |
304 | |
305 | void unlock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(__release_shared_capability__()); |
306 | }; |
307 | |
308 | template <class _Mutex> |
309 | class shared_lock { |
310 | public: |
311 | typedef _Mutex mutex_type; |
312 | |
313 | private: |
314 | mutex_type* __m_; |
315 | bool __owns_; |
316 | |
317 | public: |
318 | _LIBCPP_HIDE_FROM_ABI shared_lock() _NOEXCEPT : __m_(nullptr), __owns_(false) {} |
319 | |
320 | _LIBCPP_HIDE_FROM_ABI explicit shared_lock(mutex_type& __m) : __m_(std::addressof(__m)), __owns_(true) { |
321 | __m_->lock_shared(); |
322 | } |
323 | |
324 | _LIBCPP_HIDE_FROM_ABI shared_lock(mutex_type& __m, defer_lock_t) _NOEXCEPT |
325 | : __m_(std::addressof(__m)), |
326 | __owns_(false) {} |
327 | |
328 | _LIBCPP_HIDE_FROM_ABI shared_lock(mutex_type& __m, try_to_lock_t) |
329 | : __m_(std::addressof(__m)), __owns_(__m.try_lock_shared()) {} |
330 | |
331 | _LIBCPP_HIDE_FROM_ABI shared_lock(mutex_type& __m, adopt_lock_t) : __m_(std::addressof(__m)), __owns_(true) {} |
332 | |
333 | template <class _Clock, class _Duration> |
334 | _LIBCPP_HIDE_FROM_ABI shared_lock(mutex_type& __m, const chrono::time_point<_Clock, _Duration>& __abs_time) |
335 | : __m_(std::addressof(__m)), __owns_(__m.try_lock_shared_until(__abs_time)) {} |
336 | |
337 | template <class _Rep, class _Period> |
338 | _LIBCPP_HIDE_FROM_ABI shared_lock(mutex_type& __m, const chrono::duration<_Rep, _Period>& __rel_time) |
339 | : __m_(std::addressof(__m)), __owns_(__m.try_lock_shared_for(__rel_time)) {} |
340 | |
341 | _LIBCPP_HIDE_FROM_ABI ~shared_lock() { |
342 | if (__owns_) |
343 | __m_->unlock_shared(); |
344 | } |
345 | |
346 | shared_lock(shared_lock const&) = delete; |
347 | shared_lock& operator=(shared_lock const&) = delete; |
348 | |
349 | _LIBCPP_HIDE_FROM_ABI shared_lock(shared_lock&& __u) _NOEXCEPT : __m_(__u.__m_), __owns_(__u.__owns_) { |
350 | __u.__m_ = nullptr; |
351 | __u.__owns_ = false; |
352 | } |
353 | |
354 | _LIBCPP_HIDE_FROM_ABI shared_lock& operator=(shared_lock&& __u) _NOEXCEPT { |
355 | if (__owns_) |
356 | __m_->unlock_shared(); |
357 | __m_ = nullptr; |
358 | __owns_ = false; |
359 | __m_ = __u.__m_; |
360 | __owns_ = __u.__owns_; |
361 | __u.__m_ = nullptr; |
362 | __u.__owns_ = false; |
363 | return *this; |
364 | } |
365 | |
366 | _LIBCPP_HIDE_FROM_ABI void lock(); |
367 | _LIBCPP_HIDE_FROM_ABI bool try_lock(); |
368 | template <class _Rep, class _Period> |
369 | _LIBCPP_HIDE_FROM_ABI bool try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time); |
370 | template <class _Clock, class _Duration> |
371 | _LIBCPP_HIDE_FROM_ABI bool try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time); |
372 | _LIBCPP_HIDE_FROM_ABI void unlock(); |
373 | |
374 | // Setters |
375 | _LIBCPP_HIDE_FROM_ABI void swap(shared_lock& __u) _NOEXCEPT { |
376 | std::swap(__m_, __u.__m_); |
377 | std::swap(__owns_, __u.__owns_); |
378 | } |
379 | |
380 | _LIBCPP_HIDE_FROM_ABI mutex_type* release() _NOEXCEPT { |
381 | mutex_type* __m = __m_; |
382 | __m_ = nullptr; |
383 | __owns_ = false; |
384 | return __m; |
385 | } |
386 | |
387 | // Getters |
388 | _LIBCPP_HIDE_FROM_ABI bool owns_lock() const _NOEXCEPT { return __owns_; } |
389 | |
390 | _LIBCPP_HIDE_FROM_ABI explicit operator bool() const _NOEXCEPT { return __owns_; } |
391 | |
392 | _LIBCPP_HIDE_FROM_ABI mutex_type* mutex() const _NOEXCEPT { return __m_; } |
393 | }; |
394 | _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(shared_lock); |
395 | |
396 | template <class _Mutex> |
397 | void shared_lock<_Mutex>::lock() { |
398 | if (__m_ == nullptr) |
399 | std::__throw_system_error(EPERM, what_arg: "shared_lock::lock: references null mutex" ); |
400 | if (__owns_) |
401 | std::__throw_system_error(EDEADLK, what_arg: "shared_lock::lock: already locked" ); |
402 | __m_->lock_shared(); |
403 | __owns_ = true; |
404 | } |
405 | |
406 | template <class _Mutex> |
407 | bool shared_lock<_Mutex>::try_lock() { |
408 | if (__m_ == nullptr) |
409 | std::__throw_system_error(EPERM, what_arg: "shared_lock::try_lock: references null mutex" ); |
410 | if (__owns_) |
411 | std::__throw_system_error(EDEADLK, what_arg: "shared_lock::try_lock: already locked" ); |
412 | __owns_ = __m_->try_lock_shared(); |
413 | return __owns_; |
414 | } |
415 | |
416 | template <class _Mutex> |
417 | template <class _Rep, class _Period> |
418 | bool shared_lock<_Mutex>::try_lock_for(const chrono::duration<_Rep, _Period>& __d) { |
419 | if (__m_ == nullptr) |
420 | std::__throw_system_error(EPERM, what_arg: "shared_lock::try_lock_for: references null mutex" ); |
421 | if (__owns_) |
422 | std::__throw_system_error(EDEADLK, what_arg: "shared_lock::try_lock_for: already locked" ); |
423 | __owns_ = __m_->try_lock_shared_for(__d); |
424 | return __owns_; |
425 | } |
426 | |
427 | template <class _Mutex> |
428 | template <class _Clock, class _Duration> |
429 | bool shared_lock<_Mutex>::try_lock_until(const chrono::time_point<_Clock, _Duration>& __t) { |
430 | if (__m_ == nullptr) |
431 | std::__throw_system_error(EPERM, what_arg: "shared_lock::try_lock_until: references null mutex" ); |
432 | if (__owns_) |
433 | std::__throw_system_error(EDEADLK, what_arg: "shared_lock::try_lock_until: already locked" ); |
434 | __owns_ = __m_->try_lock_shared_until(__t); |
435 | return __owns_; |
436 | } |
437 | |
438 | template <class _Mutex> |
439 | void shared_lock<_Mutex>::unlock() { |
440 | if (!__owns_) |
441 | std::__throw_system_error(EPERM, what_arg: "shared_lock::unlock: not locked" ); |
442 | __m_->unlock_shared(); |
443 | __owns_ = false; |
444 | } |
445 | |
446 | template <class _Mutex> |
447 | inline _LIBCPP_HIDE_FROM_ABI void swap(shared_lock<_Mutex>& __x, shared_lock<_Mutex>& __y) _NOEXCEPT { |
448 | __x.swap(__y); |
449 | } |
450 | |
451 | _LIBCPP_END_NAMESPACE_STD |
452 | |
453 | # endif // _LIBCPP_STD_VER >= 14 |
454 | |
455 | _LIBCPP_POP_MACROS |
456 | |
457 | # endif // _LIBCPP_HAS_THREADS |
458 | |
459 | # if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20 |
460 | # include <optional> |
461 | # include <system_error> |
462 | # endif |
463 | #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS) |
464 | |
465 | #endif // _LIBCPP_SHARED_MUTEX |
466 | |