libstdc++
shared_mutex
Go to the documentation of this file.
1 // <shared_mutex> -*- C++ -*-
2 
3 // Copyright (C) 2013-2015 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 #pragma GCC system_header
33 
34 #if __cplusplus <= 201103L
35 # include <bits/c++14_warning.h>
36 #else
37 
38 #include <bits/c++config.h>
39 #include <mutex>
40 #include <condition_variable>
41 #include <bits/functexcept.h>
42 
43 namespace std _GLIBCXX_VISIBILITY(default)
44 {
45 _GLIBCXX_BEGIN_NAMESPACE_VERSION
46 
47  /**
48  * @ingroup mutexes
49  * @{
50  */
51 
52 #ifdef _GLIBCXX_USE_C99_STDINT_TR1
53 #ifdef _GLIBCXX_HAS_GTHREADS
54 
55 #define __cpp_lib_shared_timed_mutex 201402
56 
57  /// shared_timed_mutex
58  class shared_timed_mutex
59  {
60 #if _GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK
61  typedef chrono::system_clock __clock_t;
62 
63 #ifdef PTHREAD_RWLOCK_INITIALIZER
64  pthread_rwlock_t _M_rwlock = PTHREAD_RWLOCK_INITIALIZER;
65 
66  public:
67  shared_timed_mutex() = default;
68  ~shared_timed_mutex() = default;
69 #else
70  pthread_rwlock_t _M_rwlock;
71 
72  public:
73  shared_timed_mutex()
74  {
75  int __ret = pthread_rwlock_init(&_M_rwlock, NULL);
76  if (__ret == ENOMEM)
77  __throw_bad_alloc();
78  else if (__ret == EAGAIN)
79  __throw_system_error(int(errc::resource_unavailable_try_again));
80  else if (__ret == EPERM)
81  __throw_system_error(int(errc::operation_not_permitted));
82  // Errors not handled: EBUSY, EINVAL
83  _GLIBCXX_DEBUG_ASSERT(__ret == 0);
84  }
85 
86  ~shared_timed_mutex()
87  {
88  int __ret __attribute((__unused__)) = pthread_rwlock_destroy(&_M_rwlock);
89  // Errors not handled: EBUSY, EINVAL
90  _GLIBCXX_DEBUG_ASSERT(__ret == 0);
91  }
92 #endif
93 
94  shared_timed_mutex(const shared_timed_mutex&) = delete;
95  shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
96 
97  // Exclusive ownership
98 
99  void
100  lock()
101  {
102  int __ret = pthread_rwlock_wrlock(&_M_rwlock);
103  if (__ret == EDEADLK)
104  __throw_system_error(int(errc::resource_deadlock_would_occur));
105  // Errors not handled: EINVAL
106  _GLIBCXX_DEBUG_ASSERT(__ret == 0);
107  }
108 
109  bool
110  try_lock()
111  {
112  int __ret = pthread_rwlock_trywrlock(&_M_rwlock);
113  if (__ret == EBUSY) return false;
114  // Errors not handled: EINVAL
115  _GLIBCXX_DEBUG_ASSERT(__ret == 0);
116  return true;
117  }
118 
119  template<typename _Rep, typename _Period>
120  bool
121  try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
122  {
123  return try_lock_until(__clock_t::now() + __rel_time);
124  }
125 
126  template<typename _Duration>
127  bool
128  try_lock_until(const chrono::time_point<__clock_t, _Duration>& __atime)
129  {
130  auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
131  auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
132 
133  __gthread_time_t __ts =
134  {
135  static_cast<std::time_t>(__s.time_since_epoch().count()),
136  static_cast<long>(__ns.count())
137  };
138 
139  int __ret = pthread_rwlock_timedwrlock(&_M_rwlock, &__ts);
140  // On self-deadlock, we just fail to acquire the lock. Technically,
141  // the program violated the precondition.
142  if (__ret == ETIMEDOUT || __ret == EDEADLK)
143  return false;
144  // Errors not handled: EINVAL
145  _GLIBCXX_DEBUG_ASSERT(__ret == 0);
146  return true;
147  }
148 
149  template<typename _Clock, typename _Duration>
150  bool
151  try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time)
152  {
153  // DR 887 - Sync unknown clock to known clock.
154  const typename _Clock::time_point __c_entry = _Clock::now();
155  const __clock_t::time_point __s_entry = __clock_t::now();
156  const auto __delta = __abs_time - __c_entry;
157  const auto __s_atime = __s_entry + __delta;
158  return try_lock_until(__s_atime);
159  }
160 
161  void
162  unlock()
163  {
164  int __ret __attribute((__unused__)) = pthread_rwlock_unlock(&_M_rwlock);
165  // Errors not handled: EPERM, EBUSY, EINVAL
166  _GLIBCXX_DEBUG_ASSERT(__ret == 0);
167  }
168 
169  // Shared ownership
170 
171  void
172  lock_shared()
173  {
174  int __ret;
175  // We retry if we exceeded the maximum number of read locks supported by
176  // the POSIX implementation; this can result in busy-waiting, but this
177  // is okay based on the current specification of forward progress
178  // guarantees by the standard.
179  do
180  __ret = pthread_rwlock_rdlock(&_M_rwlock);
181  while (__ret == EAGAIN);
182  if (__ret == EDEADLK)
183  __throw_system_error(int(errc::resource_deadlock_would_occur));
184  // Errors not handled: EINVAL
185  _GLIBCXX_DEBUG_ASSERT(__ret == 0);
186  }
187 
188  bool
189  try_lock_shared()
190  {
191  int __ret = pthread_rwlock_tryrdlock(&_M_rwlock);
192  // If the maximum number of read locks has been exceeded, we just fail
193  // to acquire the lock. Unlike for lock(), we are not allowed to throw
194  // an exception.
195  if (__ret == EBUSY || __ret == EAGAIN) return false;
196  // Errors not handled: EINVAL
197  _GLIBCXX_DEBUG_ASSERT(__ret == 0);
198  return true;
199  }
200 
201  template<typename _Rep, typename _Period>
202  bool
203  try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time)
204  {
205  return try_lock_shared_until(__clock_t::now() + __rel_time);
206  }
207 
208  template<typename _Duration>
209  bool
210  try_lock_shared_until(const chrono::time_point<__clock_t,
211  _Duration>& __atime)
212  {
213  auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
214  auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
215 
216  __gthread_time_t __ts =
217  {
218  static_cast<std::time_t>(__s.time_since_epoch().count()),
219  static_cast<long>(__ns.count())
220  };
221 
222  int __ret;
223  // Unlike for lock(), we are not allowed to throw an exception so if
224  // the maximum number of read locks has been exceeded, or we would
225  // deadlock, we just try to acquire the lock again (and will time out
226  // eventually).
227  // In cases where we would exceed the maximum number of read locks
228  // throughout the whole time until the timeout, we will fail to
229  // acquire the lock even if it would be logically free; however, this
230  // is allowed by the standard, and we made a "strong effort"
231  // (see C++14 30.4.1.4p26).
232  // For cases where the implementation detects a deadlock we
233  // intentionally block and timeout so that an early return isn't
234  // mistaken for a spurious failure, which might help users realise
235  // there is a deadlock.
236  do
237  __ret = pthread_rwlock_timedrdlock(&_M_rwlock, &__ts);
238  while (__ret == EAGAIN || __ret == EDEADLK);
239  if (__ret == ETIMEDOUT)
240  return false;
241  // Errors not handled: EINVAL
242  _GLIBCXX_DEBUG_ASSERT(__ret == 0);
243  return true;
244  }
245 
246  template<typename _Clock, typename _Duration>
247  bool
248  try_lock_shared_until(const chrono::time_point<_Clock,
249  _Duration>& __abs_time)
250  {
251  // DR 887 - Sync unknown clock to known clock.
252  const typename _Clock::time_point __c_entry = _Clock::now();
253  const __clock_t::time_point __s_entry = __clock_t::now();
254  const auto __delta = __abs_time - __c_entry;
255  const auto __s_atime = __s_entry + __delta;
256  return try_lock_shared_until(__s_atime);
257  }
258 
259  void
260  unlock_shared()
261  {
262  unlock();
263  }
264 
265 #else // ! (_GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK)
266 
267  // Must use the same clock as condition_variable
268  typedef chrono::system_clock __clock_t;
269 
270  // Based on Howard Hinnant's reference implementation from N2406.
271 
272  // The high bit of _M_state is the write-entered flag which is set to
273  // indicate a writer has taken the lock or is queuing to take the lock.
274  // The remaining bits are the count of reader locks.
275  //
276  // To take a reader lock, block on gate1 while the write-entered flag is
277  // set or the maximum number of reader locks is held, then increment the
278  // reader lock count.
279  // To release, decrement the count, then if the write-entered flag is set
280  // and the count is zero then signal gate2 to wake a queued writer,
281  // otherwise if the maximum number of reader locks was held signal gate1
282  // to wake a reader.
283  //
284  // To take a writer lock, block on gate1 while the write-entered flag is
285  // set, then set the write-entered flag to start queueing, then block on
286  // gate2 while the number of reader locks is non-zero.
287  // To release, unset the write-entered flag and signal gate1 to wake all
288  // blocked readers and writers.
289  //
290  // This means that when no reader locks are held readers and writers get
291  // equal priority. When one or more reader locks is held a writer gets
292  // priority and no more reader locks can be taken while the writer is
293  // queued.
294 
295  // Only locked when accessing _M_state or waiting on condition variables.
296  mutex _M_mut;
297  // Used to block while write-entered is set or reader count at maximum.
298  condition_variable _M_gate1;
299  // Used to block queued writers while reader count is non-zero.
300  condition_variable _M_gate2;
301  // The write-entered flag and reader count.
302  unsigned _M_state;
303 
304  static constexpr unsigned _S_write_entered
305  = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1);
306  static constexpr unsigned _S_max_readers = ~_S_write_entered;
307 
308  // Test whether the write-entered flag is set. _M_mut must be locked.
309  bool _M_write_entered() const { return _M_state & _S_write_entered; }
310 
311  // The number of reader locks currently held. _M_mut must be locked.
312  unsigned _M_readers() const { return _M_state & _S_max_readers; }
313 
314  public:
315  shared_timed_mutex() : _M_state(0) {}
316 
317  ~shared_timed_mutex()
318  {
319  _GLIBCXX_DEBUG_ASSERT( _M_state == 0 );
320  }
321 
322  shared_timed_mutex(const shared_timed_mutex&) = delete;
323  shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
324 
325  // Exclusive ownership
326 
327  void
328  lock()
329  {
330  unique_lock<mutex> __lk(_M_mut);
331  // Wait until we can set the write-entered flag.
332  _M_gate1.wait(__lk, [=]{ return !_M_write_entered(); });
333  _M_state |= _S_write_entered;
334  // Then wait until there are no more readers.
335  _M_gate2.wait(__lk, [=]{ return _M_readers() == 0; });
336  }
337 
338  bool
339  try_lock()
340  {
341  unique_lock<mutex> __lk(_M_mut, try_to_lock);
342  if (__lk.owns_lock() && _M_state == 0)
343  {
344  _M_state = _S_write_entered;
345  return true;
346  }
347  return false;
348  }
349 
350  template<typename _Rep, typename _Period>
351  bool
352  try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
353  {
354  return try_lock_until(__clock_t::now() + __rel_time);
355  }
356 
357  template<typename _Clock, typename _Duration>
358  bool
359  try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time)
360  {
361  unique_lock<mutex> __lk(_M_mut);
362  if (!_M_gate1.wait_until(__lk, __abs_time,
363  [=]{ return !_M_write_entered(); }))
364  {
365  return false;
366  }
367  _M_state |= _S_write_entered;
368  if (!_M_gate2.wait_until(__lk, __abs_time,
369  [=]{ return _M_readers() == 0; }))
370  {
371  _M_state ^= _S_write_entered;
372  // Wake all threads blocked while the write-entered flag was set.
373  _M_gate1.notify_all();
374  return false;
375  }
376  return true;
377  }
378 
379  void
380  unlock()
381  {
382  lock_guard<mutex> __lk(_M_mut);
383  _GLIBCXX_DEBUG_ASSERT( _M_write_entered() );
384  _M_state = 0;
385  // call notify_all() while mutex is held so that another thread can't
386  // lock and unlock the mutex then destroy *this before we make the call.
387  _M_gate1.notify_all();
388  }
389 
390  // Shared ownership
391 
392  void
393  lock_shared()
394  {
395  unique_lock<mutex> __lk(_M_mut);
396  _M_gate1.wait(__lk, [=]{ return _M_state < _S_max_readers; });
397  ++_M_state;
398  }
399 
400  bool
401  try_lock_shared()
402  {
403  unique_lock<mutex> __lk(_M_mut, try_to_lock);
404  if (!__lk.owns_lock())
405  return false;
406  if (_M_state < _S_max_readers)
407  {
408  ++_M_state;
409  return true;
410  }
411  return false;
412  }
413 
414  template<typename _Rep, typename _Period>
415  bool
416  try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time)
417  {
418  return try_lock_shared_until(__clock_t::now() + __rel_time);
419  }
420 
421  template <typename _Clock, typename _Duration>
422  bool
423  try_lock_shared_until(const chrono::time_point<_Clock,
424  _Duration>& __abs_time)
425  {
426  unique_lock<mutex> __lk(_M_mut);
427  if (!_M_gate1.wait_until(__lk, __abs_time,
428  [=]{ return _M_state < _S_max_readers; }))
429  {
430  return false;
431  }
432  ++_M_state;
433  return true;
434  }
435 
436  void
437  unlock_shared()
438  {
439  lock_guard<mutex> __lk(_M_mut);
440  _GLIBCXX_DEBUG_ASSERT( _M_readers() > 0 );
441  auto __prev = _M_state--;
442  if (_M_write_entered())
443  {
444  // Wake the queued writer if there are no more readers.
445  if (_M_readers() == 0)
446  _M_gate2.notify_one();
447  // No need to notify gate1 because we give priority to the queued
448  // writer, and that writer will eventually notify gate1 after it
449  // clears the write-entered flag.
450  }
451  else
452  {
453  // Wake any thread that was blocked on reader overflow.
454  if (__prev == _S_max_readers)
455  _M_gate1.notify_one();
456  }
457  }
458 #endif // _GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK
459  };
460 #endif // _GLIBCXX_HAS_GTHREADS
461 
462  /// shared_lock
463  template<typename _Mutex>
464  class shared_lock
465  {
466  public:
467  typedef _Mutex mutex_type;
468 
469  // Shared locking
470 
471  shared_lock() noexcept : _M_pm(nullptr), _M_owns(false) { }
472 
473  explicit
474  shared_lock(mutex_type& __m) : _M_pm(&__m), _M_owns(true)
475  { __m.lock_shared(); }
476 
477  shared_lock(mutex_type& __m, defer_lock_t) noexcept
478  : _M_pm(&__m), _M_owns(false) { }
479 
480  shared_lock(mutex_type& __m, try_to_lock_t)
481  : _M_pm(&__m), _M_owns(__m.try_lock_shared()) { }
482 
483  shared_lock(mutex_type& __m, adopt_lock_t)
484  : _M_pm(&__m), _M_owns(true) { }
485 
486  template<typename _Clock, typename _Duration>
487  shared_lock(mutex_type& __m,
488  const chrono::time_point<_Clock, _Duration>& __abs_time)
489  : _M_pm(&__m), _M_owns(__m.try_lock_shared_until(__abs_time)) { }
490 
491  template<typename _Rep, typename _Period>
492  shared_lock(mutex_type& __m,
493  const chrono::duration<_Rep, _Period>& __rel_time)
494  : _M_pm(&__m), _M_owns(__m.try_lock_shared_for(__rel_time)) { }
495 
496  ~shared_lock()
497  {
498  if (_M_owns)
499  _M_pm->unlock_shared();
500  }
501 
502  shared_lock(shared_lock const&) = delete;
503  shared_lock& operator=(shared_lock const&) = delete;
504 
505  shared_lock(shared_lock&& __sl) noexcept : shared_lock()
506  { swap(__sl); }
507 
508  shared_lock&
509  operator=(shared_lock&& __sl) noexcept
510  {
511  shared_lock(std::move(__sl)).swap(*this);
512  return *this;
513  }
514 
515  void
516  lock()
517  {
518  _M_lockable();
519  _M_pm->lock_shared();
520  _M_owns = true;
521  }
522 
523  bool
524  try_lock()
525  {
526  _M_lockable();
527  return _M_owns = _M_pm->try_lock_shared();
528  }
529 
530  template<typename _Rep, typename _Period>
531  bool
532  try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
533  {
534  _M_lockable();
535  return _M_owns = _M_pm->try_lock_shared_for(__rel_time);
536  }
537 
538  template<typename _Clock, typename _Duration>
539  bool
540  try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time)
541  {
542  _M_lockable();
543  return _M_owns = _M_pm->try_lock_shared_until(__abs_time);
544  }
545 
546  void
547  unlock()
548  {
549  if (!_M_owns)
550  __throw_system_error(int(errc::resource_deadlock_would_occur));
551  _M_pm->unlock_shared();
552  _M_owns = false;
553  }
554 
555  // Setters
556 
557  void
558  swap(shared_lock& __u) noexcept
559  {
560  std::swap(_M_pm, __u._M_pm);
561  std::swap(_M_owns, __u._M_owns);
562  }
563 
564  mutex_type*
565  release() noexcept
566  {
567  _M_owns = false;
568  return std::exchange(_M_pm, nullptr);
569  }
570 
571  // Getters
572 
573  bool owns_lock() const noexcept { return _M_owns; }
574 
575  explicit operator bool() const noexcept { return _M_owns; }
576 
577  mutex_type* mutex() const noexcept { return _M_pm; }
578 
579  private:
580  void
581  _M_lockable() const
582  {
583  if (_M_pm == nullptr)
584  __throw_system_error(int(errc::operation_not_permitted));
585  if (_M_owns)
586  __throw_system_error(int(errc::resource_deadlock_would_occur));
587  }
588 
589  mutex_type* _M_pm;
590  bool _M_owns;
591  };
592 
593  /// Swap specialization for shared_lock
594  template<typename _Mutex>
595  void
596  swap(shared_lock<_Mutex>& __x, shared_lock<_Mutex>& __y) noexcept
597  { __x.swap(__y); }
598 
599 #endif // _GLIBCXX_USE_C99_STDINT_TR1
600 
601  // @} group mutexes
602 _GLIBCXX_END_NAMESPACE_VERSION
603 } // namespace
604 
605 #endif // C++14
606 
607 #endif // _GLIBCXX_SHARED_MUTEX