├── main.cpp ├── upgrade_mutex.cpp └── upgrade_mutex.h /main.cpp: -------------------------------------------------------------------------------- 1 | //---------------------------- upgrade_mutex.h --------------------------------- 2 | // 3 | // This software is in the public domain. The only restriction on its use is 4 | // that no one can remove it from the public domain by claiming ownership of it, 5 | // including the original authors. 6 | // 7 | // There is no warranty of correctness on the software contained herein. Use 8 | // at your own risk. 9 | // 10 | //------------------------------------------------------------------------------ 11 | 12 | #include "upgrade_mutex.h" 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | enum {reading, writing}; 19 | int state = reading; 20 | 21 | void sub_print() {} 22 | 23 | template 24 | void 25 | sub_print(const A0& a0, const Args& ...args) 26 | { 27 | std::cout << a0; 28 | sub_print(args...); 29 | } 30 | 31 | std::mutex& 32 | cout_mut() 33 | { 34 | static std::mutex m; 35 | return m; 36 | } 37 | 38 | template 39 | void 40 | print(const Args& ...args) 41 | { 42 | std::lock_guard _(cout_mut()); 43 | sub_print(args...); 44 | } 45 | 46 | namespace S 47 | { 48 | 49 | std::shared_timed_mutex mut; 50 | 51 | void reader() 52 | { 53 | typedef std::chrono::steady_clock Clock; 54 | unsigned count = 0; 55 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 56 | while (Clock::now() < until) 57 | { 58 | mut.lock_shared(); 59 | assert(state == reading); 60 | ++count; 61 | mut.unlock_shared(); 62 | } 63 | print("reader = ", count, '\n'); 64 | } 65 | 66 | void writer() 67 | { 68 | typedef std::chrono::steady_clock Clock; 69 | unsigned count = 0; 70 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 71 | while (Clock::now() < until) 72 | { 73 | mut.lock(); 74 | state = writing; 75 | assert(state == writing); 76 | state = reading; 77 | ++count; 78 | mut.unlock(); 79 | } 80 | print("writer = ", count, '\n'); 81 | } 82 | 83 | void try_reader() 84 | { 85 | typedef std::chrono::steady_clock Clock; 86 | unsigned count = 0; 87 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 88 | while (Clock::now() < until) 89 | { 90 | if (mut.try_lock_shared()) 91 | { 92 | assert(state == reading); 93 | ++count; 94 | mut.unlock_shared(); 95 | } 96 | } 97 | print("try_reader = ", count, '\n'); 98 | } 99 | 100 | void try_writer() 101 | { 102 | typedef std::chrono::steady_clock Clock; 103 | unsigned count = 0; 104 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 105 | while (Clock::now() < until) 106 | { 107 | if (mut.try_lock()) 108 | { 109 | state = writing; 110 | assert(state == writing); 111 | state = reading; 112 | ++count; 113 | mut.unlock(); 114 | } 115 | } 116 | print("try_writer = ", count, '\n'); 117 | } 118 | 119 | void try_for_reader() 120 | { 121 | typedef std::chrono::steady_clock Clock; 122 | unsigned count = 0; 123 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 124 | while (Clock::now() < until) 125 | { 126 | if (mut.try_lock_shared_for(std::chrono::microseconds(5))) 127 | { 128 | assert(state == reading); 129 | ++count; 130 | mut.unlock_shared(); 131 | } 132 | } 133 | print("try_for_reader = ", count, '\n'); 134 | } 135 | 136 | void try_for_writer() 137 | { 138 | typedef std::chrono::steady_clock Clock; 139 | unsigned count = 0; 140 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 141 | while (Clock::now() < until) 142 | { 143 | if (mut.try_lock_for(std::chrono::microseconds(5))) 144 | { 145 | state = writing; 146 | assert(state == writing); 147 | state = reading; 148 | ++count; 149 | mut.unlock(); 150 | } 151 | } 152 | print("try_for_writer = ", count, '\n'); 153 | } 154 | 155 | void 156 | test_shared_mutex() 157 | { 158 | { 159 | std::thread t1(reader); 160 | std::thread t2(writer); 161 | std::thread t3(reader); 162 | t1.join(); 163 | t2.join(); 164 | t3.join(); 165 | } 166 | { 167 | std::thread t1(try_reader); 168 | std::thread t2(try_writer); 169 | std::thread t3(try_reader); 170 | t1.join(); 171 | t2.join(); 172 | t3.join(); 173 | } 174 | { 175 | std::thread t1(try_for_reader); 176 | std::thread t2(try_for_writer); 177 | std::thread t3(try_for_reader); 178 | t1.join(); 179 | t2.join(); 180 | t3.join(); 181 | } 182 | } 183 | 184 | } 185 | 186 | namespace U 187 | { 188 | 189 | acme::upgrade_mutex mut; 190 | 191 | void reader() 192 | { 193 | typedef std::chrono::steady_clock Clock; 194 | unsigned count = 0; 195 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 196 | while (Clock::now() < until) 197 | { 198 | mut.lock_shared(); 199 | assert(state == reading); 200 | ++count; 201 | mut.unlock_shared(); 202 | } 203 | print("reader = ", count, '\n'); 204 | } 205 | 206 | void writer() 207 | { 208 | typedef std::chrono::steady_clock Clock; 209 | unsigned count = 0; 210 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 211 | while (Clock::now() < until) 212 | { 213 | mut.lock(); 214 | state = writing; 215 | assert(state == writing); 216 | state = reading; 217 | ++count; 218 | mut.unlock(); 219 | } 220 | print("writer = ", count, '\n'); 221 | } 222 | 223 | void try_reader() 224 | { 225 | typedef std::chrono::steady_clock Clock; 226 | unsigned count = 0; 227 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 228 | while (Clock::now() < until) 229 | { 230 | if (mut.try_lock_shared()) 231 | { 232 | assert(state == reading); 233 | ++count; 234 | mut.unlock_shared(); 235 | } 236 | } 237 | print("try_reader = ", count, '\n'); 238 | } 239 | 240 | void try_writer() 241 | { 242 | typedef std::chrono::steady_clock Clock; 243 | unsigned count = 0; 244 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 245 | while (Clock::now() < until) 246 | { 247 | if (mut.try_lock()) 248 | { 249 | state = writing; 250 | assert(state == writing); 251 | state = reading; 252 | ++count; 253 | mut.unlock(); 254 | } 255 | } 256 | print("try_writer = ", count, '\n'); 257 | } 258 | 259 | void try_for_reader() 260 | { 261 | typedef std::chrono::steady_clock Clock; 262 | unsigned count = 0; 263 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 264 | while (Clock::now() < until) 265 | { 266 | if (mut.try_lock_shared_for(std::chrono::microseconds(5))) 267 | { 268 | assert(state == reading); 269 | ++count; 270 | mut.unlock_shared(); 271 | } 272 | } 273 | print("try_for_reader = ", count, '\n'); 274 | } 275 | 276 | void try_for_writer() 277 | { 278 | typedef std::chrono::steady_clock Clock; 279 | unsigned count = 0; 280 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 281 | while (Clock::now() < until) 282 | { 283 | if (mut.try_lock_for(std::chrono::microseconds(5))) 284 | { 285 | state = writing; 286 | assert(state == writing); 287 | state = reading; 288 | ++count; 289 | mut.unlock(); 290 | } 291 | } 292 | print("try_for_writer = ", count, '\n'); 293 | } 294 | 295 | void upgradable() 296 | { 297 | typedef std::chrono::steady_clock Clock; 298 | unsigned count = 0; 299 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 300 | while (Clock::now() < until) 301 | { 302 | mut.lock_upgrade(); 303 | assert(state == reading); 304 | ++count; 305 | mut.unlock_upgrade(); 306 | } 307 | print("upgradable = ", count, '\n'); 308 | } 309 | 310 | void try_upgradable() 311 | { 312 | typedef std::chrono::steady_clock Clock; 313 | unsigned count = 0; 314 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 315 | while (Clock::now() < until) 316 | { 317 | if (mut.try_lock_upgrade()) 318 | { 319 | assert(state == reading); 320 | ++count; 321 | mut.unlock_upgrade(); 322 | } 323 | } 324 | print("try_upgradable = ", count, '\n'); 325 | } 326 | 327 | void try_for_upgradable() 328 | { 329 | typedef std::chrono::steady_clock Clock; 330 | unsigned count = 0; 331 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 332 | while (Clock::now() < until) 333 | { 334 | if (mut.try_lock_upgrade_for(std::chrono::microseconds(5))) 335 | { 336 | assert(state == reading); 337 | ++count; 338 | mut.unlock_upgrade(); 339 | } 340 | } 341 | print("try_for_upgradable = ", count, '\n'); 342 | } 343 | 344 | void clockwise() 345 | { 346 | typedef std::chrono::steady_clock Clock; 347 | unsigned count = 0; 348 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 349 | while (Clock::now() < until) 350 | { 351 | mut.lock_shared(); 352 | assert(state == reading); 353 | if (mut.try_unlock_shared_and_lock()) 354 | { 355 | state = writing; 356 | } 357 | else if (mut.try_unlock_shared_and_lock_upgrade()) 358 | { 359 | assert(state == reading); 360 | mut.unlock_upgrade_and_lock(); 361 | state = writing; 362 | } 363 | else 364 | { 365 | mut.unlock_shared(); 366 | continue; 367 | } 368 | assert(state == writing); 369 | state = reading; 370 | mut.unlock_and_lock_upgrade(); 371 | assert(state == reading); 372 | mut.unlock_upgrade_and_lock_shared(); 373 | assert(state == reading); 374 | mut.unlock_shared(); 375 | ++count; 376 | } 377 | print("clockwise = ", count, '\n'); 378 | } 379 | 380 | void counter_clockwise() 381 | { 382 | typedef std::chrono::steady_clock Clock; 383 | unsigned count = 0; 384 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 385 | while (Clock::now() < until) 386 | { 387 | mut.lock_upgrade(); 388 | assert(state == reading); 389 | mut.unlock_upgrade_and_lock(); 390 | assert(state == reading); 391 | state = writing; 392 | assert(state == writing); 393 | state = reading; 394 | mut.unlock_and_lock_shared(); 395 | assert(state == reading); 396 | mut.unlock_shared(); 397 | ++count; 398 | } 399 | print("counter_clockwise = ", count, '\n'); 400 | } 401 | 402 | void try_clockwise() 403 | { 404 | typedef std::chrono::steady_clock Clock; 405 | unsigned count = 0; 406 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 407 | while (Clock::now() < until) 408 | { 409 | if (mut.try_lock_shared()) 410 | { 411 | assert(state == reading); 412 | if (mut.try_unlock_shared_and_lock()) 413 | { 414 | state = writing; 415 | } 416 | else if (mut.try_unlock_shared_and_lock_upgrade()) 417 | { 418 | assert(state == reading); 419 | mut.unlock_upgrade_and_lock(); 420 | state = writing; 421 | } 422 | else 423 | { 424 | mut.unlock_shared(); 425 | continue; 426 | } 427 | assert(state == writing); 428 | state = reading; 429 | mut.unlock_and_lock_upgrade(); 430 | assert(state == reading); 431 | mut.unlock_upgrade_and_lock_shared(); 432 | assert(state == reading); 433 | mut.unlock_shared(); 434 | ++count; 435 | } 436 | } 437 | print("try_clockwise = ", count, '\n'); 438 | } 439 | 440 | void try_for_clockwise() 441 | { 442 | typedef std::chrono::steady_clock Clock; 443 | unsigned count = 0; 444 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 445 | while (Clock::now() < until) 446 | { 447 | if (mut.try_lock_shared_for(std::chrono::microseconds(5))) 448 | { 449 | assert(state == reading); 450 | if (mut.try_unlock_shared_and_lock_for(std::chrono::microseconds(5))) 451 | { 452 | state = writing; 453 | } 454 | else if (mut.try_unlock_shared_and_lock_upgrade_for(std::chrono::microseconds(5))) 455 | { 456 | assert(state == reading); 457 | mut.unlock_upgrade_and_lock(); 458 | state = writing; 459 | } 460 | else 461 | { 462 | mut.unlock_shared(); 463 | continue; 464 | } 465 | assert(state == writing); 466 | state = reading; 467 | mut.unlock_and_lock_upgrade(); 468 | assert(state == reading); 469 | mut.unlock_upgrade_and_lock_shared(); 470 | assert(state == reading); 471 | mut.unlock_shared(); 472 | ++count; 473 | } 474 | } 475 | print("try_for_clockwise = ", count, '\n'); 476 | } 477 | 478 | void try_counter_clockwise() 479 | { 480 | typedef std::chrono::steady_clock Clock; 481 | unsigned count = 0; 482 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 483 | while (Clock::now() < until) 484 | { 485 | if (mut.try_lock_upgrade()) 486 | { 487 | assert(state == reading); 488 | if (mut.try_unlock_upgrade_and_lock()) 489 | { 490 | assert(state == reading); 491 | state = writing; 492 | assert(state == writing); 493 | state = reading; 494 | mut.unlock_and_lock_shared(); 495 | assert(state == reading); 496 | mut.unlock_shared(); 497 | ++count; 498 | } 499 | else 500 | { 501 | mut.unlock_upgrade(); 502 | } 503 | } 504 | } 505 | print("try_counter_clockwise = ", count, '\n'); 506 | } 507 | 508 | void try_for_counter_clockwise() 509 | { 510 | typedef std::chrono::steady_clock Clock; 511 | unsigned count = 0; 512 | Clock::time_point until = Clock::now() + std::chrono::seconds(3); 513 | while (Clock::now() < until) 514 | { 515 | if (mut.try_lock_upgrade_for(std::chrono::microseconds(5))) 516 | { 517 | assert(state == reading); 518 | if (mut.try_unlock_upgrade_and_lock_for(std::chrono::microseconds(5))) 519 | { 520 | assert(state == reading); 521 | state = writing; 522 | assert(state == writing); 523 | state = reading; 524 | mut.unlock_and_lock_shared(); 525 | assert(state == reading); 526 | mut.unlock_shared(); 527 | ++count; 528 | } 529 | else 530 | { 531 | mut.unlock_upgrade(); 532 | } 533 | } 534 | } 535 | print("try_for_counter_clockwise = ", count, '\n'); 536 | } 537 | 538 | void 539 | test_upgrade_mutex() 540 | { 541 | { 542 | std::thread t1(reader); 543 | std::thread t2(writer); 544 | std::thread t3(reader); 545 | t1.join(); 546 | t2.join(); 547 | t3.join(); 548 | } 549 | { 550 | std::thread t1(try_reader); 551 | std::thread t2(try_writer); 552 | std::thread t3(try_reader); 553 | t1.join(); 554 | t2.join(); 555 | t3.join(); 556 | } 557 | { 558 | std::thread t1(try_for_reader); 559 | std::thread t2(try_for_writer); 560 | std::thread t3(try_for_reader); 561 | t1.join(); 562 | t2.join(); 563 | t3.join(); 564 | } 565 | { 566 | std::thread t1(reader); 567 | std::thread t2(writer); 568 | std::thread t3(upgradable); 569 | t1.join(); 570 | t2.join(); 571 | t3.join(); 572 | } 573 | { 574 | std::thread t1(reader); 575 | std::thread t2(writer); 576 | std::thread t3(try_upgradable); 577 | t1.join(); 578 | t2.join(); 579 | t3.join(); 580 | } 581 | { 582 | std::thread t1(reader); 583 | std::thread t2(writer); 584 | std::thread t3(try_for_upgradable); 585 | t1.join(); 586 | t2.join(); 587 | t3.join(); 588 | } 589 | { 590 | state = reading; 591 | std::thread t1(clockwise); 592 | std::thread t2(counter_clockwise); 593 | std::thread t3(clockwise); 594 | std::thread t4(counter_clockwise); 595 | t1.join(); 596 | t2.join(); 597 | t3.join(); 598 | t4.join(); 599 | } 600 | { 601 | state = reading; 602 | std::thread t1(try_clockwise); 603 | std::thread t2(try_counter_clockwise); 604 | t1.join(); 605 | t2.join(); 606 | } 607 | { 608 | state = reading; 609 | std::thread t1(try_for_clockwise); 610 | std::thread t2(try_for_counter_clockwise); 611 | t1.join(); 612 | t2.join(); 613 | } 614 | } 615 | 616 | } 617 | 618 | #include 619 | 620 | namespace Assignment 621 | { 622 | 623 | class A 624 | { 625 | typedef acme::upgrade_mutex mutex_type; 626 | typedef std::shared_lock SharedLock; 627 | typedef acme::upgrade_lock UpgradeLock; 628 | typedef std::unique_lock Lock; 629 | 630 | mutable mutex_type mut_; 631 | std::vector data_; 632 | 633 | public: 634 | 635 | A(const A& a) 636 | { 637 | SharedLock _(a.mut_); 638 | data_ = a.data_; 639 | } 640 | 641 | A& operator=(const A& a) 642 | { 643 | if (this != &a) 644 | { 645 | Lock this_lock(mut_, std::defer_lock); 646 | SharedLock that_lock(a.mut_, std::defer_lock); 647 | std::lock(this_lock, that_lock); 648 | data_ = a.data_; 649 | } 650 | return *this; 651 | } 652 | 653 | void swap(A& a) 654 | { 655 | Lock this_lock(mut_, std::defer_lock); 656 | Lock that_lock(a.mut_, std::defer_lock); 657 | std::lock(this_lock, that_lock); 658 | data_.swap(a.data_); 659 | } 660 | 661 | void average(A& a) 662 | { 663 | assert(data_.size() == a.data_.size()); 664 | assert(this != &a); 665 | 666 | Lock this_lock(mut_, std::defer_lock); 667 | UpgradeLock share_that_lock(a.mut_, std::defer_lock); 668 | std::lock(this_lock, share_that_lock); 669 | 670 | for (unsigned i = 0; i < data_.size(); ++i) 671 | data_[i] = (data_[i] + a.data_[i]) / 2; 672 | 673 | #if 0 674 | // Not possible without committee help 675 | SharedLock share_this_lock(std::move(this_lock)); 676 | #else 677 | // Ugly workaraound 678 | SharedLock share_this_lock(*this_lock.release(), std::adopt_lock); 679 | share_this_lock.mutex()->unlock_and_lock_shared(); 680 | #endif 681 | Lock that_lock(std::move(share_that_lock)); 682 | a.data_ = data_; 683 | } 684 | }; 685 | 686 | } // Assignment 687 | 688 | void temp() 689 | { 690 | using namespace acme; 691 | using namespace std; 692 | static upgrade_mutex mut; 693 | unique_lock ul(mut); 694 | shared_lock sl; 695 | #if 0 696 | // Not possible without committee help 697 | sl = shared_lock(std::move(ul)); 698 | #else 699 | // Ugly workaraound 700 | sl = shared_lock(*ul.release(), std::adopt_lock); 701 | sl.mutex()->unlock_and_lock_shared(); 702 | #endif 703 | } 704 | 705 | int main() 706 | { 707 | S::test_shared_mutex(); 708 | U::test_upgrade_mutex(); 709 | } 710 | -------------------------------------------------------------------------------- /upgrade_mutex.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------- upgrade_mutex.cpp -------------------------------- 2 | // 3 | // This software is in the public domain. The only restriction on its use is 4 | // that no one can remove it from the public domain by claiming ownership of it, 5 | // including the original authors. 6 | // 7 | // There is no warranty of correctness on the software contained herein. Use 8 | // at your own risk. 9 | // 10 | //------------------------------------------------------------------------------ 11 | 12 | #include "upgrade_mutex.h" 13 | 14 | namespace acme 15 | { 16 | 17 | upgrade_mutex::upgrade_mutex() 18 | : state_(0) 19 | { 20 | } 21 | 22 | upgrade_mutex::~upgrade_mutex() = default; 23 | 24 | // Exclusive ownership 25 | 26 | void 27 | upgrade_mutex::lock() 28 | { 29 | std::unique_lock lk(mut_); 30 | while (state_ & (write_entered_ | upgradable_entered_)) 31 | gate1_.wait(lk); 32 | state_ |= write_entered_; 33 | while (state_ & n_readers_) 34 | gate2_.wait(lk); 35 | } 36 | 37 | bool 38 | upgrade_mutex::try_lock() 39 | { 40 | std::unique_lock lk(mut_); 41 | if (state_ == 0) 42 | { 43 | state_ = write_entered_; 44 | return true; 45 | } 46 | return false; 47 | } 48 | 49 | void 50 | upgrade_mutex::unlock() 51 | { 52 | std::lock_guard _(mut_); 53 | state_ = 0; 54 | gate1_.notify_all(); 55 | } 56 | 57 | // Shared ownership 58 | 59 | void 60 | upgrade_mutex::lock_shared() 61 | { 62 | std::unique_lock lk(mut_); 63 | while ((state_ & write_entered_) || (state_ & n_readers_) == n_readers_) 64 | gate1_.wait(lk); 65 | unsigned num_readers = (state_ & n_readers_) + 1; 66 | state_ &= ~n_readers_; 67 | state_ |= num_readers; 68 | } 69 | 70 | bool 71 | upgrade_mutex::try_lock_shared() 72 | { 73 | std::unique_lock lk(mut_); 74 | unsigned num_readers = state_ & n_readers_; 75 | if (!(state_ & write_entered_) && num_readers != n_readers_) 76 | { 77 | ++num_readers; 78 | state_ &= ~n_readers_; 79 | state_ |= num_readers; 80 | return true; 81 | } 82 | return false; 83 | } 84 | 85 | void 86 | upgrade_mutex::unlock_shared() 87 | { 88 | std::lock_guard _(mut_); 89 | unsigned num_readers = (state_ & n_readers_) - 1; 90 | state_ &= ~n_readers_; 91 | state_ |= num_readers; 92 | if (state_ & write_entered_) 93 | { 94 | if (num_readers == 0) 95 | gate2_.notify_one(); 96 | } 97 | else 98 | { 99 | if (num_readers == n_readers_ - 1) 100 | gate1_.notify_one(); 101 | } 102 | } 103 | 104 | // Upgrade ownership 105 | 106 | void 107 | upgrade_mutex::lock_upgrade() 108 | { 109 | std::unique_lock lk(mut_); 110 | while ((state_ & (write_entered_ | upgradable_entered_)) || 111 | (state_ & n_readers_) == n_readers_) 112 | gate1_.wait(lk); 113 | unsigned num_readers = (state_ & n_readers_) + 1; 114 | state_ &= ~n_readers_; 115 | state_ |= upgradable_entered_ | num_readers; 116 | } 117 | 118 | bool 119 | upgrade_mutex::try_lock_upgrade() 120 | { 121 | std::unique_lock lk(mut_); 122 | unsigned num_readers = state_ & n_readers_; 123 | if (!(state_ & (write_entered_ | upgradable_entered_)) 124 | && num_readers != n_readers_) 125 | { 126 | ++num_readers; 127 | state_ &= ~n_readers_; 128 | state_ |= upgradable_entered_ | num_readers; 129 | return true; 130 | } 131 | return false; 132 | } 133 | 134 | void 135 | upgrade_mutex::unlock_upgrade() 136 | { 137 | { 138 | std::lock_guard _(mut_); 139 | unsigned num_readers = (state_ & n_readers_) - 1; 140 | state_ &= ~(upgradable_entered_ | n_readers_); 141 | state_ |= num_readers; 142 | } 143 | gate1_.notify_all(); 144 | } 145 | 146 | // Shared <-> Exclusive 147 | 148 | bool 149 | upgrade_mutex::try_unlock_shared_and_lock() 150 | { 151 | std::unique_lock lk(mut_); 152 | if (state_ == 1) 153 | { 154 | state_ = write_entered_; 155 | return true; 156 | } 157 | return false; 158 | } 159 | 160 | void 161 | upgrade_mutex::unlock_and_lock_shared() 162 | { 163 | { 164 | std::lock_guard _(mut_); 165 | state_ = 1; 166 | } 167 | gate1_.notify_all(); 168 | } 169 | 170 | // Shared <-> Upgrade 171 | 172 | bool 173 | upgrade_mutex::try_unlock_shared_and_lock_upgrade() 174 | { 175 | std::unique_lock lk(mut_); 176 | if (!(state_ & (write_entered_ | upgradable_entered_))) 177 | { 178 | state_ |= upgradable_entered_; 179 | return true; 180 | } 181 | return false; 182 | } 183 | 184 | void 185 | upgrade_mutex::unlock_upgrade_and_lock_shared() 186 | { 187 | { 188 | std::lock_guard _(mut_); 189 | state_ &= ~upgradable_entered_; 190 | } 191 | gate1_.notify_all(); 192 | } 193 | 194 | // Upgrade <-> Exclusive 195 | 196 | void 197 | upgrade_mutex::unlock_upgrade_and_lock() 198 | { 199 | std::unique_lock lk(mut_); 200 | unsigned num_readers = (state_ & n_readers_) - 1; 201 | state_ &= ~(upgradable_entered_ | n_readers_); 202 | state_ |= write_entered_ | num_readers; 203 | while (state_ & n_readers_) 204 | gate2_.wait(lk); 205 | } 206 | 207 | bool 208 | upgrade_mutex::try_unlock_upgrade_and_lock() 209 | { 210 | std::unique_lock lk(mut_); 211 | if (state_ == (upgradable_entered_ | 1)) 212 | { 213 | state_ = write_entered_; 214 | return true; 215 | } 216 | return false; 217 | } 218 | 219 | void 220 | upgrade_mutex::unlock_and_lock_upgrade() 221 | { 222 | { 223 | std::lock_guard _(mut_); 224 | state_ = upgradable_entered_ | 1; 225 | } 226 | gate1_.notify_all(); 227 | } 228 | 229 | } // acme 230 | -------------------------------------------------------------------------------- /upgrade_mutex.h: -------------------------------------------------------------------------------- 1 | //---------------------------- upgrade_mutex.h --------------------------------- 2 | // 3 | // This software is in the public domain. The only restriction on its use is 4 | // that no one can remove it from the public domain by claiming ownership of it, 5 | // including the original authors. 6 | // 7 | // There is no warranty of correctness on the software contained herein. Use 8 | // at your own risk. 9 | // 10 | //------------------------------------------------------------------------------ 11 | 12 | #ifndef UPGRADE_MUTEX 13 | #define UPGRADE_MUTEX 14 | 15 | /* 16 | synopsis 17 | 18 | namespace acme 19 | { 20 | 21 | class upgrade_mutex 22 | { 23 | public: 24 | upgrade_mutex(); 25 | ~upgrade_mutex(); 26 | 27 | upgrade_mutex(const upgrade_mutex&) = delete; 28 | upgrade_mutex& operator=(const upgrade_mutex&) = delete; 29 | 30 | // Exclusive ownership 31 | 32 | void lock(); 33 | bool try_lock(); 34 | template 35 | bool try_lock_for(const std::chrono::duration& rel_time); 36 | template 37 | bool 38 | try_lock_until( 39 | const std::chrono::time_point& abs_time); 40 | void unlock(); 41 | 42 | // Shared ownership 43 | 44 | void lock_shared(); 45 | bool try_lock_shared(); 46 | template 47 | bool 48 | try_lock_shared_for(const std::chrono::duration& rel_time); 49 | template 50 | bool 51 | try_lock_shared_until( 52 | const std::chrono::time_point& abs_time); 53 | void unlock_shared(); 54 | 55 | // Upgrade ownership 56 | 57 | void lock_upgrade(); 58 | bool try_lock_upgrade(); 59 | template 60 | bool 61 | try_lock_upgrade_for( 62 | const std::chrono::duration& rel_time); 63 | template 64 | bool 65 | try_lock_upgrade_until( 66 | const std::chrono::time_point& abs_time); 67 | void unlock_upgrade(); 68 | 69 | // Shared <-> Exclusive -- unused by Locks without std::lib cooperation 70 | 71 | bool try_unlock_shared_and_lock(); 72 | template 73 | bool 74 | try_unlock_shared_and_lock_for( 75 | const std::chrono::duration& rel_time); 76 | template 77 | bool 78 | try_unlock_shared_and_lock_until( 79 | const std::chrono::time_point& abs_time); 80 | void unlock_and_lock_shared(); 81 | 82 | // Shared <-> Upgrade 83 | 84 | bool try_unlock_shared_and_lock_upgrade(); 85 | template 86 | bool 87 | try_unlock_shared_and_lock_upgrade_for( 88 | const std::chrono::duration& rel_time); 89 | template 90 | bool 91 | try_unlock_shared_and_lock_upgrade_until( 92 | const std::chrono::time_point& abs_time); 93 | void unlock_upgrade_and_lock_shared(); 94 | 95 | // Upgrade <-> Exclusive 96 | 97 | void unlock_upgrade_and_lock(); 98 | void unlock_and_lock_upgrade(); 99 | 100 | // unused by Locks without std::lib cooperation 101 | 102 | bool try_unlock_upgrade_and_lock(); 103 | template 104 | bool 105 | try_unlock_upgrade_and_lock_for( 106 | const std::chrono::duration& rel_time); 107 | template 108 | bool 109 | try_unlock_upgrade_and_lock_until( 110 | const std::chrono::time_point& abs_time); 111 | }; 112 | 113 | template 114 | class upgrade_lock 115 | { 116 | public: 117 | typedef Mutex mutex_type; 118 | 119 | ~upgrade_lock(); 120 | upgrade_lock() noexcept; 121 | upgrade_lock(upgrade_lock const&) = delete; 122 | upgrade_lock& operator=(upgrade_lock const&) = delete; 123 | upgrade_lock(upgrade_lock&& ul) noexcept; 124 | upgrade_lock& operator=(upgrade_lock&& ul); 125 | 126 | explicit upgrade_lock(mutex_type& m); 127 | upgrade_lock(mutex_type& m, std::defer_lock_t) noexcept; 128 | upgrade_lock(mutex_type& m, std::try_to_lock_t); 129 | upgrade_lock(mutex_type& m, std::adopt_lock_t) noexcept; 130 | template 131 | upgrade_lock(mutex_type& m, 132 | const std::chrono::time_point& abs_time); 133 | template 134 | upgrade_lock(mutex_type& m, 135 | const std::chrono::duration& rel_time); 136 | 137 | // Shared <-> Upgrade 138 | 139 | upgrade_lock(std::shared_lock&& sl, std::try_to_lock_t); 140 | 141 | template 142 | upgrade_lock(std::shared_lock&& sl, 143 | const std::chrono::time_point& abs_time); 144 | template 145 | upgrade_lock(std::shared_lock&& sl, 146 | const std::chrono::duration& rel_time); 147 | explicit operator std::shared_lock () &&; 148 | 149 | // Exclusive <-> Upgrade 150 | 151 | explicit upgrade_lock(std::unique_lock&& ul); 152 | explicit operator std::unique_lock () &&; 153 | 154 | // Upgrade 155 | 156 | void lock(); 157 | bool try_lock(); 158 | template 159 | bool try_lock_for(const std::chrono::duration& rel_time); 160 | template 161 | bool 162 | try_lock_until( 163 | const std::chrono::time_point& abs_time); 164 | void unlock(); 165 | 166 | void swap(upgrade_lock&& u); 167 | mutex_type* release(); 168 | 169 | bool owns_lock() const; 170 | explicit operator bool () const; 171 | mutex_type* mutex() const; 172 | }; 173 | 174 | template 175 | void 176 | swap(upgrade_lock& x, upgrade_lock& y); 177 | 178 | } // acme 179 | */ 180 | 181 | #include 182 | #include 183 | #include 184 | #include 185 | #include 186 | 187 | namespace acme 188 | { 189 | 190 | // upgrade_mutex 191 | 192 | class upgrade_mutex 193 | { 194 | std::mutex mut_; 195 | std::condition_variable gate1_; 196 | std::condition_variable gate2_; 197 | unsigned state_; 198 | 199 | static const unsigned write_entered_ = 1U << (sizeof(unsigned)*CHAR_BIT - 1); 200 | static const unsigned upgradable_entered_ = write_entered_ >> 1; 201 | static const unsigned n_readers_ = ~(write_entered_ | upgradable_entered_); 202 | 203 | public: 204 | upgrade_mutex(); 205 | ~upgrade_mutex(); 206 | 207 | upgrade_mutex(const upgrade_mutex&) = delete; 208 | upgrade_mutex& operator=(const upgrade_mutex&) = delete; 209 | 210 | // Exclusive ownership 211 | 212 | void lock(); 213 | bool try_lock(); 214 | template 215 | bool try_lock_for(const std::chrono::duration& rel_time) 216 | { 217 | return try_lock_until(std::chrono::steady_clock::now() + rel_time); 218 | } 219 | template 220 | bool 221 | try_lock_until( 222 | const std::chrono::time_point& abs_time); 223 | void unlock(); 224 | 225 | // Shared ownership 226 | 227 | void lock_shared(); 228 | bool try_lock_shared(); 229 | template 230 | bool 231 | try_lock_shared_for(const std::chrono::duration& rel_time) 232 | { 233 | return try_lock_shared_until(std::chrono::steady_clock::now() + 234 | rel_time); 235 | } 236 | template 237 | bool 238 | try_lock_shared_until( 239 | const std::chrono::time_point& abs_time); 240 | void unlock_shared(); 241 | 242 | // Upgrade ownership 243 | 244 | void lock_upgrade(); 245 | bool try_lock_upgrade(); 246 | template 247 | bool 248 | try_lock_upgrade_for( 249 | const std::chrono::duration& rel_time) 250 | { 251 | return try_lock_upgrade_until(std::chrono::steady_clock::now() + 252 | rel_time); 253 | } 254 | template 255 | bool 256 | try_lock_upgrade_until( 257 | const std::chrono::time_point& abs_time); 258 | void unlock_upgrade(); 259 | 260 | // Shared <-> Exclusive 261 | 262 | bool try_unlock_shared_and_lock(); 263 | template 264 | bool 265 | try_unlock_shared_and_lock_for( 266 | const std::chrono::duration& rel_time) 267 | { 268 | return try_unlock_shared_and_lock_until( 269 | std::chrono::steady_clock::now() + rel_time); 270 | } 271 | template 272 | bool 273 | try_unlock_shared_and_lock_until( 274 | const std::chrono::time_point& abs_time); 275 | void unlock_and_lock_shared(); 276 | 277 | // Shared <-> Upgrade 278 | 279 | bool try_unlock_shared_and_lock_upgrade(); 280 | template 281 | bool 282 | try_unlock_shared_and_lock_upgrade_for( 283 | const std::chrono::duration& rel_time) 284 | { 285 | return try_unlock_shared_and_lock_upgrade_until( 286 | std::chrono::steady_clock::now() + rel_time); 287 | } 288 | template 289 | bool 290 | try_unlock_shared_and_lock_upgrade_until( 291 | const std::chrono::time_point& abs_time); 292 | void unlock_upgrade_and_lock_shared(); 293 | 294 | // Upgrade <-> Exclusive 295 | 296 | void unlock_upgrade_and_lock(); 297 | bool try_unlock_upgrade_and_lock(); 298 | template 299 | bool 300 | try_unlock_upgrade_and_lock_for( 301 | const std::chrono::duration& rel_time) 302 | { 303 | return try_unlock_upgrade_and_lock_until( 304 | std::chrono::steady_clock::now() + rel_time); 305 | } 306 | template 307 | bool 308 | try_unlock_upgrade_and_lock_until( 309 | const std::chrono::time_point& abs_time); 310 | void unlock_and_lock_upgrade(); 311 | }; 312 | 313 | template 314 | bool 315 | upgrade_mutex::try_lock_until( 316 | const std::chrono::time_point& abs_time) 317 | { 318 | std::unique_lock lk(mut_); 319 | if (state_ & (write_entered_ | upgradable_entered_)) 320 | { 321 | while (true) 322 | { 323 | std::cv_status status = gate1_.wait_until(lk, abs_time); 324 | if ((state_ & (write_entered_ | upgradable_entered_)) == 0) 325 | break; 326 | if (status == std::cv_status::timeout) 327 | return false; 328 | } 329 | } 330 | state_ |= write_entered_; 331 | if (state_ & n_readers_) 332 | { 333 | while (true) 334 | { 335 | std::cv_status status = gate2_.wait_until(lk, abs_time); 336 | if ((state_ & n_readers_) == 0) 337 | break; 338 | if (status == std::cv_status::timeout) 339 | { 340 | state_ &= ~write_entered_; 341 | return false; 342 | } 343 | } 344 | } 345 | return true; 346 | } 347 | 348 | template 349 | bool 350 | upgrade_mutex::try_lock_shared_until( 351 | const std::chrono::time_point& abs_time) 352 | { 353 | std::unique_lock lk(mut_); 354 | if ((state_ & write_entered_) || (state_ & n_readers_) == n_readers_) 355 | { 356 | while (true) 357 | { 358 | std::cv_status status = gate1_.wait_until(lk, abs_time); 359 | if ((state_ & write_entered_) == 0 && 360 | (state_ & n_readers_) < n_readers_) 361 | break; 362 | if (status == std::cv_status::timeout) 363 | return false; 364 | } 365 | } 366 | unsigned num_readers = (state_ & n_readers_) + 1; 367 | state_ &= ~n_readers_; 368 | state_ |= num_readers; 369 | return true; 370 | } 371 | 372 | template 373 | bool 374 | upgrade_mutex::try_lock_upgrade_until( 375 | const std::chrono::time_point& abs_time) 376 | { 377 | std::unique_lock lk(mut_); 378 | if ((state_ & (write_entered_ | upgradable_entered_)) || 379 | (state_ & n_readers_) == n_readers_) 380 | { 381 | while (true) 382 | { 383 | std::cv_status status = gate1_.wait_until(lk, abs_time); 384 | if ((state_ & (write_entered_ | upgradable_entered_)) == 0 && 385 | (state_ & n_readers_) < n_readers_) 386 | break; 387 | if (status == std::cv_status::timeout) 388 | return false; 389 | } 390 | } 391 | unsigned num_readers = (state_ & n_readers_) + 1; 392 | state_ &= ~n_readers_; 393 | state_ |= upgradable_entered_ | num_readers; 394 | return true; 395 | } 396 | 397 | template 398 | bool 399 | upgrade_mutex::try_unlock_shared_and_lock_until( 400 | const std::chrono::time_point& abs_time) 401 | { 402 | std::unique_lock lk(mut_); 403 | if (state_ != 1) 404 | { 405 | while (true) 406 | { 407 | std::cv_status status = gate2_.wait_until(lk, abs_time); 408 | if (state_ == 1) 409 | break; 410 | if (status == std::cv_status::timeout) 411 | return false; 412 | } 413 | } 414 | state_ = write_entered_; 415 | return true; 416 | } 417 | 418 | template 419 | bool 420 | upgrade_mutex::try_unlock_shared_and_lock_upgrade_until( 421 | const std::chrono::time_point& abs_time) 422 | { 423 | std::unique_lock lk(mut_); 424 | if ((state_ & (write_entered_ | upgradable_entered_)) != 0) 425 | { 426 | while (true) 427 | { 428 | std::cv_status status = gate2_.wait_until(lk, abs_time); 429 | if ((state_ & (write_entered_ | upgradable_entered_)) == 0) 430 | break; 431 | if (status == std::cv_status::timeout) 432 | return false; 433 | } 434 | } 435 | state_ |= upgradable_entered_; 436 | return true; 437 | } 438 | 439 | template 440 | bool 441 | upgrade_mutex::try_unlock_upgrade_and_lock_until( 442 | const std::chrono::time_point& abs_time) 443 | { 444 | std::unique_lock lk(mut_); 445 | if ((state_ & n_readers_) != 1) 446 | { 447 | while (true) 448 | { 449 | std::cv_status status = gate2_.wait_until(lk, abs_time); 450 | if ((state_ & n_readers_) == 1) 451 | break; 452 | if (status == std::cv_status::timeout) 453 | return false; 454 | } 455 | } 456 | state_ = write_entered_; 457 | return true; 458 | } 459 | 460 | // upgrade_lock 461 | 462 | template 463 | class upgrade_lock 464 | { 465 | public: 466 | typedef Mutex mutex_type; 467 | 468 | private: 469 | mutex_type* m_; 470 | bool owns_; 471 | 472 | struct __nat {int _;}; 473 | 474 | public: 475 | ~upgrade_lock() 476 | { 477 | if (owns_) 478 | m_->unlock_upgrade(); 479 | } 480 | 481 | upgrade_lock() noexcept 482 | : m_(nullptr), owns_(false) {} 483 | 484 | upgrade_lock(upgrade_lock const&) = delete; 485 | upgrade_lock& operator=(upgrade_lock const&) = delete; 486 | 487 | upgrade_lock(upgrade_lock&& ul) noexcept 488 | : m_(ul.m_), owns_(ul.owns_) 489 | { 490 | ul.m_ = nullptr; 491 | ul.owns_ = false; 492 | } 493 | 494 | upgrade_lock& operator=(upgrade_lock&& ul) 495 | { 496 | if (owns_) 497 | m_->unlock_upgrade(); 498 | m_ = ul.m_; 499 | owns_ = ul.owns_; 500 | ul.m_ = nullptr; 501 | ul.owns_ = false; 502 | return *this; 503 | } 504 | 505 | explicit upgrade_lock(mutex_type& m) 506 | : m_(&m), owns_(true) 507 | {m_->lock_upgrade();} 508 | 509 | upgrade_lock(mutex_type& m, std::defer_lock_t) noexcept 510 | : m_(&m), owns_(false) {} 511 | 512 | upgrade_lock(mutex_type& m, std::try_to_lock_t) 513 | : m_(&m), owns_(m.try_lock_upgrade()) {} 514 | 515 | upgrade_lock(mutex_type& m, std::adopt_lock_t) noexcept 516 | : m_(&m), owns_(true) {} 517 | 518 | template 519 | upgrade_lock(mutex_type& m, 520 | const std::chrono::time_point& abs_time) 521 | : m_(&m), owns_(m.try_lock_upgrade_until(abs_time)) {} 522 | template 523 | upgrade_lock(mutex_type& m, 524 | const std::chrono::duration& rel_time) 525 | : m_(&m), owns_(m.try_lock_upgrade_for(rel_time)) {} 526 | 527 | // Shared <-> Upgrade 528 | 529 | upgrade_lock(std::shared_lock&& sl, std::try_to_lock_t) 530 | : m_(nullptr), owns_(false) 531 | { 532 | if (sl.owns_lock()) 533 | { 534 | if (sl.mutex()->try_unlock_shared_and_lock_upgrade()) 535 | { 536 | m_ = sl.release(); 537 | owns_ = true; 538 | } 539 | } 540 | else 541 | m_ = sl.release(); 542 | } 543 | 544 | template 545 | upgrade_lock(std::shared_lock&& sl, 546 | const std::chrono::time_point& abs_time) 547 | : m_(nullptr), owns_(false) 548 | { 549 | if (sl.owns_lock()) 550 | { 551 | if (sl.mutex()->try_unlock_shared_and_lock_upgrade_until(abs_time)) 552 | { 553 | m_ = sl.release(); 554 | owns_ = true; 555 | } 556 | } 557 | else 558 | m_ = sl.release(); 559 | } 560 | 561 | template 562 | upgrade_lock(std::shared_lock&& sl, 563 | const std::chrono::duration& rel_time) 564 | : m_(nullptr), owns_(false) 565 | { 566 | if (sl.owns_lock()) 567 | { 568 | if (sl.mutex()->try_unlock_shared_and_lock_upgrade_for(rel_time)) 569 | { 570 | m_ = sl.release(); 571 | owns_ = true; 572 | } 573 | } 574 | else 575 | m_ = sl.release(); 576 | } 577 | 578 | explicit operator std::shared_lock () && 579 | { 580 | if (owns_) 581 | { 582 | m_->unlock_upgrade_and_lock_shared(); 583 | return std::shared_lock(*release(), std::adopt_lock); 584 | } 585 | if (m_ != nullptr) 586 | return std::shared_lock(*release(), std::defer_lock); 587 | return std::shared_lock{}; 588 | } 589 | 590 | // Exclusive <-> Upgrade 591 | 592 | explicit upgrade_lock(std::unique_lock&& ul) 593 | : m_(ul.mutex()), owns_(ul.owns_lock()) 594 | { 595 | if (owns_) 596 | m_->unlock_and_lock_upgrade(); 597 | ul.release(); 598 | } 599 | 600 | explicit operator std::unique_lock () && 601 | { 602 | if (owns_) 603 | { 604 | m_->unlock_upgrade_and_lock(); 605 | return std::unique_lock(*release(), std::adopt_lock); 606 | } 607 | if (m_ != nullptr) 608 | return std::unique_lock(*release(), std::defer_lock); 609 | return std::unique_lock{}; 610 | } 611 | 612 | // Upgrade 613 | 614 | void lock(); 615 | bool try_lock(); 616 | template 617 | bool try_lock_for(const std::chrono::duration& rel_time) 618 | { 619 | return try_lock_until(std::chrono::steady_clock::now() + rel_time); 620 | } 621 | template 622 | bool 623 | try_lock_until( 624 | const std::chrono::time_point& abs_time); 625 | void unlock(); 626 | 627 | void swap(upgrade_lock&& u) 628 | { 629 | std::swap(m_, u.m_); 630 | std::swap(owns_, u.owns_); 631 | } 632 | 633 | mutex_type* release() 634 | { 635 | mutex_type* r = m_; 636 | m_ = 0; 637 | owns_ = false; 638 | return r; 639 | } 640 | 641 | bool owns_lock() const {return owns_;} 642 | explicit operator bool () const {return owns_;} 643 | mutex_type* mutex() const {return m_;} 644 | }; 645 | 646 | template 647 | void 648 | upgrade_lock::lock() 649 | { 650 | if (m_ == nullptr) 651 | throw std::system_error(std::error_code(EPERM, std::system_category()), 652 | "upgrade_lock::lock: references null mutex"); 653 | if (owns_) 654 | throw std::system_error(std::error_code(EDEADLK, std::system_category()), 655 | "upgrade_lock::lock: already locked"); 656 | m_->lock_upgrade(); 657 | owns_ = true; 658 | } 659 | 660 | template 661 | bool 662 | upgrade_lock::try_lock() 663 | { 664 | if (m_ == nullptr) 665 | throw std::system_error(std::error_code(EPERM, std::system_category()), 666 | "upgrade_lock::try_lock: references null mutex"); 667 | if (owns_) 668 | throw std::system_error(std::error_code(EDEADLK, std::system_category()), 669 | "upgrade_lock::try_lock: already locked"); 670 | owns_ = m_->try_lock_upgrade(); 671 | return owns_; 672 | } 673 | 674 | template 675 | template 676 | bool 677 | upgrade_lock::try_lock_until( 678 | const std::chrono::time_point& abs_time) 679 | { 680 | if (m_ == nullptr) 681 | throw std::system_error(std::error_code(EPERM, std::system_category()), 682 | "upgrade_lock::try_lock_until: references null mutex"); 683 | if (owns_) 684 | throw std::system_error(std::error_code(EDEADLK, std::system_category()), 685 | "upgrade_lock::try_lock_until: already locked"); 686 | owns_ = m_->try_lock_upgrade_until(abs_time); 687 | return owns_; 688 | } 689 | 690 | template 691 | void 692 | upgrade_lock::unlock() 693 | { 694 | if (!owns_) 695 | throw std::system_error(std::error_code(EPERM, std::system_category()), 696 | "upgrade_lock::unlock: not locked"); 697 | m_->unlock_upgrade(); 698 | owns_ = false; 699 | } 700 | 701 | template 702 | inline 703 | void 704 | swap(upgrade_lock& x, upgrade_lock& y) 705 | { 706 | x.swap(y); 707 | } 708 | 709 | } // acme 710 | 711 | #endif // UPGRADE_MUTEX 712 | --------------------------------------------------------------------------------