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 43namespace 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 608