1 /* 2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 4 * Distributed under the terms of the MIT License. 5 * 6 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 7 * Distributed under the terms of the NewOS License. 8 */ 9 10 11 /*! Mutex and recursive_lock code */ 12 13 14 #include <lock.h> 15 16 #include <stdlib.h> 17 #include <string.h> 18 19 #include <OS.h> 20 21 #include <debug.h> 22 #include <int.h> 23 #include <kernel.h> 24 #include <listeners.h> 25 #include <scheduling_analysis.h> 26 #include <thread.h> 27 #include <util/AutoLock.h> 28 29 30 struct mutex_waiter { 31 Thread* thread; 32 mutex_waiter* next; // next in queue 33 mutex_waiter* last; // last in queue (valid for the first in queue) 34 }; 35 36 struct rw_lock_waiter { 37 Thread* thread; 38 rw_lock_waiter* next; // next in queue 39 rw_lock_waiter* last; // last in queue (valid for the first in queue) 40 bool writer; 41 }; 42 43 #define MUTEX_FLAG_RELEASED 0x2 44 45 46 int32 47 recursive_lock_get_recursion(recursive_lock *lock) 48 { 49 if (RECURSIVE_LOCK_HOLDER(lock) == thread_get_current_thread_id()) 50 return lock->recursion; 51 52 return -1; 53 } 54 55 56 void 57 recursive_lock_init(recursive_lock *lock, const char *name) 58 { 59 mutex_init(&lock->lock, name != NULL ? name : "recursive lock"); 60 RECURSIVE_LOCK_HOLDER(lock) = -1; 61 lock->recursion = 0; 62 } 63 64 65 void 66 recursive_lock_init_etc(recursive_lock *lock, const char *name, uint32 flags) 67 { 68 mutex_init_etc(&lock->lock, name != NULL ? name : "recursive lock", flags); 69 RECURSIVE_LOCK_HOLDER(lock) = -1; 70 lock->recursion = 0; 71 } 72 73 74 void 75 recursive_lock_destroy(recursive_lock *lock) 76 { 77 if (lock == NULL) 78 return; 79 80 mutex_destroy(&lock->lock); 81 } 82 83 84 status_t 85 recursive_lock_lock(recursive_lock *lock) 86 { 87 #if KDEBUG 88 if (!gKernelStartup && !are_interrupts_enabled()) { 89 panic("recursive_lock_lock: called with interrupts disabled for lock " 90 "%p (\"%s\")\n", lock, lock->lock.name); 91 } 92 #endif 93 94 thread_id thread = thread_get_current_thread_id(); 95 96 if (thread != RECURSIVE_LOCK_HOLDER(lock)) { 97 mutex_lock(&lock->lock); 98 #if !KDEBUG 99 lock->holder = thread; 100 #endif 101 } 102 103 lock->recursion++; 104 return B_OK; 105 } 106 107 108 status_t 109 recursive_lock_trylock(recursive_lock *lock) 110 { 111 thread_id thread = thread_get_current_thread_id(); 112 113 #if KDEBUG 114 if (!gKernelStartup && !are_interrupts_enabled()) { 115 panic("recursive_lock_lock: called with interrupts disabled for lock " 116 "%p (\"%s\")\n", lock, lock->lock.name); 117 } 118 #endif 119 120 if (thread != RECURSIVE_LOCK_HOLDER(lock)) { 121 status_t status = mutex_trylock(&lock->lock); 122 if (status != B_OK) 123 return status; 124 125 #if !KDEBUG 126 lock->holder = thread; 127 #endif 128 } 129 130 lock->recursion++; 131 return B_OK; 132 } 133 134 135 void 136 recursive_lock_unlock(recursive_lock *lock) 137 { 138 if (thread_get_current_thread_id() != RECURSIVE_LOCK_HOLDER(lock)) 139 panic("recursive_lock %p unlocked by non-holder thread!\n", lock); 140 141 if (--lock->recursion == 0) { 142 #if !KDEBUG 143 lock->holder = -1; 144 #endif 145 mutex_unlock(&lock->lock); 146 } 147 } 148 149 150 // #pragma mark - 151 152 153 static status_t 154 rw_lock_wait(rw_lock* lock, bool writer, InterruptsSpinLocker& locker) 155 { 156 // enqueue in waiter list 157 rw_lock_waiter waiter; 158 waiter.thread = thread_get_current_thread(); 159 waiter.next = NULL; 160 waiter.writer = writer; 161 162 if (lock->waiters != NULL) 163 lock->waiters->last->next = &waiter; 164 else 165 lock->waiters = &waiter; 166 167 lock->waiters->last = &waiter; 168 169 // block 170 thread_prepare_to_block(waiter.thread, 0, THREAD_BLOCK_TYPE_RW_LOCK, lock); 171 locker.Unlock(); 172 173 status_t result = thread_block(); 174 175 locker.Lock(); 176 return result; 177 } 178 179 180 static int32 181 rw_lock_unblock(rw_lock* lock) 182 { 183 // Check whether there are any waiting threads at all and whether anyone 184 // has the write lock. 185 rw_lock_waiter* waiter = lock->waiters; 186 if (waiter == NULL || lock->holder >= 0) 187 return 0; 188 189 // writer at head of queue? 190 if (waiter->writer) { 191 if (lock->active_readers > 0 || lock->pending_readers > 0) 192 return 0; 193 194 // dequeue writer 195 lock->waiters = waiter->next; 196 if (lock->waiters != NULL) 197 lock->waiters->last = waiter->last; 198 199 lock->holder = waiter->thread->id; 200 201 // unblock thread 202 thread_unblock(waiter->thread, B_OK); 203 204 waiter->thread = NULL; 205 return RW_LOCK_WRITER_COUNT_BASE; 206 } 207 208 // wake up one or more readers 209 uint32 readerCount = 0; 210 do { 211 // dequeue reader 212 lock->waiters = waiter->next; 213 if (lock->waiters != NULL) 214 lock->waiters->last = waiter->last; 215 216 readerCount++; 217 218 // unblock thread 219 thread_unblock(waiter->thread, B_OK); 220 221 waiter->thread = NULL; 222 } while ((waiter = lock->waiters) != NULL && !waiter->writer); 223 224 if (lock->count >= RW_LOCK_WRITER_COUNT_BASE) 225 lock->active_readers += readerCount; 226 227 return readerCount; 228 } 229 230 231 void 232 rw_lock_init(rw_lock* lock, const char* name) 233 { 234 lock->name = name; 235 lock->waiters = NULL; 236 B_INITIALIZE_SPINLOCK(&lock->lock); 237 lock->holder = -1; 238 lock->count = 0; 239 lock->owner_count = 0; 240 lock->active_readers = 0; 241 lock->pending_readers = 0; 242 lock->flags = 0; 243 244 T_SCHEDULING_ANALYSIS(InitRWLock(lock, name)); 245 NotifyWaitObjectListeners(&WaitObjectListener::RWLockInitialized, lock); 246 } 247 248 249 void 250 rw_lock_init_etc(rw_lock* lock, const char* name, uint32 flags) 251 { 252 lock->name = (flags & RW_LOCK_FLAG_CLONE_NAME) != 0 ? strdup(name) : name; 253 lock->waiters = NULL; 254 B_INITIALIZE_SPINLOCK(&lock->lock); 255 lock->holder = -1; 256 lock->count = 0; 257 lock->owner_count = 0; 258 lock->active_readers = 0; 259 lock->pending_readers = 0; 260 lock->flags = flags & RW_LOCK_FLAG_CLONE_NAME; 261 262 T_SCHEDULING_ANALYSIS(InitRWLock(lock, name)); 263 NotifyWaitObjectListeners(&WaitObjectListener::RWLockInitialized, lock); 264 } 265 266 267 void 268 rw_lock_destroy(rw_lock* lock) 269 { 270 char* name = (lock->flags & RW_LOCK_FLAG_CLONE_NAME) != 0 271 ? (char*)lock->name : NULL; 272 273 // unblock all waiters 274 InterruptsSpinLocker locker(lock->lock); 275 276 #if KDEBUG 277 if (lock->waiters != NULL && thread_get_current_thread_id() 278 != lock->holder) { 279 panic("rw_lock_destroy(): there are blocking threads, but the caller " 280 "doesn't hold the write lock (%p)", lock); 281 282 locker.Unlock(); 283 if (rw_lock_write_lock(lock) != B_OK) 284 return; 285 locker.Lock(); 286 } 287 #endif 288 289 while (rw_lock_waiter* waiter = lock->waiters) { 290 // dequeue 291 lock->waiters = waiter->next; 292 293 // unblock thread 294 thread_unblock(waiter->thread, B_ERROR); 295 } 296 297 lock->name = NULL; 298 299 locker.Unlock(); 300 301 free(name); 302 } 303 304 305 #if !KDEBUG_RW_LOCK_DEBUG 306 307 status_t 308 _rw_lock_read_lock(rw_lock* lock) 309 { 310 #if KDEBUG 311 if (!gKernelStartup && !are_interrupts_enabled()) { 312 panic("_rw_lock_read_lock(): called with interrupts disabled for lock %p", 313 lock); 314 } 315 #endif 316 317 InterruptsSpinLocker locker(lock->lock); 318 319 // We might be the writer ourselves. 320 if (lock->holder == thread_get_current_thread_id()) { 321 lock->owner_count++; 322 return B_OK; 323 } 324 325 // The writer that originally had the lock when we called atomic_add() might 326 // already have gone and another writer could have overtaken us. In this 327 // case the original writer set pending_readers, so we know that we don't 328 // have to wait. 329 if (lock->pending_readers > 0) { 330 lock->pending_readers--; 331 332 if (lock->count >= RW_LOCK_WRITER_COUNT_BASE) 333 lock->active_readers++; 334 335 return B_OK; 336 } 337 338 ASSERT(lock->count >= RW_LOCK_WRITER_COUNT_BASE); 339 340 // we need to wait 341 return rw_lock_wait(lock, false, locker); 342 } 343 344 345 status_t 346 _rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags, 347 bigtime_t timeout) 348 { 349 #if KDEBUG 350 if (!gKernelStartup && !are_interrupts_enabled()) { 351 panic("_rw_lock_read_lock_with_timeout(): called with interrupts " 352 "disabled for lock %p", lock); 353 } 354 #endif 355 356 InterruptsSpinLocker locker(lock->lock); 357 358 // We might be the writer ourselves. 359 if (lock->holder == thread_get_current_thread_id()) { 360 lock->owner_count++; 361 return B_OK; 362 } 363 364 // The writer that originally had the lock when we called atomic_add() might 365 // already have gone and another writer could have overtaken us. In this 366 // case the original writer set pending_readers, so we know that we don't 367 // have to wait. 368 if (lock->pending_readers > 0) { 369 lock->pending_readers--; 370 371 if (lock->count >= RW_LOCK_WRITER_COUNT_BASE) 372 lock->active_readers++; 373 374 return B_OK; 375 } 376 377 ASSERT(lock->count >= RW_LOCK_WRITER_COUNT_BASE); 378 379 // we need to wait 380 381 // enqueue in waiter list 382 rw_lock_waiter waiter; 383 waiter.thread = thread_get_current_thread(); 384 waiter.next = NULL; 385 waiter.writer = false; 386 387 if (lock->waiters != NULL) 388 lock->waiters->last->next = &waiter; 389 else 390 lock->waiters = &waiter; 391 392 lock->waiters->last = &waiter; 393 394 // block 395 thread_prepare_to_block(waiter.thread, 0, THREAD_BLOCK_TYPE_RW_LOCK, lock); 396 locker.Unlock(); 397 398 status_t error = thread_block_with_timeout(timeoutFlags, timeout); 399 if (error == B_OK || waiter.thread == NULL) { 400 // We were unblocked successfully -- potentially our unblocker overtook 401 // us after we already failed. In either case, we've got the lock, now. 402 return B_OK; 403 } 404 405 locker.Lock(); 406 // We failed to get the lock -- dequeue from waiter list. 407 rw_lock_waiter* previous = NULL; 408 rw_lock_waiter* other = lock->waiters; 409 while (other != &waiter) { 410 previous = other; 411 other = other->next; 412 } 413 414 if (previous == NULL) { 415 // we are the first in line 416 lock->waiters = waiter.next; 417 if (lock->waiters != NULL) 418 lock->waiters->last = waiter.last; 419 } else { 420 // one or more other waiters are before us in the queue 421 previous->next = waiter.next; 422 if (lock->waiters->last == &waiter) 423 lock->waiters->last = previous; 424 } 425 426 // Decrement the count. ATM this is all we have to do. There's at least 427 // one writer ahead of us -- otherwise the last writer would have unblocked 428 // us (writers only manipulate the lock data with thread spinlock being 429 // held) -- so our leaving doesn't make a difference to the ones behind us 430 // in the queue. 431 atomic_add(&lock->count, -1); 432 433 return error; 434 } 435 436 437 void 438 _rw_lock_read_unlock(rw_lock* lock) 439 { 440 InterruptsSpinLocker locker(lock->lock); 441 442 // If we're still holding the write lock or if there are other readers, 443 // no-one can be woken up. 444 if (lock->holder == thread_get_current_thread_id()) { 445 ASSERT(lock->owner_count % RW_LOCK_WRITER_COUNT_BASE > 0); 446 lock->owner_count--; 447 return; 448 } 449 450 if (--lock->active_readers > 0) 451 return; 452 453 if (lock->active_readers < 0) { 454 panic("rw_lock_read_unlock(): lock %p not read-locked", lock); 455 lock->active_readers = 0; 456 return; 457 } 458 459 rw_lock_unblock(lock); 460 } 461 462 #endif // !KDEBUG_RW_LOCK_DEBUG 463 464 465 status_t 466 rw_lock_write_lock(rw_lock* lock) 467 { 468 #if KDEBUG 469 if (!gKernelStartup && !are_interrupts_enabled()) { 470 panic("_rw_lock_write_lock(): called with interrupts disabled for lock %p", 471 lock); 472 } 473 #endif 474 475 InterruptsSpinLocker locker(lock->lock); 476 477 // If we're already the lock holder, we just need to increment the owner 478 // count. 479 thread_id thread = thread_get_current_thread_id(); 480 if (lock->holder == thread) { 481 lock->owner_count += RW_LOCK_WRITER_COUNT_BASE; 482 return B_OK; 483 } 484 485 // announce our claim 486 int32 oldCount = atomic_add(&lock->count, RW_LOCK_WRITER_COUNT_BASE); 487 488 if (oldCount == 0) { 489 // No-one else held a read or write lock, so it's ours now. 490 lock->holder = thread; 491 lock->owner_count = RW_LOCK_WRITER_COUNT_BASE; 492 return B_OK; 493 } 494 495 // We have to wait. If we're the first writer, note the current reader 496 // count. 497 if (oldCount < RW_LOCK_WRITER_COUNT_BASE) 498 lock->active_readers = oldCount - lock->pending_readers; 499 500 status_t status = rw_lock_wait(lock, true, locker); 501 if (status == B_OK) { 502 lock->holder = thread; 503 lock->owner_count = RW_LOCK_WRITER_COUNT_BASE; 504 } 505 506 return status; 507 } 508 509 510 void 511 _rw_lock_write_unlock(rw_lock* lock) 512 { 513 InterruptsSpinLocker locker(lock->lock); 514 515 if (thread_get_current_thread_id() != lock->holder) { 516 panic("rw_lock_write_unlock(): lock %p not write-locked by this thread", 517 lock); 518 return; 519 } 520 521 ASSERT(lock->owner_count >= RW_LOCK_WRITER_COUNT_BASE); 522 523 lock->owner_count -= RW_LOCK_WRITER_COUNT_BASE; 524 if (lock->owner_count >= RW_LOCK_WRITER_COUNT_BASE) 525 return; 526 527 // We gave up our last write lock -- clean up and unblock waiters. 528 int32 readerCount = lock->owner_count; 529 lock->holder = -1; 530 lock->owner_count = 0; 531 532 int32 oldCount = atomic_add(&lock->count, -RW_LOCK_WRITER_COUNT_BASE); 533 oldCount -= RW_LOCK_WRITER_COUNT_BASE; 534 535 if (oldCount != 0) { 536 // If writers are waiting, take over our reader count. 537 if (oldCount >= RW_LOCK_WRITER_COUNT_BASE) { 538 lock->active_readers = readerCount; 539 rw_lock_unblock(lock); 540 } else { 541 // No waiting writer, but there are one or more readers. We will 542 // unblock all waiting readers -- that's the easy part -- and must 543 // also make sure that all readers that haven't entered the critical 544 // section yet, won't start to wait. Otherwise a writer overtaking 545 // such a reader will correctly start to wait, but the reader, 546 // seeing the writer count > 0, would also start to wait. We set 547 // pending_readers to the number of readers that are still expected 548 // to enter the critical section. 549 lock->pending_readers = oldCount - readerCount 550 - rw_lock_unblock(lock); 551 } 552 } 553 } 554 555 556 static int 557 dump_rw_lock_info(int argc, char** argv) 558 { 559 if (argc < 2) { 560 print_debugger_command_usage(argv[0]); 561 return 0; 562 } 563 564 rw_lock* lock = (rw_lock*)parse_expression(argv[1]); 565 566 if (!IS_KERNEL_ADDRESS(lock)) { 567 kprintf("invalid address: %p\n", lock); 568 return 0; 569 } 570 571 kprintf("rw lock %p:\n", lock); 572 kprintf(" name: %s\n", lock->name); 573 kprintf(" holder: %" B_PRId32 "\n", lock->holder); 574 kprintf(" count: %#" B_PRIx32 "\n", lock->count); 575 kprintf(" active readers %d\n", lock->active_readers); 576 kprintf(" pending readers %d\n", lock->pending_readers); 577 kprintf(" owner count: %#" B_PRIx32 "\n", lock->owner_count); 578 kprintf(" flags: %#" B_PRIx32 "\n", lock->flags); 579 580 kprintf(" waiting threads:"); 581 rw_lock_waiter* waiter = lock->waiters; 582 while (waiter != NULL) { 583 kprintf(" %" B_PRId32 "/%c", waiter->thread->id, waiter->writer ? 'w' : 'r'); 584 waiter = waiter->next; 585 } 586 kputs("\n"); 587 588 return 0; 589 } 590 591 592 // #pragma mark - 593 594 595 void 596 mutex_init(mutex* lock, const char *name) 597 { 598 mutex_init_etc(lock, name, 0); 599 } 600 601 602 void 603 mutex_init_etc(mutex* lock, const char *name, uint32 flags) 604 { 605 lock->name = (flags & MUTEX_FLAG_CLONE_NAME) != 0 ? strdup(name) : name; 606 lock->waiters = NULL; 607 B_INITIALIZE_SPINLOCK(&lock->lock); 608 #if KDEBUG 609 lock->holder = -1; 610 #else 611 lock->count = 0; 612 lock->ignore_unlock_count = 0; 613 #endif 614 lock->flags = flags & MUTEX_FLAG_CLONE_NAME; 615 616 T_SCHEDULING_ANALYSIS(InitMutex(lock, name)); 617 NotifyWaitObjectListeners(&WaitObjectListener::MutexInitialized, lock); 618 } 619 620 621 void 622 mutex_destroy(mutex* lock) 623 { 624 char* name = (lock->flags & MUTEX_FLAG_CLONE_NAME) != 0 625 ? (char*)lock->name : NULL; 626 627 // unblock all waiters 628 InterruptsSpinLocker locker(lock->lock); 629 630 #if KDEBUG 631 if (lock->holder != -1 && thread_get_current_thread_id() != lock->holder) { 632 panic("mutex_destroy(): the lock (%p) is held by %" B_PRId32 ", not " 633 "by the caller", lock, lock->holder); 634 if (_mutex_lock(lock, &locker) != B_OK) 635 return; 636 locker.Lock(); 637 } 638 #endif 639 640 while (mutex_waiter* waiter = lock->waiters) { 641 // dequeue 642 lock->waiters = waiter->next; 643 644 // unblock thread 645 thread_unblock(waiter->thread, B_ERROR); 646 } 647 648 lock->name = NULL; 649 lock->flags = 0; 650 #if KDEBUG 651 lock->holder = 0; 652 #else 653 lock->count = INT16_MIN; 654 #endif 655 656 locker.Unlock(); 657 658 free(name); 659 } 660 661 662 static inline status_t 663 mutex_lock_threads_locked(mutex* lock, InterruptsSpinLocker* locker) 664 { 665 #if KDEBUG 666 return _mutex_lock(lock, locker); 667 #else 668 if (atomic_add(&lock->count, -1) < 0) 669 return _mutex_lock(lock, locker); 670 return B_OK; 671 #endif 672 } 673 674 675 status_t 676 mutex_switch_lock(mutex* from, mutex* to) 677 { 678 #if KDEBUG 679 if (!gKernelStartup && !are_interrupts_enabled()) { 680 panic("mutex_switch_lock(): called with interrupts disabled " 681 "for locks %p, %p", from, to); 682 } 683 #endif 684 685 InterruptsSpinLocker locker(to->lock); 686 687 mutex_unlock(from); 688 689 return mutex_lock_threads_locked(to, &locker); 690 } 691 692 693 void 694 mutex_transfer_lock(mutex* lock, thread_id thread) 695 { 696 #if KDEBUG 697 if (thread_get_current_thread_id() != lock->holder) 698 panic("mutex_transfer_lock(): current thread is not the lock holder!"); 699 lock->holder = thread; 700 #endif 701 } 702 703 704 status_t 705 mutex_switch_from_read_lock(rw_lock* from, mutex* to) 706 { 707 #if KDEBUG 708 if (!gKernelStartup && !are_interrupts_enabled()) { 709 panic("mutex_switch_from_read_lock(): called with interrupts disabled " 710 "for locks %p, %p", from, to); 711 } 712 #endif 713 714 InterruptsSpinLocker locker(to->lock); 715 716 #if KDEBUG_RW_LOCK_DEBUG 717 _rw_lock_write_unlock(from); 718 #else 719 int32 oldCount = atomic_add(&from->count, -1); 720 if (oldCount >= RW_LOCK_WRITER_COUNT_BASE) 721 _rw_lock_read_unlock(from); 722 #endif 723 724 return mutex_lock_threads_locked(to, &locker); 725 } 726 727 728 status_t 729 _mutex_lock(mutex* lock, void* _locker) 730 { 731 #if KDEBUG 732 if (!gKernelStartup && _locker == NULL && !are_interrupts_enabled()) { 733 panic("_mutex_lock(): called with interrupts disabled for lock %p", 734 lock); 735 } 736 #endif 737 738 // lock only, if !lockLocked 739 InterruptsSpinLocker* locker 740 = reinterpret_cast<InterruptsSpinLocker*>(_locker); 741 742 InterruptsSpinLocker lockLocker; 743 if (locker == NULL) { 744 lockLocker.SetTo(lock->lock, false); 745 locker = &lockLocker; 746 } 747 748 // Might have been released after we decremented the count, but before 749 // we acquired the spinlock. 750 #if KDEBUG 751 if (lock->holder < 0) { 752 lock->holder = thread_get_current_thread_id(); 753 return B_OK; 754 } else if (lock->holder == thread_get_current_thread_id()) { 755 panic("_mutex_lock(): double lock of %p by thread %" B_PRId32, lock, 756 lock->holder); 757 } else if (lock->holder == 0) 758 panic("_mutex_lock(): using uninitialized lock %p", lock); 759 #else 760 if ((lock->flags & MUTEX_FLAG_RELEASED) != 0) { 761 lock->flags &= ~MUTEX_FLAG_RELEASED; 762 return B_OK; 763 } 764 #endif 765 766 // enqueue in waiter list 767 mutex_waiter waiter; 768 waiter.thread = thread_get_current_thread(); 769 waiter.next = NULL; 770 771 if (lock->waiters != NULL) { 772 lock->waiters->last->next = &waiter; 773 } else 774 lock->waiters = &waiter; 775 776 lock->waiters->last = &waiter; 777 778 // block 779 thread_prepare_to_block(waiter.thread, 0, THREAD_BLOCK_TYPE_MUTEX, lock); 780 locker->Unlock(); 781 782 status_t error = thread_block(); 783 #if KDEBUG 784 if (error == B_OK) 785 atomic_set(&lock->holder, waiter.thread->id); 786 #endif 787 return error; 788 } 789 790 791 void 792 _mutex_unlock(mutex* lock) 793 { 794 InterruptsSpinLocker locker(lock->lock); 795 796 #if KDEBUG 797 if (thread_get_current_thread_id() != lock->holder) { 798 panic("_mutex_unlock() failure: thread %" B_PRId32 " is trying to " 799 "release mutex %p (current holder %" B_PRId32 ")\n", 800 thread_get_current_thread_id(), lock, lock->holder); 801 return; 802 } 803 #else 804 if (lock->ignore_unlock_count > 0) { 805 lock->ignore_unlock_count--; 806 return; 807 } 808 #endif 809 810 mutex_waiter* waiter = lock->waiters; 811 if (waiter != NULL) { 812 // dequeue the first waiter 813 lock->waiters = waiter->next; 814 if (lock->waiters != NULL) 815 lock->waiters->last = waiter->last; 816 #if KDEBUG 817 thread_id unblockedThread = waiter->thread->id; 818 #endif 819 820 // unblock thread 821 thread_unblock(waiter->thread, B_OK); 822 823 #if KDEBUG 824 // Already set the holder to the unblocked thread. Besides that this 825 // actually reflects the current situation, setting it to -1 would 826 // cause a race condition, since another locker could think the lock 827 // is not held by anyone. 828 lock->holder = unblockedThread; 829 #endif 830 } else { 831 // We've acquired the spinlock before the locker that is going to wait. 832 // Just mark the lock as released. 833 #if KDEBUG 834 lock->holder = -1; 835 #else 836 lock->flags |= MUTEX_FLAG_RELEASED; 837 #endif 838 } 839 } 840 841 842 status_t 843 _mutex_trylock(mutex* lock) 844 { 845 #if KDEBUG 846 InterruptsSpinLocker _(lock->lock); 847 848 if (lock->holder < 0) { 849 lock->holder = thread_get_current_thread_id(); 850 return B_OK; 851 } else if (lock->holder == 0) 852 panic("_mutex_trylock(): using uninitialized lock %p", lock); 853 return B_WOULD_BLOCK; 854 #else 855 return mutex_trylock(lock); 856 #endif 857 } 858 859 860 status_t 861 _mutex_lock_with_timeout(mutex* lock, uint32 timeoutFlags, bigtime_t timeout) 862 { 863 #if KDEBUG 864 if (!gKernelStartup && !are_interrupts_enabled()) { 865 panic("_mutex_lock(): called with interrupts disabled for lock %p", 866 lock); 867 } 868 #endif 869 870 InterruptsSpinLocker locker(lock->lock); 871 872 // Might have been released after we decremented the count, but before 873 // we acquired the spinlock. 874 #if KDEBUG 875 if (lock->holder < 0) { 876 lock->holder = thread_get_current_thread_id(); 877 return B_OK; 878 } else if (lock->holder == thread_get_current_thread_id()) { 879 panic("_mutex_lock(): double lock of %p by thread %" B_PRId32, lock, 880 lock->holder); 881 } else if (lock->holder == 0) 882 panic("_mutex_lock(): using uninitialized lock %p", lock); 883 #else 884 if ((lock->flags & MUTEX_FLAG_RELEASED) != 0) { 885 lock->flags &= ~MUTEX_FLAG_RELEASED; 886 return B_OK; 887 } 888 #endif 889 890 // enqueue in waiter list 891 mutex_waiter waiter; 892 waiter.thread = thread_get_current_thread(); 893 waiter.next = NULL; 894 895 if (lock->waiters != NULL) { 896 lock->waiters->last->next = &waiter; 897 } else 898 lock->waiters = &waiter; 899 900 lock->waiters->last = &waiter; 901 902 // block 903 thread_prepare_to_block(waiter.thread, 0, THREAD_BLOCK_TYPE_MUTEX, lock); 904 locker.Unlock(); 905 906 status_t error = thread_block_with_timeout(timeoutFlags, timeout); 907 908 if (error == B_OK) { 909 #if KDEBUG 910 lock->holder = waiter.thread->id; 911 #endif 912 } else { 913 locker.Lock(); 914 915 // If the timeout occurred, we must remove our waiter structure from 916 // the queue. 917 mutex_waiter* previousWaiter = NULL; 918 mutex_waiter* otherWaiter = lock->waiters; 919 while (otherWaiter != NULL && otherWaiter != &waiter) { 920 previousWaiter = otherWaiter; 921 otherWaiter = otherWaiter->next; 922 } 923 if (otherWaiter == &waiter) { 924 // the structure is still in the list -- dequeue 925 if (&waiter == lock->waiters) { 926 if (waiter.next != NULL) 927 waiter.next->last = waiter.last; 928 lock->waiters = waiter.next; 929 } else { 930 if (waiter.next == NULL) 931 lock->waiters->last = previousWaiter; 932 previousWaiter->next = waiter.next; 933 } 934 935 #if !KDEBUG 936 // we need to fix the lock count 937 if (atomic_add(&lock->count, 1) == -1) { 938 // This means we were the only thread waiting for the lock and 939 // the lock owner has already called atomic_add() in 940 // mutex_unlock(). That is we probably would get the lock very 941 // soon (if the lock holder has a low priority, that might 942 // actually take rather long, though), but the timeout already 943 // occurred, so we don't try to wait. Just increment the ignore 944 // unlock count. 945 lock->ignore_unlock_count++; 946 } 947 #endif 948 } 949 } 950 951 return error; 952 } 953 954 955 static int 956 dump_mutex_info(int argc, char** argv) 957 { 958 if (argc < 2) { 959 print_debugger_command_usage(argv[0]); 960 return 0; 961 } 962 963 mutex* lock = (mutex*)parse_expression(argv[1]); 964 965 if (!IS_KERNEL_ADDRESS(lock)) { 966 kprintf("invalid address: %p\n", lock); 967 return 0; 968 } 969 970 kprintf("mutex %p:\n", lock); 971 kprintf(" name: %s\n", lock->name); 972 kprintf(" flags: 0x%x\n", lock->flags); 973 #if KDEBUG 974 kprintf(" holder: %" B_PRId32 "\n", lock->holder); 975 #else 976 kprintf(" count: %" B_PRId32 "\n", lock->count); 977 #endif 978 979 kprintf(" waiting threads:"); 980 mutex_waiter* waiter = lock->waiters; 981 while (waiter != NULL) { 982 kprintf(" %" B_PRId32, waiter->thread->id); 983 waiter = waiter->next; 984 } 985 kputs("\n"); 986 987 return 0; 988 } 989 990 991 // #pragma mark - 992 993 994 void 995 lock_debug_init() 996 { 997 add_debugger_command_etc("mutex", &dump_mutex_info, 998 "Dump info about a mutex", 999 "<mutex>\n" 1000 "Prints info about the specified mutex.\n" 1001 " <mutex> - pointer to the mutex to print the info for.\n", 0); 1002 add_debugger_command_etc("rwlock", &dump_rw_lock_info, 1003 "Dump info about an rw lock", 1004 "<lock>\n" 1005 "Prints info about the specified rw lock.\n" 1006 " <lock> - pointer to the rw lock to print the info for.\n", 0); 1007 } 1008