1 #ifndef BOOST_THREAD_PTHREAD_SHARED_MUTEX_HPP 2 #define BOOST_THREAD_PTHREAD_SHARED_MUTEX_HPP 3 4 // (C) Copyright 2006-8 Anthony Williams 5 // (C) Copyright 2012 Vicente J. Botet Escriba 6 // 7 // Distributed under the Boost Software License, Version 1.0. (See 8 // accompanying file LICENSE_1_0.txt or copy at 9 // http://www.boost.org/LICENSE_1_0.txt) 10 11 #include <boost/assert.hpp> 12 #include <boost/bind/bind.hpp> 13 #include <boost/static_assert.hpp> 14 #include <boost/thread/mutex.hpp> 15 #include <boost/thread/condition_variable.hpp> 16 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 17 #include <boost/thread/detail/thread_interruption.hpp> 18 #endif 19 #ifdef BOOST_THREAD_USES_CHRONO 20 #include <boost/chrono/system_clocks.hpp> 21 #include <boost/chrono/ceil.hpp> 22 #endif 23 #include <boost/thread/detail/delete.hpp> 24 25 #include <boost/config/abi_prefix.hpp> 26 27 namespace boost 28 { 29 class shared_mutex 30 { 31 private: 32 class state_data 33 { 34 public: state_data()35 state_data () : 36 shared_count(0), 37 exclusive(false), 38 upgrade(false), 39 exclusive_waiting_blocked(false) 40 {} 41 assert_free() const42 void assert_free() const 43 { 44 BOOST_ASSERT( ! exclusive ); 45 BOOST_ASSERT( ! upgrade ); 46 BOOST_ASSERT( shared_count==0 ); 47 } 48 assert_locked() const49 void assert_locked() const 50 { 51 BOOST_ASSERT( exclusive ); 52 BOOST_ASSERT( shared_count==0 ); 53 BOOST_ASSERT( ! upgrade ); 54 } 55 assert_lock_shared() const56 void assert_lock_shared () const 57 { 58 BOOST_ASSERT( ! exclusive ); 59 BOOST_ASSERT( shared_count>0 ); 60 //BOOST_ASSERT( (! upgrade) || (shared_count>1)); 61 // if upgraded there are at least 2 threads sharing the mutex, 62 // except when unlock_upgrade_and_lock has decreased the number of readers but has not taken yet exclusive ownership. 63 } 64 assert_lock_upgraded() const65 void assert_lock_upgraded () const 66 { 67 BOOST_ASSERT( ! exclusive ); 68 BOOST_ASSERT( upgrade ); 69 BOOST_ASSERT( shared_count>0 ); 70 } 71 assert_lock_not_upgraded() const72 void assert_lock_not_upgraded () const 73 { 74 BOOST_ASSERT( ! upgrade ); 75 } 76 can_lock() const77 bool can_lock () const 78 { 79 return ! (shared_count || exclusive); 80 } 81 lock()82 void lock () 83 { 84 exclusive = true; 85 } 86 unlock()87 void unlock () 88 { 89 exclusive = false; 90 exclusive_waiting_blocked = false; 91 } 92 can_lock_shared() const93 bool can_lock_shared () const 94 { 95 return ! (exclusive || exclusive_waiting_blocked); 96 } 97 no_shared() const98 bool no_shared () const 99 { 100 return shared_count==0; 101 } 102 one_shared() const103 bool one_shared () const 104 { 105 return shared_count==1; 106 } 107 lock_shared()108 void lock_shared () 109 { 110 ++shared_count; 111 } 112 113 unlock_shared()114 void unlock_shared () 115 { 116 --shared_count; 117 } 118 lock_upgrade()119 void lock_upgrade () 120 { 121 ++shared_count; 122 upgrade=true; 123 } can_lock_upgrade() const124 bool can_lock_upgrade () const 125 { 126 return ! (exclusive || exclusive_waiting_blocked || upgrade); 127 } 128 unlock_upgrade()129 void unlock_upgrade () 130 { 131 upgrade=false; 132 --shared_count; 133 } 134 135 //private: 136 unsigned shared_count; 137 bool exclusive; 138 bool upgrade; 139 bool exclusive_waiting_blocked; 140 }; 141 142 143 144 state_data state; 145 boost::mutex state_change; 146 boost::condition_variable shared_cond; 147 boost::condition_variable exclusive_cond; 148 boost::condition_variable upgrade_cond; 149 release_waiters()150 void release_waiters() 151 { 152 exclusive_cond.notify_one(); 153 shared_cond.notify_all(); 154 } 155 156 public: 157 158 BOOST_THREAD_NO_COPYABLE(shared_mutex) 159 shared_mutex()160 shared_mutex() 161 { 162 } 163 ~shared_mutex()164 ~shared_mutex() 165 { 166 } 167 lock_shared()168 void lock_shared() 169 { 170 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 171 boost::this_thread::disable_interruption do_not_disturb; 172 #endif 173 boost::unique_lock<boost::mutex> lk(state_change); 174 shared_cond.wait(lk, boost::bind(&state_data::can_lock_shared, boost::ref(state))); 175 state.lock_shared(); 176 } 177 try_lock_shared()178 bool try_lock_shared() 179 { 180 boost::unique_lock<boost::mutex> lk(state_change); 181 182 if(!state.can_lock_shared()) 183 { 184 return false; 185 } 186 state.lock_shared(); 187 return true; 188 } 189 190 #if defined BOOST_THREAD_USES_DATETIME timed_lock_shared(system_time const & timeout)191 bool timed_lock_shared(system_time const& timeout) 192 { 193 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 194 boost::this_thread::disable_interruption do_not_disturb; 195 #endif 196 boost::unique_lock<boost::mutex> lk(state_change); 197 if(!shared_cond.timed_wait(lk, timeout, boost::bind(&state_data::can_lock_shared, boost::ref(state)))) 198 { 199 return false; 200 } 201 state.lock_shared(); 202 return true; 203 } 204 205 template<typename TimeDuration> timed_lock_shared(TimeDuration const & relative_time)206 bool timed_lock_shared(TimeDuration const & relative_time) 207 { 208 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 209 boost::this_thread::disable_interruption do_not_disturb; 210 #endif 211 boost::unique_lock<boost::mutex> lk(state_change); 212 if(!shared_cond.timed_wait(lk, relative_time, boost::bind(&state_data::can_lock_shared, boost::ref(state)))) 213 { 214 return false; 215 } 216 state.lock_shared(); 217 return true; 218 } 219 #endif 220 #ifdef BOOST_THREAD_USES_CHRONO 221 template <class Rep, class Period> try_lock_shared_for(const chrono::duration<Rep,Period> & rel_time)222 bool try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time) 223 { 224 return try_lock_shared_until(chrono::steady_clock::now() + rel_time); 225 } 226 template <class Clock, class Duration> try_lock_shared_until(const chrono::time_point<Clock,Duration> & abs_time)227 bool try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time) 228 { 229 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 230 boost::this_thread::disable_interruption do_not_disturb; 231 #endif 232 boost::unique_lock<boost::mutex> lk(state_change); 233 if(!shared_cond.wait_until(lk, abs_time, boost::bind(&state_data::can_lock_shared, boost::ref(state)))) 234 { 235 return false; 236 } 237 state.lock_shared(); 238 return true; 239 } 240 #endif unlock_shared()241 void unlock_shared() 242 { 243 boost::unique_lock<boost::mutex> lk(state_change); 244 state.assert_lock_shared(); 245 state.unlock_shared(); 246 if (state.no_shared()) 247 { 248 if (state.upgrade) 249 { 250 // As there is a thread doing a unlock_upgrade_and_lock that is waiting for state.no_shared() 251 // avoid other threads to lock, lock_upgrade or lock_shared, so only this thread is notified. 252 state.upgrade=false; 253 state.exclusive=true; 254 //lk.unlock(); 255 upgrade_cond.notify_one(); 256 } 257 else 258 { 259 state.exclusive_waiting_blocked=false; 260 //lk.unlock(); 261 } 262 release_waiters(); 263 } 264 } 265 lock()266 void lock() 267 { 268 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 269 boost::this_thread::disable_interruption do_not_disturb; 270 #endif 271 boost::unique_lock<boost::mutex> lk(state_change); 272 state.exclusive_waiting_blocked=true; 273 exclusive_cond.wait(lk, boost::bind(&state_data::can_lock, boost::ref(state))); 274 state.exclusive=true; 275 } 276 277 #if defined BOOST_THREAD_USES_DATETIME timed_lock(system_time const & timeout)278 bool timed_lock(system_time const& timeout) 279 { 280 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 281 boost::this_thread::disable_interruption do_not_disturb; 282 #endif 283 boost::unique_lock<boost::mutex> lk(state_change); 284 state.exclusive_waiting_blocked=true; 285 if(!exclusive_cond.timed_wait(lk, timeout, boost::bind(&state_data::can_lock, boost::ref(state)))) 286 { 287 state.exclusive_waiting_blocked=false; 288 release_waiters(); 289 return false; 290 } 291 state.exclusive=true; 292 return true; 293 } 294 295 template<typename TimeDuration> timed_lock(TimeDuration const & relative_time)296 bool timed_lock(TimeDuration const & relative_time) 297 { 298 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 299 boost::this_thread::disable_interruption do_not_disturb; 300 #endif 301 boost::unique_lock<boost::mutex> lk(state_change); 302 state.exclusive_waiting_blocked=true; 303 if(!exclusive_cond.timed_wait(lk, relative_time, boost::bind(&state_data::can_lock, boost::ref(state)))) 304 { 305 state.exclusive_waiting_blocked=false; 306 release_waiters(); 307 return false; 308 } 309 state.exclusive=true; 310 return true; 311 } 312 #endif 313 #ifdef BOOST_THREAD_USES_CHRONO 314 template <class Rep, class Period> try_lock_for(const chrono::duration<Rep,Period> & rel_time)315 bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) 316 { 317 return try_lock_until(chrono::steady_clock::now() + rel_time); 318 } 319 template <class Clock, class Duration> try_lock_until(const chrono::time_point<Clock,Duration> & abs_time)320 bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time) 321 { 322 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 323 boost::this_thread::disable_interruption do_not_disturb; 324 #endif 325 boost::unique_lock<boost::mutex> lk(state_change); 326 state.exclusive_waiting_blocked=true; 327 if(!exclusive_cond.wait_until(lk, abs_time, boost::bind(&state_data::can_lock, boost::ref(state)))) 328 { 329 state.exclusive_waiting_blocked=false; 330 release_waiters(); 331 return false; 332 } 333 state.exclusive=true; 334 return true; 335 } 336 #endif 337 try_lock()338 bool try_lock() 339 { 340 boost::unique_lock<boost::mutex> lk(state_change); 341 if(!state.can_lock()) 342 { 343 return false; 344 } 345 state.exclusive=true; 346 return true; 347 } 348 unlock()349 void unlock() 350 { 351 boost::unique_lock<boost::mutex> lk(state_change); 352 state.assert_locked(); 353 state.exclusive=false; 354 state.exclusive_waiting_blocked=false; 355 state.assert_free(); 356 release_waiters(); 357 } 358 lock_upgrade()359 void lock_upgrade() 360 { 361 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 362 boost::this_thread::disable_interruption do_not_disturb; 363 #endif 364 boost::unique_lock<boost::mutex> lk(state_change); 365 shared_cond.wait(lk, boost::bind(&state_data::can_lock_upgrade, boost::ref(state))); 366 state.lock_shared(); 367 state.upgrade=true; 368 } 369 370 #if defined BOOST_THREAD_USES_DATETIME timed_lock_upgrade(system_time const & timeout)371 bool timed_lock_upgrade(system_time const& timeout) 372 { 373 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 374 boost::this_thread::disable_interruption do_not_disturb; 375 #endif 376 boost::unique_lock<boost::mutex> lk(state_change); 377 if(!shared_cond.timed_wait(lk, timeout, boost::bind(&state_data::can_lock_upgrade, boost::ref(state)))) 378 { 379 return false; 380 } 381 state.lock_shared(); 382 state.upgrade=true; 383 return true; 384 } 385 386 template<typename TimeDuration> timed_lock_upgrade(TimeDuration const & relative_time)387 bool timed_lock_upgrade(TimeDuration const & relative_time) 388 { 389 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 390 boost::this_thread::disable_interruption do_not_disturb; 391 #endif 392 boost::unique_lock<boost::mutex> lk(state_change); 393 if(!shared_cond.timed_wait(lk, relative_time, boost::bind(&state_data::can_lock_upgrade, boost::ref(state)))) 394 { 395 return false; 396 } 397 state.lock_shared(); 398 state.upgrade=true; 399 return true; 400 } 401 #endif 402 #ifdef BOOST_THREAD_USES_CHRONO 403 template <class Rep, class Period> try_lock_upgrade_for(const chrono::duration<Rep,Period> & rel_time)404 bool try_lock_upgrade_for(const chrono::duration<Rep, Period>& rel_time) 405 { 406 return try_lock_upgrade_until(chrono::steady_clock::now() + rel_time); 407 } 408 template <class Clock, class Duration> try_lock_upgrade_until(const chrono::time_point<Clock,Duration> & abs_time)409 bool try_lock_upgrade_until(const chrono::time_point<Clock, Duration>& abs_time) 410 { 411 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 412 boost::this_thread::disable_interruption do_not_disturb; 413 #endif 414 boost::unique_lock<boost::mutex> lk(state_change); 415 if(!shared_cond.wait_until(lk, abs_time, boost::bind(&state_data::can_lock_upgrade, boost::ref(state)))) 416 { 417 return false; 418 } 419 state.lock_shared(); 420 state.upgrade=true; 421 return true; 422 } 423 #endif try_lock_upgrade()424 bool try_lock_upgrade() 425 { 426 boost::unique_lock<boost::mutex> lk(state_change); 427 if(!state.can_lock_upgrade()) 428 { 429 return false; 430 } 431 state.lock_shared(); 432 state.upgrade=true; 433 state.assert_lock_upgraded(); 434 return true; 435 } 436 unlock_upgrade()437 void unlock_upgrade() 438 { 439 boost::unique_lock<boost::mutex> lk(state_change); 440 //state.upgrade=false; 441 state.unlock_upgrade(); 442 if(state.no_shared()) 443 { 444 state.exclusive_waiting_blocked=false; 445 release_waiters(); 446 } else { 447 shared_cond.notify_all(); 448 } 449 } 450 451 // Upgrade <-> Exclusive unlock_upgrade_and_lock()452 void unlock_upgrade_and_lock() 453 { 454 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 455 boost::this_thread::disable_interruption do_not_disturb; 456 #endif 457 boost::unique_lock<boost::mutex> lk(state_change); 458 state.assert_lock_upgraded(); 459 state.unlock_shared(); 460 upgrade_cond.wait(lk, boost::bind(&state_data::no_shared, boost::ref(state))); 461 state.upgrade=false; 462 state.exclusive=true; 463 state.assert_locked(); 464 } 465 unlock_and_lock_upgrade()466 void unlock_and_lock_upgrade() 467 { 468 boost::unique_lock<boost::mutex> lk(state_change); 469 state.assert_locked(); 470 state.exclusive=false; 471 state.upgrade=true; 472 state.lock_shared(); 473 state.exclusive_waiting_blocked=false; 474 state.assert_lock_upgraded(); 475 release_waiters(); 476 } 477 try_unlock_upgrade_and_lock()478 bool try_unlock_upgrade_and_lock() 479 { 480 boost::unique_lock<boost::mutex> lk(state_change); 481 state.assert_lock_upgraded(); 482 if( !state.exclusive 483 && !state.exclusive_waiting_blocked 484 && state.upgrade 485 && state.shared_count==1) 486 { 487 state.shared_count=0; 488 state.exclusive=true; 489 state.upgrade=false; 490 state.assert_locked(); 491 return true; 492 } 493 return false; 494 } 495 #ifdef BOOST_THREAD_USES_CHRONO 496 template <class Rep, class Period> 497 bool try_unlock_upgrade_and_lock_for(const chrono::duration<Rep,Period> & rel_time)498 try_unlock_upgrade_and_lock_for( 499 const chrono::duration<Rep, Period>& rel_time) 500 { 501 return try_unlock_upgrade_and_lock_until( 502 chrono::steady_clock::now() + rel_time); 503 } 504 template <class Clock, class Duration> 505 bool try_unlock_upgrade_and_lock_until(const chrono::time_point<Clock,Duration> & abs_time)506 try_unlock_upgrade_and_lock_until( 507 const chrono::time_point<Clock, Duration>& abs_time) 508 { 509 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 510 boost::this_thread::disable_interruption do_not_disturb; 511 #endif 512 boost::unique_lock<boost::mutex> lk(state_change); 513 state.assert_lock_upgraded(); 514 if(!shared_cond.wait_until(lk, abs_time, boost::bind(&state_data::one_shared, boost::ref(state)))) 515 { 516 return false; 517 } 518 state.upgrade=false; 519 state.exclusive=true; 520 state.exclusive_waiting_blocked=false; 521 state.shared_count=0; 522 return true; 523 } 524 #endif 525 526 // Shared <-> Exclusive unlock_and_lock_shared()527 void unlock_and_lock_shared() 528 { 529 boost::unique_lock<boost::mutex> lk(state_change); 530 state.assert_locked(); 531 state.exclusive=false; 532 state.lock_shared(); 533 state.exclusive_waiting_blocked=false; 534 release_waiters(); 535 } 536 537 #ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS try_unlock_shared_and_lock()538 bool try_unlock_shared_and_lock() 539 { 540 boost::unique_lock<boost::mutex> lk(state_change); 541 state.assert_lock_shared(); 542 if( !state.exclusive 543 && !state.exclusive_waiting_blocked 544 && !state.upgrade 545 && state.shared_count==1) 546 { 547 state.shared_count=0; 548 state.exclusive=true; 549 return true; 550 } 551 return false; 552 } 553 #ifdef BOOST_THREAD_USES_CHRONO 554 template <class Rep, class Period> 555 bool try_unlock_shared_and_lock_for(const chrono::duration<Rep,Period> & rel_time)556 try_unlock_shared_and_lock_for( 557 const chrono::duration<Rep, Period>& rel_time) 558 { 559 return try_unlock_shared_and_lock_until( 560 chrono::steady_clock::now() + rel_time); 561 } 562 template <class Clock, class Duration> 563 bool try_unlock_shared_and_lock_until(const chrono::time_point<Clock,Duration> & abs_time)564 try_unlock_shared_and_lock_until( 565 const chrono::time_point<Clock, Duration>& abs_time) 566 { 567 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 568 boost::this_thread::disable_interruption do_not_disturb; 569 #endif 570 boost::unique_lock<boost::mutex> lk(state_change); 571 state.assert_lock_shared(); 572 if(!shared_cond.wait_until(lk, abs_time, boost::bind(&state_data::one_shared, boost::ref(state)))) 573 { 574 return false; 575 } 576 state.upgrade=false; 577 state.exclusive=true; 578 state.exclusive_waiting_blocked=false; 579 state.shared_count=0; 580 return true; 581 } 582 #endif 583 #endif 584 585 // Shared <-> Upgrade unlock_upgrade_and_lock_shared()586 void unlock_upgrade_and_lock_shared() 587 { 588 boost::unique_lock<boost::mutex> lk(state_change); 589 state.assert_lock_upgraded(); 590 state.upgrade=false; 591 state.exclusive_waiting_blocked=false; 592 release_waiters(); 593 } 594 595 #ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS try_unlock_shared_and_lock_upgrade()596 bool try_unlock_shared_and_lock_upgrade() 597 { 598 boost::unique_lock<boost::mutex> lk(state_change); 599 state.assert_lock_shared(); 600 if(state.can_lock_upgrade()) 601 { 602 state.upgrade=true; 603 return true; 604 } 605 return false; 606 } 607 #ifdef BOOST_THREAD_USES_CHRONO 608 template <class Rep, class Period> 609 bool try_unlock_shared_and_lock_upgrade_for(const chrono::duration<Rep,Period> & rel_time)610 try_unlock_shared_and_lock_upgrade_for( 611 const chrono::duration<Rep, Period>& rel_time) 612 { 613 return try_unlock_shared_and_lock_upgrade_until( 614 chrono::steady_clock::now() + rel_time); 615 } 616 template <class Clock, class Duration> 617 bool try_unlock_shared_and_lock_upgrade_until(const chrono::time_point<Clock,Duration> & abs_time)618 try_unlock_shared_and_lock_upgrade_until( 619 const chrono::time_point<Clock, Duration>& abs_time) 620 { 621 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 622 boost::this_thread::disable_interruption do_not_disturb; 623 #endif 624 boost::unique_lock<boost::mutex> lk(state_change); 625 state.assert_lock_shared(); 626 if(!exclusive_cond.wait_until(lk, abs_time, boost::bind(&state_data::can_lock_upgrade, boost::ref(state)))) 627 { 628 return false; 629 } 630 state.upgrade=true; 631 return true; 632 } 633 #endif 634 #endif 635 }; 636 637 typedef shared_mutex upgrade_mutex; 638 } 639 640 #include <boost/config/abi_suffix.hpp> 641 642 #endif 643