1 | // -*- C++ -*- |
2 | //===------------------------ shared_mutex --------------------------------===// |
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 | #include <__config> |
126 | #include <version> |
127 | |
128 | _LIBCPP_PUSH_MACROS |
129 | #include <__undef_macros> |
130 | |
131 | |
132 | #if _LIBCPP_STD_VER > 11 || defined(_LIBCPP_BUILDING_LIBRARY) |
133 | |
134 | #include <__mutex_base> |
135 | |
136 | #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) |
137 | #pragma GCC system_header |
138 | #endif |
139 | |
140 | #ifdef _LIBCPP_HAS_NO_THREADS |
141 | #error <shared_mutex> is not supported on this single threaded system |
142 | #else // !_LIBCPP_HAS_NO_THREADS |
143 | |
144 | _LIBCPP_BEGIN_NAMESPACE_STD |
145 | |
146 | struct _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_SHARED_MUTEX _LIBCPP_THREAD_SAFETY_ANNOTATION(capability("shared_mutex" )) |
147 | __shared_mutex_base |
148 | { |
149 | mutex __mut_; |
150 | condition_variable __gate1_; |
151 | condition_variable __gate2_; |
152 | unsigned __state_; |
153 | |
154 | static const unsigned __write_entered_ = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1); |
155 | static const unsigned __n_readers_ = ~__write_entered_; |
156 | |
157 | __shared_mutex_base(); |
158 | _LIBCPP_INLINE_VISIBILITY ~__shared_mutex_base() = default; |
159 | |
160 | __shared_mutex_base(const __shared_mutex_base&) = delete; |
161 | __shared_mutex_base& operator=(const __shared_mutex_base&) = delete; |
162 | |
163 | // Exclusive ownership |
164 | void lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_capability()); // blocking |
165 | bool try_lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(try_acquire_capability(true)); |
166 | void unlock() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_capability()); |
167 | |
168 | // Shared ownership |
169 | void lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_shared_capability()); // blocking |
170 | bool try_lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(try_acquire_shared_capability(true)); |
171 | void unlock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_shared_capability()); |
172 | |
173 | // typedef implementation-defined native_handle_type; // See 30.2.3 |
174 | // native_handle_type native_handle(); // See 30.2.3 |
175 | }; |
176 | |
177 | |
178 | #if _LIBCPP_STD_VER > 14 |
179 | class _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_SHARED_MUTEX shared_mutex |
180 | { |
181 | __shared_mutex_base __base; |
182 | public: |
183 | _LIBCPP_INLINE_VISIBILITY shared_mutex() : __base() {} |
184 | _LIBCPP_INLINE_VISIBILITY ~shared_mutex() = default; |
185 | |
186 | shared_mutex(const shared_mutex&) = delete; |
187 | shared_mutex& operator=(const shared_mutex&) = delete; |
188 | |
189 | // Exclusive ownership |
190 | _LIBCPP_INLINE_VISIBILITY void lock() { return __base.lock(); } |
191 | _LIBCPP_INLINE_VISIBILITY bool try_lock() { return __base.try_lock(); } |
192 | _LIBCPP_INLINE_VISIBILITY void unlock() { return __base.unlock(); } |
193 | |
194 | // Shared ownership |
195 | _LIBCPP_INLINE_VISIBILITY void lock_shared() { return __base.lock_shared(); } |
196 | _LIBCPP_INLINE_VISIBILITY bool try_lock_shared() { return __base.try_lock_shared(); } |
197 | _LIBCPP_INLINE_VISIBILITY void unlock_shared() { return __base.unlock_shared(); } |
198 | |
199 | // typedef __shared_mutex_base::native_handle_type native_handle_type; |
200 | // _LIBCPP_INLINE_VISIBILITY native_handle_type native_handle() { return __base::unlock_shared(); } |
201 | }; |
202 | #endif |
203 | |
204 | |
205 | class _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_SHARED_MUTEX shared_timed_mutex |
206 | { |
207 | __shared_mutex_base __base; |
208 | public: |
209 | shared_timed_mutex(); |
210 | _LIBCPP_INLINE_VISIBILITY ~shared_timed_mutex() = default; |
211 | |
212 | shared_timed_mutex(const shared_timed_mutex&) = delete; |
213 | shared_timed_mutex& operator=(const shared_timed_mutex&) = delete; |
214 | |
215 | // Exclusive ownership |
216 | void lock(); |
217 | bool try_lock(); |
218 | template <class _Rep, class _Period> |
219 | _LIBCPP_INLINE_VISIBILITY |
220 | bool |
221 | try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time) |
222 | { |
223 | return try_lock_until(chrono::steady_clock::now() + __rel_time); |
224 | } |
225 | template <class _Clock, class _Duration> |
226 | _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS |
227 | bool |
228 | try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time); |
229 | void unlock(); |
230 | |
231 | // Shared ownership |
232 | void lock_shared(); |
233 | bool try_lock_shared(); |
234 | template <class _Rep, class _Period> |
235 | _LIBCPP_INLINE_VISIBILITY |
236 | bool |
237 | try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time) |
238 | { |
239 | return try_lock_shared_until(chrono::steady_clock::now() + __rel_time); |
240 | } |
241 | template <class _Clock, class _Duration> |
242 | _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS |
243 | bool |
244 | try_lock_shared_until(const chrono::time_point<_Clock, _Duration>& __abs_time); |
245 | void unlock_shared(); |
246 | }; |
247 | |
248 | template <class _Clock, class _Duration> |
249 | bool |
250 | shared_timed_mutex::try_lock_until( |
251 | const chrono::time_point<_Clock, _Duration>& __abs_time) |
252 | { |
253 | unique_lock<mutex> __lk(__base.__mut_); |
254 | if (__base.__state_ & __base.__write_entered_) |
255 | { |
256 | while (true) |
257 | { |
258 | cv_status __status = __base.__gate1_.wait_until(__lk, __abs_time); |
259 | if ((__base.__state_ & __base.__write_entered_) == 0) |
260 | break; |
261 | if (__status == cv_status::timeout) |
262 | return false; |
263 | } |
264 | } |
265 | __base.__state_ |= __base.__write_entered_; |
266 | if (__base.__state_ & __base.__n_readers_) |
267 | { |
268 | while (true) |
269 | { |
270 | cv_status __status = __base.__gate2_.wait_until(__lk, __abs_time); |
271 | if ((__base.__state_ & __base.__n_readers_) == 0) |
272 | break; |
273 | if (__status == cv_status::timeout) |
274 | { |
275 | __base.__state_ &= ~__base.__write_entered_; |
276 | __base.__gate1_.notify_all(); |
277 | return false; |
278 | } |
279 | } |
280 | } |
281 | return true; |
282 | } |
283 | |
284 | template <class _Clock, class _Duration> |
285 | bool |
286 | shared_timed_mutex::try_lock_shared_until( |
287 | const chrono::time_point<_Clock, _Duration>& __abs_time) |
288 | { |
289 | unique_lock<mutex> __lk(__base.__mut_); |
290 | if ((__base.__state_ & __base.__write_entered_) || (__base.__state_ & __base.__n_readers_) == __base.__n_readers_) |
291 | { |
292 | while (true) |
293 | { |
294 | cv_status status = __base.__gate1_.wait_until(__lk, __abs_time); |
295 | if ((__base.__state_ & __base.__write_entered_) == 0 && |
296 | (__base.__state_ & __base.__n_readers_) < __base.__n_readers_) |
297 | break; |
298 | if (status == cv_status::timeout) |
299 | return false; |
300 | } |
301 | } |
302 | unsigned __num_readers = (__base.__state_ & __base.__n_readers_) + 1; |
303 | __base.__state_ &= ~__base.__n_readers_; |
304 | __base.__state_ |= __num_readers; |
305 | return true; |
306 | } |
307 | |
308 | template <class _Mutex> |
309 | class shared_lock |
310 | { |
311 | public: |
312 | typedef _Mutex mutex_type; |
313 | |
314 | private: |
315 | mutex_type* __m_; |
316 | bool __owns_; |
317 | |
318 | public: |
319 | _LIBCPP_INLINE_VISIBILITY |
320 | shared_lock() _NOEXCEPT |
321 | : __m_(nullptr), |
322 | __owns_(false) |
323 | {} |
324 | |
325 | _LIBCPP_INLINE_VISIBILITY |
326 | explicit shared_lock(mutex_type& __m) |
327 | : __m_(_VSTD::addressof(__m)), |
328 | __owns_(true) |
329 | {__m_->lock_shared();} |
330 | |
331 | _LIBCPP_INLINE_VISIBILITY |
332 | shared_lock(mutex_type& __m, defer_lock_t) _NOEXCEPT |
333 | : __m_(_VSTD::addressof(__m)), |
334 | __owns_(false) |
335 | {} |
336 | |
337 | _LIBCPP_INLINE_VISIBILITY |
338 | shared_lock(mutex_type& __m, try_to_lock_t) |
339 | : __m_(_VSTD::addressof(__m)), |
340 | __owns_(__m.try_lock_shared()) |
341 | {} |
342 | |
343 | _LIBCPP_INLINE_VISIBILITY |
344 | shared_lock(mutex_type& __m, adopt_lock_t) |
345 | : __m_(_VSTD::addressof(__m)), |
346 | __owns_(true) |
347 | {} |
348 | |
349 | template <class _Clock, class _Duration> |
350 | _LIBCPP_INLINE_VISIBILITY |
351 | shared_lock(mutex_type& __m, |
352 | const chrono::time_point<_Clock, _Duration>& __abs_time) |
353 | : __m_(_VSTD::addressof(__m)), |
354 | __owns_(__m.try_lock_shared_until(__abs_time)) |
355 | {} |
356 | |
357 | template <class _Rep, class _Period> |
358 | _LIBCPP_INLINE_VISIBILITY |
359 | shared_lock(mutex_type& __m, |
360 | const chrono::duration<_Rep, _Period>& __rel_time) |
361 | : __m_(_VSTD::addressof(__m)), |
362 | __owns_(__m.try_lock_shared_for(__rel_time)) |
363 | {} |
364 | |
365 | _LIBCPP_INLINE_VISIBILITY |
366 | ~shared_lock() |
367 | { |
368 | if (__owns_) |
369 | __m_->unlock_shared(); |
370 | } |
371 | |
372 | shared_lock(shared_lock const&) = delete; |
373 | shared_lock& operator=(shared_lock const&) = delete; |
374 | |
375 | _LIBCPP_INLINE_VISIBILITY |
376 | shared_lock(shared_lock&& __u) _NOEXCEPT |
377 | : __m_(__u.__m_), |
378 | __owns_(__u.__owns_) |
379 | { |
380 | __u.__m_ = nullptr; |
381 | __u.__owns_ = false; |
382 | } |
383 | |
384 | _LIBCPP_INLINE_VISIBILITY |
385 | shared_lock& operator=(shared_lock&& __u) _NOEXCEPT |
386 | { |
387 | if (__owns_) |
388 | __m_->unlock_shared(); |
389 | __m_ = nullptr; |
390 | __owns_ = false; |
391 | __m_ = __u.__m_; |
392 | __owns_ = __u.__owns_; |
393 | __u.__m_ = nullptr; |
394 | __u.__owns_ = false; |
395 | return *this; |
396 | } |
397 | |
398 | void lock(); |
399 | bool try_lock(); |
400 | template <class Rep, class Period> |
401 | bool try_lock_for(const chrono::duration<Rep, Period>& rel_time); |
402 | template <class Clock, class Duration> |
403 | bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time); |
404 | void unlock(); |
405 | |
406 | // Setters |
407 | _LIBCPP_INLINE_VISIBILITY |
408 | void swap(shared_lock& __u) _NOEXCEPT |
409 | { |
410 | _VSTD::swap(__m_, __u.__m_); |
411 | _VSTD::swap(__owns_, __u.__owns_); |
412 | } |
413 | |
414 | _LIBCPP_INLINE_VISIBILITY |
415 | mutex_type* release() _NOEXCEPT |
416 | { |
417 | mutex_type* __m = __m_; |
418 | __m_ = nullptr; |
419 | __owns_ = false; |
420 | return __m; |
421 | } |
422 | |
423 | // Getters |
424 | _LIBCPP_INLINE_VISIBILITY |
425 | bool owns_lock() const _NOEXCEPT {return __owns_;} |
426 | |
427 | _LIBCPP_INLINE_VISIBILITY |
428 | explicit operator bool () const _NOEXCEPT {return __owns_;} |
429 | |
430 | _LIBCPP_INLINE_VISIBILITY |
431 | mutex_type* mutex() const _NOEXCEPT {return __m_;} |
432 | }; |
433 | |
434 | template <class _Mutex> |
435 | void |
436 | shared_lock<_Mutex>::lock() |
437 | { |
438 | if (__m_ == nullptr) |
439 | __throw_system_error(EPERM, "shared_lock::lock: references null mutex" ); |
440 | if (__owns_) |
441 | __throw_system_error(EDEADLK, "shared_lock::lock: already locked" ); |
442 | __m_->lock_shared(); |
443 | __owns_ = true; |
444 | } |
445 | |
446 | template <class _Mutex> |
447 | bool |
448 | shared_lock<_Mutex>::try_lock() |
449 | { |
450 | if (__m_ == nullptr) |
451 | __throw_system_error(EPERM, "shared_lock::try_lock: references null mutex" ); |
452 | if (__owns_) |
453 | __throw_system_error(EDEADLK, "shared_lock::try_lock: already locked" ); |
454 | __owns_ = __m_->try_lock_shared(); |
455 | return __owns_; |
456 | } |
457 | |
458 | template <class _Mutex> |
459 | template <class _Rep, class _Period> |
460 | bool |
461 | shared_lock<_Mutex>::try_lock_for(const chrono::duration<_Rep, _Period>& __d) |
462 | { |
463 | if (__m_ == nullptr) |
464 | __throw_system_error(EPERM, "shared_lock::try_lock_for: references null mutex" ); |
465 | if (__owns_) |
466 | __throw_system_error(EDEADLK, "shared_lock::try_lock_for: already locked" ); |
467 | __owns_ = __m_->try_lock_shared_for(__d); |
468 | return __owns_; |
469 | } |
470 | |
471 | template <class _Mutex> |
472 | template <class _Clock, class _Duration> |
473 | bool |
474 | shared_lock<_Mutex>::try_lock_until(const chrono::time_point<_Clock, _Duration>& __t) |
475 | { |
476 | if (__m_ == nullptr) |
477 | __throw_system_error(EPERM, "shared_lock::try_lock_until: references null mutex" ); |
478 | if (__owns_) |
479 | __throw_system_error(EDEADLK, "shared_lock::try_lock_until: already locked" ); |
480 | __owns_ = __m_->try_lock_shared_until(__t); |
481 | return __owns_; |
482 | } |
483 | |
484 | template <class _Mutex> |
485 | void |
486 | shared_lock<_Mutex>::unlock() |
487 | { |
488 | if (!__owns_) |
489 | __throw_system_error(EPERM, "shared_lock::unlock: not locked" ); |
490 | __m_->unlock_shared(); |
491 | __owns_ = false; |
492 | } |
493 | |
494 | template <class _Mutex> |
495 | inline _LIBCPP_INLINE_VISIBILITY |
496 | void |
497 | swap(shared_lock<_Mutex>& __x, shared_lock<_Mutex>& __y) _NOEXCEPT |
498 | {__x.swap(__y);} |
499 | |
500 | _LIBCPP_END_NAMESPACE_STD |
501 | |
502 | #endif // !_LIBCPP_HAS_NO_THREADS |
503 | |
504 | #endif // _LIBCPP_STD_VER > 11 |
505 | |
506 | _LIBCPP_POP_MACROS |
507 | |
508 | #endif // _LIBCPP_SHARED_MUTEX |
509 | |