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