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