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