1 // <shared_mutex> -*- C++ -*-
3 // Copyright (C) 2013-2015 Free Software Foundation, Inc.
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
25 /** @file include/shared_mutex
26 * This is a Standard C++ Library header.
29 #ifndef _GLIBCXX_SHARED_MUTEX
30 #define _GLIBCXX_SHARED_MUTEX 1
32 #pragma GCC system_header
34 #if __cplusplus <= 201103L
35 # include <bits/c++14_warning.h>
38 #include <bits/c++config.h>
40 #include <condition_variable>
41 #include <bits/functexcept.h>
43 namespace std _GLIBCXX_VISIBILITY(default)
45 _GLIBCXX_BEGIN_NAMESPACE_VERSION
52 #ifdef _GLIBCXX_USE_C99_STDINT_TR1
53 #ifdef _GLIBCXX_HAS_GTHREADS
55 #define __cpp_lib_shared_timed_mutex 201402
57 /// shared_timed_mutex
58 class shared_timed_mutex
60 #if defined(__GTHREADS_CXX0X)
61 typedef chrono::system_clock __clock_t;
63 pthread_rwlock_t _M_rwlock;
68 int __ret = pthread_rwlock_init(&_M_rwlock, NULL);
71 else if (__ret == EAGAIN)
72 __throw_system_error(int(errc::resource_unavailable_try_again));
73 else if (__ret == EPERM)
74 __throw_system_error(int(errc::operation_not_permitted));
75 // Errors not handled: EBUSY, EINVAL
76 _GLIBCXX_DEBUG_ASSERT(__ret == 0);
81 int __ret __attribute((__unused__)) = pthread_rwlock_destroy(&_M_rwlock);
82 // Errors not handled: EBUSY, EINVAL
83 _GLIBCXX_DEBUG_ASSERT(__ret == 0);
86 shared_timed_mutex(const shared_timed_mutex&) = delete;
87 shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
89 // Exclusive ownership
94 int __ret = pthread_rwlock_wrlock(&_M_rwlock);
96 __throw_system_error(int(errc::resource_deadlock_would_occur));
97 // Errors not handled: EINVAL
98 _GLIBCXX_DEBUG_ASSERT(__ret == 0);
104 int __ret = pthread_rwlock_trywrlock(&_M_rwlock);
105 if (__ret == EBUSY) return false;
106 // Errors not handled: EINVAL
107 _GLIBCXX_DEBUG_ASSERT(__ret == 0);
111 #if _GTHREAD_USE_MUTEX_TIMEDLOCK
112 template<typename _Rep, typename _Period>
114 try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
116 return try_lock_until(__clock_t::now() + __rel_time);
119 template<typename _Duration>
121 try_lock_until(const chrono::time_point<__clock_t, _Duration>& __atime)
123 auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
124 auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
126 __gthread_time_t __ts =
128 static_cast<std::time_t>(__s.time_since_epoch().count()),
129 static_cast<long>(__ns.count())
132 int __ret = pthread_rwlock_timedwrlock(&_M_rwlock, &__ts);
133 // On self-deadlock, we just fail to acquire the lock. Technically,
134 // the program violated the precondition.
135 if (__ret == ETIMEDOUT || __ret == EDEADLK)
137 // Errors not handled: EINVAL
138 _GLIBCXX_DEBUG_ASSERT(__ret == 0);
142 template<typename _Clock, typename _Duration>
144 try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time)
146 // DR 887 - Sync unknown clock to known clock.
147 const typename _Clock::time_point __c_entry = _Clock::now();
148 const __clock_t::time_point __s_entry = __clock_t::now();
149 const auto __delta = __abs_time - __c_entry;
150 const auto __s_atime = __s_entry + __delta;
151 return try_lock_until(__s_atime);
158 int __ret __attribute((__unused__)) = pthread_rwlock_unlock(&_M_rwlock);
159 // Errors not handled: EPERM, EBUSY, EINVAL
160 _GLIBCXX_DEBUG_ASSERT(__ret == 0);
168 int __ret = pthread_rwlock_rdlock(&_M_rwlock);
169 if (__ret == EDEADLK)
170 __throw_system_error(int(errc::resource_deadlock_would_occur));
172 // Maximum number of read locks has been exceeded.
173 __throw_system_error(int(errc::device_or_resource_busy));
174 // Errors not handled: EINVAL
175 _GLIBCXX_DEBUG_ASSERT(__ret == 0);
181 int __ret = pthread_rwlock_tryrdlock(&_M_rwlock);
182 // If the maximum number of read locks has been exceeded, we just fail
183 // to acquire the lock. Unlike for lock(), we are not allowed to throw
185 if (__ret == EBUSY || __ret == EAGAIN) return false;
186 // Errors not handled: EINVAL
187 _GLIBCXX_DEBUG_ASSERT(__ret == 0);
191 #if _GTHREAD_USE_MUTEX_TIMEDLOCK
192 template<typename _Rep, typename _Period>
194 try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time)
196 return try_lock_shared_until(__clock_t::now() + __rel_time);
199 template<typename _Duration>
201 try_lock_shared_until(const chrono::time_point<__clock_t,
204 auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
205 auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
207 __gthread_time_t __ts =
209 static_cast<std::time_t>(__s.time_since_epoch().count()),
210 static_cast<long>(__ns.count())
213 int __ret = pthread_rwlock_timedrdlock(&_M_rwlock, &__ts);
214 // If the maximum number of read locks has been exceeded, or we would
215 // deadlock, we just fail to acquire the lock. Unlike for lock(),
216 // we are not allowed to throw an exception.
217 if (__ret == ETIMEDOUT || __ret == EAGAIN || __ret == EDEADLK)
219 // Errors not handled: EINVAL
220 _GLIBCXX_DEBUG_ASSERT(__ret == 0);
224 template<typename _Clock, typename _Duration>
226 try_lock_shared_until(const chrono::time_point<_Clock,
227 _Duration>& __abs_time)
229 // DR 887 - Sync unknown clock to known clock.
230 const typename _Clock::time_point __c_entry = _Clock::now();
231 const __clock_t::time_point __s_entry = __clock_t::now();
232 const auto __delta = __abs_time - __c_entry;
233 const auto __s_atime = __s_entry + __delta;
234 return try_lock_shared_until(__s_atime);
244 #else // defined(__GTHREADS_CXX0X)
246 #if _GTHREAD_USE_MUTEX_TIMEDLOCK
247 struct _Mutex : mutex, __timed_mutex_impl<_Mutex>
249 template<typename _Rep, typename _Period>
251 try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
252 { return _M_try_lock_for(__rtime); }
254 template<typename _Clock, typename _Duration>
256 try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
257 { return _M_try_lock_until(__atime); }
260 typedef mutex _Mutex;
263 // Based on Howard Hinnant's reference implementation from N2406
266 condition_variable _M_gate1;
267 condition_variable _M_gate2;
270 static constexpr unsigned _S_write_entered
271 = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1);
272 static constexpr unsigned _M_n_readers = ~_S_write_entered;
275 shared_timed_mutex() : _M_state(0) {}
277 ~shared_timed_mutex()
279 _GLIBCXX_DEBUG_ASSERT( _M_state == 0 );
282 shared_timed_mutex(const shared_timed_mutex&) = delete;
283 shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
285 // Exclusive ownership
290 unique_lock<mutex> __lk(_M_mut);
291 while (_M_state & _S_write_entered)
293 _M_state |= _S_write_entered;
294 while (_M_state & _M_n_readers)
301 unique_lock<mutex> __lk(_M_mut, try_to_lock);
302 if (__lk.owns_lock() && _M_state == 0)
304 _M_state = _S_write_entered;
310 #if _GTHREAD_USE_MUTEX_TIMEDLOCK
311 template<typename _Rep, typename _Period>
313 try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
315 unique_lock<_Mutex> __lk(_M_mut, __rel_time);
316 if (__lk.owns_lock() && _M_state == 0)
318 _M_state = _S_write_entered;
324 template<typename _Clock, typename _Duration>
326 try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time)
328 unique_lock<_Mutex> __lk(_M_mut, __abs_time);
329 if (__lk.owns_lock() && _M_state == 0)
331 _M_state = _S_write_entered;
342 lock_guard<_Mutex> __lk(_M_mut);
345 _M_gate1.notify_all();
353 unique_lock<mutex> __lk(_M_mut);
354 while ((_M_state & _S_write_entered)
355 || (_M_state & _M_n_readers) == _M_n_readers)
359 unsigned __num_readers = (_M_state & _M_n_readers) + 1;
360 _M_state &= ~_M_n_readers;
361 _M_state |= __num_readers;
367 unique_lock<_Mutex> __lk(_M_mut, try_to_lock);
368 unsigned __num_readers = _M_state & _M_n_readers;
369 if (__lk.owns_lock() && !(_M_state & _S_write_entered)
370 && __num_readers != _M_n_readers)
373 _M_state &= ~_M_n_readers;
374 _M_state |= __num_readers;
380 #if _GTHREAD_USE_MUTEX_TIMEDLOCK
381 template<typename _Rep, typename _Period>
383 try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time)
385 unique_lock<_Mutex> __lk(_M_mut, __rel_time);
386 if (__lk.owns_lock())
388 unsigned __num_readers = _M_state & _M_n_readers;
389 if (!(_M_state & _S_write_entered)
390 && __num_readers != _M_n_readers)
393 _M_state &= ~_M_n_readers;
394 _M_state |= __num_readers;
401 template <typename _Clock, typename _Duration>
403 try_lock_shared_until(const chrono::time_point<_Clock,
404 _Duration>& __abs_time)
406 unique_lock<_Mutex> __lk(_M_mut, __abs_time);
407 if (__lk.owns_lock())
409 unsigned __num_readers = _M_state & _M_n_readers;
410 if (!(_M_state & _S_write_entered)
411 && __num_readers != _M_n_readers)
414 _M_state &= ~_M_n_readers;
415 _M_state |= __num_readers;
426 lock_guard<_Mutex> __lk(_M_mut);
427 unsigned __num_readers = (_M_state & _M_n_readers) - 1;
428 _M_state &= ~_M_n_readers;
429 _M_state |= __num_readers;
430 if (_M_state & _S_write_entered)
432 if (__num_readers == 0)
433 _M_gate2.notify_one();
437 if (__num_readers == _M_n_readers - 1)
438 _M_gate1.notify_one();
441 #endif // !defined(__GTHREADS_CXX0X)
443 #endif // _GLIBCXX_HAS_GTHREADS
446 template<typename _Mutex>
450 typedef _Mutex mutex_type;
454 shared_lock() noexcept : _M_pm(nullptr), _M_owns(false) { }
457 shared_lock(mutex_type& __m) : _M_pm(&__m), _M_owns(true)
458 { __m.lock_shared(); }
460 shared_lock(mutex_type& __m, defer_lock_t) noexcept
461 : _M_pm(&__m), _M_owns(false) { }
463 shared_lock(mutex_type& __m, try_to_lock_t)
464 : _M_pm(&__m), _M_owns(__m.try_lock_shared()) { }
466 shared_lock(mutex_type& __m, adopt_lock_t)
467 : _M_pm(&__m), _M_owns(true) { }
469 template<typename _Clock, typename _Duration>
470 shared_lock(mutex_type& __m,
471 const chrono::time_point<_Clock, _Duration>& __abs_time)
472 : _M_pm(&__m), _M_owns(__m.try_lock_shared_until(__abs_time)) { }
474 template<typename _Rep, typename _Period>
475 shared_lock(mutex_type& __m,
476 const chrono::duration<_Rep, _Period>& __rel_time)
477 : _M_pm(&__m), _M_owns(__m.try_lock_shared_for(__rel_time)) { }
482 _M_pm->unlock_shared();
485 shared_lock(shared_lock const&) = delete;
486 shared_lock& operator=(shared_lock const&) = delete;
488 shared_lock(shared_lock&& __sl) noexcept : shared_lock()
492 operator=(shared_lock&& __sl) noexcept
494 shared_lock(std::move(__sl)).swap(*this);
502 _M_pm->lock_shared();
510 return _M_owns = _M_pm->try_lock_shared();
513 template<typename _Rep, typename _Period>
515 try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
518 return _M_owns = _M_pm->try_lock_shared_for(__rel_time);
521 template<typename _Clock, typename _Duration>
523 try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time)
526 return _M_owns = _M_pm->try_lock_shared_until(__abs_time);
533 __throw_system_error(int(errc::resource_deadlock_would_occur));
534 _M_pm->unlock_shared();
541 swap(shared_lock& __u) noexcept
543 std::swap(_M_pm, __u._M_pm);
544 std::swap(_M_owns, __u._M_owns);
551 return std::exchange(_M_pm, nullptr);
556 bool owns_lock() const noexcept { return _M_owns; }
558 explicit operator bool() const noexcept { return _M_owns; }
560 mutex_type* mutex() const noexcept { return _M_pm; }
566 if (_M_pm == nullptr)
567 __throw_system_error(int(errc::operation_not_permitted));
569 __throw_system_error(int(errc::resource_deadlock_would_occur));
576 /// Swap specialization for shared_lock
577 template<typename _Mutex>
579 swap(shared_lock<_Mutex>& __x, shared_lock<_Mutex>& __y) noexcept
582 #endif // _GLIBCXX_USE_C99_STDINT_TR1
585 _GLIBCXX_END_NAMESPACE_VERSION
590 #endif // _GLIBCXX_SHARED_MUTEX