libstdc++
shared_mutex
Go to the documentation of this file.
1 // <shared_mutex> -*- C++ -*-
2 
3 // Copyright (C) 2013-2026 Free Software Foundation, Inc.
4 //
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)
9 // any later version.
10 
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.
15 
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.
19 
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/>.
24 
25 /** @file include/shared_mutex
26  * This is a Standard C++ Library header.
27  */
28 
29 #ifndef _GLIBCXX_SHARED_MUTEX
30 #define _GLIBCXX_SHARED_MUTEX 1
31 
32 #ifdef _GLIBCXX_SYSHDR
33 #pragma GCC system_header
34 #endif
35 
36 #include <bits/requires_hosted.h> // concurrency
37 
38 #if __cplusplus >= 201402L
39 
40 #include <bits/chrono.h>
41 #include <bits/error_constants.h>
42 #include <bits/new_throw.h>
43 #include <bits/functexcept.h>
44 #include <bits/move.h> // move, __exchange
45 #include <bits/std_mutex.h> // defer_lock_t
46 
47 #define __glibcxx_want_shared_mutex
48 #define __glibcxx_want_shared_timed_mutex
49 #include <bits/version.h>
50 
51 #if ! (_GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK)
52 # include <condition_variable>
53 #endif
54 
55 namespace std _GLIBCXX_VISIBILITY(default)
56 {
57 _GLIBCXX_BEGIN_NAMESPACE_VERSION
58 
59  /**
60  * @addtogroup mutexes
61  * @{
62  */
63 
64 #ifdef _GLIBCXX_HAS_GTHREADS
65 
66 #ifdef __cpp_lib_shared_mutex // C++ >= 17 && hosted && gthread
67  class shared_mutex;
68 #endif
69 
70  class shared_timed_mutex;
71 
72  /// @cond undocumented
73 
74 #if _GLIBCXX_USE_PTHREAD_RWLOCK_T
75 #ifdef __gthrw
76 #define _GLIBCXX_GTHRW(name) \
77  __gthrw(pthread_ ## name); \
78  inline int \
79  __glibcxx_ ## name (pthread_rwlock_t *__rwlock) \
80  { \
81  if (__gthread_active_p ()) \
82  return __gthrw_(pthread_ ## name) (__rwlock); \
83  else \
84  return 0; \
85  }
86  _GLIBCXX_GTHRW(rwlock_rdlock)
87  _GLIBCXX_GTHRW(rwlock_tryrdlock)
88  _GLIBCXX_GTHRW(rwlock_wrlock)
89  _GLIBCXX_GTHRW(rwlock_trywrlock)
90  _GLIBCXX_GTHRW(rwlock_unlock)
91 # ifndef PTHREAD_RWLOCK_INITIALIZER
92  _GLIBCXX_GTHRW(rwlock_destroy)
93  __gthrw(pthread_rwlock_init);
94  inline int
95  __glibcxx_rwlock_init (pthread_rwlock_t *__rwlock)
96  {
97  if (__gthread_active_p ())
98  return __gthrw_(pthread_rwlock_init) (__rwlock, NULL);
99  else
100  return 0;
101  }
102 # endif
103 # if _GTHREAD_USE_MUTEX_TIMEDLOCK
104  __gthrw(pthread_rwlock_timedrdlock);
105  inline int
106  __glibcxx_rwlock_timedrdlock (pthread_rwlock_t *__rwlock,
107  const timespec *__ts)
108  {
109  if (__gthread_active_p ())
110  return __gthrw_(pthread_rwlock_timedrdlock) (__rwlock, __ts);
111  else
112  return 0;
113  }
114  __gthrw(pthread_rwlock_timedwrlock);
115  inline int
116  __glibcxx_rwlock_timedwrlock (pthread_rwlock_t *__rwlock,
117  const timespec *__ts)
118  {
119  if (__gthread_active_p ())
120  return __gthrw_(pthread_rwlock_timedwrlock) (__rwlock, __ts);
121  else
122  return 0;
123  }
124 # endif
125 #else
126  inline int
127  __glibcxx_rwlock_rdlock (pthread_rwlock_t *__rwlock)
128  { return pthread_rwlock_rdlock (__rwlock); }
129  inline int
130  __glibcxx_rwlock_tryrdlock (pthread_rwlock_t *__rwlock)
131  { return pthread_rwlock_tryrdlock (__rwlock); }
132  inline int
133  __glibcxx_rwlock_wrlock (pthread_rwlock_t *__rwlock)
134  { return pthread_rwlock_wrlock (__rwlock); }
135  inline int
136  __glibcxx_rwlock_trywrlock (pthread_rwlock_t *__rwlock)
137  { return pthread_rwlock_trywrlock (__rwlock); }
138  inline int
139  __glibcxx_rwlock_unlock (pthread_rwlock_t *__rwlock)
140  { return pthread_rwlock_unlock (__rwlock); }
141  inline int
142  __glibcxx_rwlock_destroy(pthread_rwlock_t *__rwlock)
143  { return pthread_rwlock_destroy (__rwlock); }
144  inline int
145  __glibcxx_rwlock_init(pthread_rwlock_t *__rwlock)
146  { return pthread_rwlock_init (__rwlock, NULL); }
147 # if _GTHREAD_USE_MUTEX_TIMEDLOCK
148  inline int
149  __glibcxx_rwlock_timedrdlock (pthread_rwlock_t *__rwlock,
150  const timespec *__ts)
151  { return pthread_rwlock_timedrdlock (__rwlock, __ts); }
152  inline int
153  __glibcxx_rwlock_timedwrlock (pthread_rwlock_t *__rwlock,
154  const timespec *__ts)
155  { return pthread_rwlock_timedwrlock (__rwlock, __ts); }
156 # endif
157 #endif
158 
159  /// A shared mutex type implemented using pthread_rwlock_t.
160  class __shared_mutex_pthread
161  {
162  friend class shared_timed_mutex;
163 
164 #ifdef PTHREAD_RWLOCK_INITIALIZER
165  pthread_rwlock_t _M_rwlock = PTHREAD_RWLOCK_INITIALIZER;
166 
167  public:
168  __shared_mutex_pthread() = default;
169  ~__shared_mutex_pthread() = default;
170 #else
171  pthread_rwlock_t _M_rwlock;
172 
173  public:
174  __shared_mutex_pthread()
175  {
176  int __ret = __glibcxx_rwlock_init(&_M_rwlock);
177  if (__ret == ENOMEM)
178  __throw_bad_alloc();
179  else if (__ret == EAGAIN)
180  __throw_system_error(int(errc::resource_unavailable_try_again));
181  else if (__ret == EPERM)
182  __throw_system_error(int(errc::operation_not_permitted));
183  // Errors not handled: EBUSY, EINVAL
184  __glibcxx_assert(__ret == 0);
185  }
186 
187  ~__shared_mutex_pthread()
188  {
189  int __ret __attribute((__unused__)) = __glibcxx_rwlock_destroy(&_M_rwlock);
190  // Errors not handled: EBUSY, EINVAL
191  __glibcxx_assert(__ret == 0);
192  }
193 #endif
194 
195  __shared_mutex_pthread(const __shared_mutex_pthread&) = delete;
196  __shared_mutex_pthread& operator=(const __shared_mutex_pthread&) = delete;
197 
198  void
199  lock()
200  {
201  int __ret = __glibcxx_rwlock_wrlock(&_M_rwlock);
202  if (__ret == EDEADLK)
203  __throw_system_error(int(errc::resource_deadlock_would_occur));
204  // Errors not handled: EINVAL
205  __glibcxx_assert(__ret == 0);
206  }
207 
208  bool
209  try_lock()
210  {
211  int __ret = __glibcxx_rwlock_trywrlock(&_M_rwlock);
212  if (__ret == 0)
213  return true;
214  if (__ret == EBUSY)
215  return false;
216  // Errors not handled: EINVAL, EDEADLK
217  __glibcxx_assert(__ret == 0);
218  // try_lock() is not permitted to throw
219  return false;
220  }
221 
222  void
223  unlock()
224  {
225  int __ret __attribute((__unused__)) = __glibcxx_rwlock_unlock(&_M_rwlock);
226  // Errors not handled: EPERM, EBUSY, EINVAL
227  __glibcxx_assert(__ret == 0);
228  }
229 
230  // Shared ownership
231 
232  void
233  lock_shared()
234  {
235  int __ret;
236  // We retry if we exceeded the maximum number of read locks supported by
237  // the POSIX implementation; this can result in busy-waiting, but this
238  // is okay based on the current specification of forward progress
239  // guarantees by the standard.
240  do
241  __ret = __glibcxx_rwlock_rdlock(&_M_rwlock);
242  while (__ret == EAGAIN);
243  if (__ret == EDEADLK)
244  __throw_system_error(int(errc::resource_deadlock_would_occur));
245  // Errors not handled: EINVAL
246  __glibcxx_assert(__ret == 0);
247  }
248 
249  bool
250  try_lock_shared()
251  {
252  int __ret = __glibcxx_rwlock_tryrdlock(&_M_rwlock);
253  // If the maximum number of read locks has been exceeded, we just fail
254  // to acquire the lock. Unlike for lock(), we are not allowed to throw
255  // an exception.
256  if (__ret == EBUSY || __ret == EAGAIN) return false;
257  // Errors not handled: EINVAL
258  __glibcxx_assert(__ret == 0);
259  return true;
260  }
261 
262  void
263  unlock_shared()
264  {
265  unlock();
266  }
267 
268  void* native_handle() { return &_M_rwlock; }
269  };
270 #endif
271 
272 #if ! (_GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK)
273  /// A shared mutex type implemented using std::condition_variable.
274  class __shared_mutex_cv
275  {
276  friend class shared_timed_mutex;
277 
278  // Based on Howard Hinnant's reference implementation from N2406.
279 
280  // The high bit of _M_state is the write-entered flag which is set to
281  // indicate a writer has taken the lock or is queuing to take the lock.
282  // The remaining bits are the count of reader locks.
283  //
284  // To take a reader lock, block on gate1 while the write-entered flag is
285  // set or the maximum number of reader locks is held, then increment the
286  // reader lock count.
287  // To release, decrement the count, then if the write-entered flag is set
288  // and the count is zero then signal gate2 to wake a queued writer,
289  // otherwise if the maximum number of reader locks was held signal gate1
290  // to wake a reader.
291  //
292  // To take a writer lock, block on gate1 while the write-entered flag is
293  // set, then set the write-entered flag to start queueing, then block on
294  // gate2 while the number of reader locks is non-zero.
295  // To release, unset the write-entered flag and signal gate1 to wake all
296  // blocked readers and writers.
297  //
298  // This means that when no reader locks are held readers and writers get
299  // equal priority. When one or more reader locks is held a writer gets
300  // priority and no more reader locks can be taken while the writer is
301  // queued.
302 
303  // Only locked when accessing _M_state or waiting on condition variables.
304  mutex _M_mut;
305  // Used to block while write-entered is set or reader count at maximum.
306  condition_variable _M_gate1;
307  // Used to block queued writers while reader count is non-zero.
308  condition_variable _M_gate2;
309  // The write-entered flag and reader count.
310  unsigned _M_state;
311 
312  static constexpr unsigned _S_write_entered
313  = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1);
314  static constexpr unsigned _S_max_readers = ~_S_write_entered;
315 
316  // Test whether the write-entered flag is set. _M_mut must be locked.
317  bool _M_write_entered() const { return _M_state & _S_write_entered; }
318 
319  // The number of reader locks currently held. _M_mut must be locked.
320  unsigned _M_readers() const { return _M_state & _S_max_readers; }
321 
322  public:
323  __shared_mutex_cv() : _M_state(0) {}
324 
325  ~__shared_mutex_cv()
326  {
327  __glibcxx_assert( _M_state == 0 );
328  }
329 
330  __shared_mutex_cv(const __shared_mutex_cv&) = delete;
331  __shared_mutex_cv& operator=(const __shared_mutex_cv&) = delete;
332 
333  // Exclusive ownership
334 
335  void
336  lock()
337  {
338  unique_lock<mutex> __lk(_M_mut);
339  // Wait until we can set the write-entered flag.
340  _M_gate1.wait(__lk, [this]{ return !_M_write_entered(); });
341  _M_state |= _S_write_entered;
342  // Then wait until there are no more readers.
343  _M_gate2.wait(__lk, [this]{ return _M_readers() == 0; });
344  }
345 
346  bool
347  try_lock()
348  {
349  unique_lock<mutex> __lk(_M_mut, try_to_lock);
350  if (__lk.owns_lock() && _M_state == 0)
351  {
352  _M_state = _S_write_entered;
353  return true;
354  }
355  return false;
356  }
357 
358  void
359  unlock()
360  {
361  lock_guard<mutex> __lk(_M_mut);
362  __glibcxx_assert( _M_write_entered() );
363  _M_state = 0;
364  // call notify_all() while mutex is held so that another thread can't
365  // lock and unlock the mutex then destroy *this before we make the call.
366  _M_gate1.notify_all();
367  }
368 
369  // Shared ownership
370 
371  void
372  lock_shared()
373  {
374  unique_lock<mutex> __lk(_M_mut);
375  _M_gate1.wait(__lk, [this]{ return _M_state < _S_max_readers; });
376  ++_M_state;
377  }
378 
379  bool
380  try_lock_shared()
381  {
382  unique_lock<mutex> __lk(_M_mut, try_to_lock);
383  if (!__lk.owns_lock())
384  return false;
385  if (_M_state < _S_max_readers)
386  {
387  ++_M_state;
388  return true;
389  }
390  return false;
391  }
392 
393  void
394  unlock_shared()
395  {
396  lock_guard<mutex> __lk(_M_mut);
397  __glibcxx_assert( _M_readers() > 0 );
398  auto __prev = _M_state--;
399  if (_M_write_entered())
400  {
401  // Wake the queued writer if there are no more readers.
402  if (_M_readers() == 0)
403  _M_gate2.notify_one();
404  // No need to notify gate1 because we give priority to the queued
405  // writer, and that writer will eventually notify gate1 after it
406  // clears the write-entered flag.
407  }
408  else
409  {
410  // Wake any thread that was blocked on reader overflow.
411  if (__prev == _S_max_readers)
412  _M_gate1.notify_one();
413  }
414  }
415  };
416 #endif
417  /// @endcond
418 
419 #ifdef __cpp_lib_shared_mutex
420  /// The standard shared mutex type.
421  class shared_mutex
422  {
423  public:
424  shared_mutex() = default;
425  ~shared_mutex() = default;
426 
427  shared_mutex(const shared_mutex&) = delete;
428  shared_mutex& operator=(const shared_mutex&) = delete;
429 
430  // Exclusive ownership
431 
432  void lock() { _M_impl.lock(); }
433  [[nodiscard]] bool try_lock() { return _M_impl.try_lock(); }
434  void unlock() { _M_impl.unlock(); }
435 
436  // Shared ownership
437 
438  void lock_shared() { _M_impl.lock_shared(); }
439  [[nodiscard]] bool try_lock_shared() { return _M_impl.try_lock_shared(); }
440  void unlock_shared() { _M_impl.unlock_shared(); }
441 
442 #if _GLIBCXX_USE_PTHREAD_RWLOCK_T
443  typedef void* native_handle_type;
444  native_handle_type native_handle() { return _M_impl.native_handle(); }
445 
446  private:
447  __shared_mutex_pthread _M_impl;
448 #else
449  private:
450  __shared_mutex_cv _M_impl;
451 #endif
452  };
453 #endif // __cpp_lib_shared_mutex
454 
455  /// @cond undocumented
456 #if _GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK
457  using __shared_timed_mutex_base = __shared_mutex_pthread;
458 #else
459  using __shared_timed_mutex_base = __shared_mutex_cv;
460 #endif
461  /// @endcond
462 
463  /// The standard shared timed mutex type.
465  : private __shared_timed_mutex_base
466  {
467  using _Base = __shared_timed_mutex_base;
468 
469  // Must use the same clock as condition_variable for __shared_mutex_cv.
470 #ifdef _GLIBCXX_USE_PTHREAD_RWLOCK_CLOCKLOCK
472 #else
474 #endif
475 
476  public:
477  shared_timed_mutex() = default;
478  ~shared_timed_mutex() = default;
479 
480  shared_timed_mutex(const shared_timed_mutex&) = delete;
481  shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
482 
483  // Exclusive ownership
484 
485  void lock() { _Base::lock(); }
486  _GLIBCXX_NODISCARD bool try_lock() { return _Base::try_lock(); }
487  void unlock() { _Base::unlock(); }
488 
489  template<typename _Rep, typename _Period>
490  _GLIBCXX_NODISCARD
491  bool
492  try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
493  {
494  auto __rt = chrono::duration_cast<__clock_t::duration>(__rtime);
496  ++__rt;
497  return try_lock_until(__clock_t::now() + __rt);
498  }
499 
500  // Shared ownership
501 
502  void lock_shared() { _Base::lock_shared(); }
503  _GLIBCXX_NODISCARD
504  bool try_lock_shared() { return _Base::try_lock_shared(); }
505  void unlock_shared() { _Base::unlock_shared(); }
506 
507  template<typename _Rep, typename _Period>
508  _GLIBCXX_NODISCARD
509  bool
510  try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rtime)
511  {
512  auto __rt = chrono::duration_cast<__clock_t::duration>(__rtime);
514  ++__rt;
515  return try_lock_shared_until(__clock_t::now() + __rt);
516  }
517 
518 #if _GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK
519 
520  // Exclusive ownership
521 
522  template<typename _Duration>
523  _GLIBCXX_NODISCARD
524  bool
525  try_lock_until(const chrono::time_point<chrono::system_clock,
526  _Duration>& __atime)
527  {
528  struct timespec __ts = chrono::__to_timeout_timespec(__atime);
529  int __ret = __glibcxx_rwlock_timedwrlock(&_M_rwlock, &__ts);
530  if (__ret == 0)
531  return true;
532  if (__ret == ETIMEDOUT)
533  return false;
534  // Errors not handled: EINVAL, EDEADLK
535  __glibcxx_assert(__ret == 0);
536  return false;
537  }
538 
539 #ifdef _GLIBCXX_USE_PTHREAD_RWLOCK_CLOCKLOCK
540  template<typename _Duration>
541  _GLIBCXX_NODISCARD
542  bool
543  try_lock_until(const chrono::time_point<chrono::steady_clock,
544  _Duration>& __atime)
545  {
546  struct timespec __ts = chrono::__to_timeout_timespec(__atime);
547  int __ret = pthread_rwlock_clockwrlock(&_M_rwlock, CLOCK_MONOTONIC,
548  &__ts);
549  if (__ret == 0)
550  return true;
551  if (__ret == ETIMEDOUT)
552  return false;
553  // Errors not handled: EINVAL, EDEADLK
554  __glibcxx_assert(__ret == 0);
555  return false;
556  }
557 #endif
558 
559  template<typename _Clock, typename _Duration>
560  _GLIBCXX_NODISCARD
561  bool
562  try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
563  {
564 #if __cplusplus > 201703L
565  static_assert(chrono::is_clock_v<_Clock>);
566 #endif
567  // The user-supplied clock may not tick at the same rate as
568  // steady_clock, so we must loop in order to guarantee that
569  // the timeout has expired before returning false.
570  typename _Clock::time_point __now = _Clock::now();
571  do {
572  auto __rtime = __atime - __now;
573  if (try_lock_for(__rtime))
574  return true;
575  __now = _Clock::now();
576  } while (__atime > __now);
577  return false;
578  }
579 
580  // Shared ownership
581 
582  template<typename _Duration>
583  _GLIBCXX_NODISCARD
584  bool
585  try_lock_shared_until(const chrono::time_point<chrono::system_clock,
586  _Duration>& __atime)
587  {
588  struct timespec __ts = chrono::__to_timeout_timespec(__atime);
589 
590  int __ret;
591  // Unlike for lock(), we are not allowed to throw an exception so if
592  // the maximum number of read locks has been exceeded, or we would
593  // deadlock, we just try to acquire the lock again (and will time out
594  // eventually).
595  // In cases where we would exceed the maximum number of read locks
596  // throughout the whole time until the timeout, we will fail to
597  // acquire the lock even if it would be logically free; however, this
598  // is allowed by the standard, and we made a "strong effort"
599  // (see C++14 30.4.1.4p26).
600  do
601  __ret = __glibcxx_rwlock_timedrdlock(&_M_rwlock, &__ts);
602  while (__ret == EAGAIN);
603  if (__ret == 0)
604  return true;
605  if (__ret == ETIMEDOUT)
606  return false;
607  // Errors not handled: EINVAL, EDEADLK
608  __glibcxx_assert(__ret == 0);
609  return false;
610  }
611 
612 #ifdef _GLIBCXX_USE_PTHREAD_RWLOCK_CLOCKLOCK
613  template<typename _Duration>
614  _GLIBCXX_NODISCARD
615  bool
616  try_lock_shared_until(const chrono::time_point<chrono::steady_clock,
617  _Duration>& __atime)
618  {
619  struct timespec __ts = chrono::__to_timeout_timespec(__atime);
620  int __ret = pthread_rwlock_clockrdlock(&_M_rwlock, CLOCK_MONOTONIC,
621  &__ts);
622  // On self-deadlock, if _GLIBCXX_ASSERTIONS is not defined, we just
623  // fail to acquire the lock. Technically, the program violated the
624  // precondition.
625  if (__ret == 0)
626  return true;
627  if (__ret == ETIMEDOUT)
628  return false;
629  // Errors not handled: EINVAL, EDEADLK
630  __glibcxx_assert(__ret == 0);
631  return false;
632  }
633 #endif
634 
635  template<typename _Clock, typename _Duration>
636  _GLIBCXX_NODISCARD
637  bool
638  try_lock_shared_until(const chrono::time_point<_Clock,
639  _Duration>& __atime)
640  {
641 #if __cplusplus > 201703L
642  static_assert(chrono::is_clock_v<_Clock>);
643 #endif
644  // The user-supplied clock may not tick at the same rate as
645  // steady_clock, so we must loop in order to guarantee that
646  // the timeout has expired before returning false.
647  typename _Clock::time_point __now = _Clock::now();
648  do {
649  auto __rtime = __atime - __now;
650  if (try_lock_shared_for(__rtime))
651  return true;
652  __now = _Clock::now();
653  } while (__atime > __now);
654  return false;
655  }
656 
657 #else // ! (_GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK)
658 
659  // Exclusive ownership
660 
661  template<typename _Clock, typename _Duration>
662  _GLIBCXX_NODISCARD
663  bool
664  try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time)
665  {
666  unique_lock<mutex> __lk(_M_mut);
667  if (!_M_gate1.wait_until(__lk, __abs_time,
668  [this]{ return !_M_write_entered(); }))
669  {
670  return false;
671  }
672  _M_state |= _S_write_entered;
673  if (!_M_gate2.wait_until(__lk, __abs_time,
674  [this]{ return _M_readers() == 0; }))
675  {
676  _M_state ^= _S_write_entered;
677  // Wake all threads blocked while the write-entered flag was set.
678  _M_gate1.notify_all();
679  return false;
680  }
681  return true;
682  }
683 
684  // Shared ownership
685 
686  template <typename _Clock, typename _Duration>
687  _GLIBCXX_NODISCARD
688  bool
689  try_lock_shared_until(const chrono::time_point<_Clock,
690  _Duration>& __abs_time)
691  {
692  unique_lock<mutex> __lk(_M_mut);
693  if (!_M_gate1.wait_until(__lk, __abs_time,
694  [this]{ return _M_state < _S_max_readers; }))
695  {
696  return false;
697  }
698  ++_M_state;
699  return true;
700  }
701 
702 #endif // _GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK
703  };
704 #endif // _GLIBCXX_HAS_GTHREADS
705 
706  /// shared_lock
707  template<typename _Mutex>
709  {
710  public:
711  typedef _Mutex mutex_type;
712 
713  // Shared locking
714 
715  shared_lock() noexcept : _M_pm(nullptr), _M_owns(false) { }
716 
717  explicit
718  shared_lock(mutex_type& __m)
719  : _M_pm(std::__addressof(__m)), _M_owns(true)
720  { __m.lock_shared(); }
721 
722  shared_lock(mutex_type& __m, defer_lock_t) noexcept
723  : _M_pm(std::__addressof(__m)), _M_owns(false) { }
724 
725  shared_lock(mutex_type& __m, try_to_lock_t)
726  : _M_pm(std::__addressof(__m)), _M_owns(__m.try_lock_shared()) { }
727 
728  shared_lock(mutex_type& __m, adopt_lock_t)
729  : _M_pm(std::__addressof(__m)), _M_owns(true) { }
730 
731  template<typename _Clock, typename _Duration>
732  shared_lock(mutex_type& __m,
733  const chrono::time_point<_Clock, _Duration>& __abs_time)
734  : _M_pm(std::__addressof(__m)),
735  _M_owns(__m.try_lock_shared_until(__abs_time)) { }
736 
737  template<typename _Rep, typename _Period>
738  shared_lock(mutex_type& __m,
739  const chrono::duration<_Rep, _Period>& __rel_time)
740  : _M_pm(std::__addressof(__m)),
741  _M_owns(__m.try_lock_shared_for(__rel_time)) { }
742 
743  ~shared_lock()
744  {
745  if (_M_owns)
746  _M_pm->unlock_shared();
747  }
748 
749  shared_lock(shared_lock const&) = delete;
750  shared_lock& operator=(shared_lock const&) = delete;
751 
752  shared_lock(shared_lock&& __sl) noexcept : shared_lock()
753  { swap(__sl); }
754 
755  shared_lock&
756  operator=(shared_lock&& __sl) noexcept
757  {
758  // _GLIBCXX_RESOLVE_LIB_DEFECTS
759  // 4172. unique_lock self-move-assignment is broken
760  shared_lock(std::move(__sl)).swap(*this);
761  return *this;
762  }
763 
764  void
765  lock()
766  {
767  _M_lockable();
768  _M_pm->lock_shared();
769  _M_owns = true;
770  }
771 
772  _GLIBCXX_NODISCARD
773  bool
774  try_lock()
775  {
776  _M_lockable();
777  return _M_owns = _M_pm->try_lock_shared();
778  }
779 
780  template<typename _Rep, typename _Period>
781  _GLIBCXX_NODISCARD
782  bool
783  try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
784  {
785  _M_lockable();
786  return _M_owns = _M_pm->try_lock_shared_for(__rel_time);
787  }
788 
789  template<typename _Clock, typename _Duration>
790  _GLIBCXX_NODISCARD
791  bool
792  try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time)
793  {
794  _M_lockable();
795  return _M_owns = _M_pm->try_lock_shared_until(__abs_time);
796  }
797 
798  void
799  unlock()
800  {
801  if (!_M_owns)
802  __throw_system_error(int(errc::operation_not_permitted));
803  _M_pm->unlock_shared();
804  _M_owns = false;
805  }
806 
807  // Setters
808 
809  void
810  swap(shared_lock& __u) noexcept
811  {
812  std::swap(_M_pm, __u._M_pm);
813  std::swap(_M_owns, __u._M_owns);
814  }
815 
816  mutex_type*
817  release() noexcept
818  {
819  _M_owns = false;
820  return std::__exchange(_M_pm, nullptr);
821  }
822 
823  // Getters
824 
825  _GLIBCXX_NODISCARD
826  bool owns_lock() const noexcept { return _M_owns; }
827 
828  explicit operator bool() const noexcept { return _M_owns; }
829 
830  _GLIBCXX_NODISCARD
831  mutex_type* mutex() const noexcept { return _M_pm; }
832 
833  private:
834  void
835  _M_lockable() const
836  {
837  if (_M_pm == nullptr)
838  __throw_system_error(int(errc::operation_not_permitted));
839  if (_M_owns)
840  __throw_system_error(int(errc::resource_deadlock_would_occur));
841  }
842 
843  mutex_type* _M_pm;
844  bool _M_owns;
845  };
846 
847  /// Swap specialization for shared_lock
848  /// @relates shared_mutex
849  template<typename _Mutex>
850  void
851  swap(shared_lock<_Mutex>& __x, shared_lock<_Mutex>& __y) noexcept
852  { __x.swap(__y); }
853 
854  /// @} group mutexes
855 _GLIBCXX_END_NAMESPACE_VERSION
856 } // namespace
857 
858 #endif // C++14
859 
860 #endif // _GLIBCXX_SHARED_MUTEX
std::shared_lock
shared_lock
Definition: shared_mutex:708
requires_hosted.h
std::chrono::system_clock
System clock.
Definition: chrono.h:1230
chrono.h
std::ratio_greater
ratio_greater
Definition: ratio:460
error_constants.h
new_throw.h
std::mutex
Definition: std_mutex.h:99
move.h
std::move
constexpr std::remove_reference< _Tp >::type && move(_Tp &&__t) noexcept
Convert a value to an rvalue.
Definition: move.h:138
std::lock
void lock(_L1 &__l1, _L2 &__l2, _L3 &... __l3)
Generic lock.
Definition: mutex:686
std::try_lock
int try_lock(_L1 &__l1, _L2 &__l2, _L3 &... __l3)
Generic try_lock.
Definition: mutex:627
condition_variable
std
ISO C++ entities toplevel namespace is std.
std::try_to_lock_t
Try to acquire ownership of the mutex without blocking.
Definition: std_mutex.h:245
std::chrono::steady_clock
Monotonic clock.
Definition: chrono.h:1272
std::try_to_lock
constexpr try_to_lock_t try_to_lock
Tag used to prevent a scoped lock from blocking if a mutex is locked.
Definition: std_mutex.h:255
std::__addressof
constexpr _Tp * __addressof(_Tp &__r) noexcept
Same as C++11 std::addressof.
Definition: move.h:52
std::shared_timed_mutex
The standard shared timed mutex type.
Definition: shared_mutex:464
std::adopt_lock_t
Assume the calling thread has already obtained mutex ownership and manage it.
Definition: std_mutex.h:249
std::chrono::time_point
chrono::time_point represents a point in time as measured by a clock
Definition: chrono.h:72
std_mutex.h
std::defer_lock_t
Do not acquire ownership of the mutex.
Definition: std_mutex.h:242
functexcept.h
std::chrono::duration
chrono::duration represents a distance between two points in time
Definition: chrono.h:68
version.h
std::unique_lock
A movable scoped lock type.
Definition: unique_lock.h:62