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