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