1/*
2 * Copyright 2002-2010, Haiku Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 *		Ingo Weinhold, bonefish@cs.tu-berlin.de.
7 *		Axel D��rfler, axeld@pinc-software.de.
8 */
9
10#include <lock.h>
11
12#include <stdlib.h>
13#include <string.h>
14
15#include <AutoLocker.h>
16
17// libroot
18#include <user_thread.h>
19
20// system
21#include <syscalls.h>
22#include <user_thread_defs.h>
23
24#include "thread.h"
25
26
27struct mutex_waiter {
28	Thread*			thread;
29	mutex_waiter*	next;		// next in queue
30	mutex_waiter*	last;		// last in queue (valid for the first in queue)
31};
32
33struct rw_lock_waiter {
34	Thread*			thread;
35	rw_lock_waiter*	next;		// next in queue
36	rw_lock_waiter*	last;		// last in queue (valid for the first in queue)
37	bool			writer;
38};
39
40#define MUTEX_FLAG_OWNS_NAME	MUTEX_FLAG_CLONE_NAME
41#define MUTEX_FLAG_RELEASED		0x2
42
43#define RW_LOCK_FLAG_OWNS_NAME	RW_LOCK_FLAG_CLONE_NAME
44
45
46static void _rw_lock_read_unlock_threads_locked(rw_lock* lock);
47static void _rw_lock_write_unlock_threads_locked(rw_lock* lock);
48
49static status_t _mutex_lock_threads_locked(mutex* lock);
50static void _mutex_unlock_threads_locked(mutex* lock);
51
52
53/*!	Helper class playing the role of the kernel's thread spinlock. We don't use
54	as spinlock as that could be expensive in userland (due to spinlock holder
55	potentially being unscheduled), but a benaphore.
56*/
57struct ThreadSpinlock {
58	ThreadSpinlock()
59		:
60		fCount(1),
61		fSemaphore(create_sem(0, "thread spinlock"))
62	{
63		if (fSemaphore < 0)
64			panic("Failed to create thread spinlock semaphore!");
65	}
66
67	~ThreadSpinlock()
68	{
69		if (fSemaphore >= 0)
70			delete_sem(fSemaphore);
71	}
72
73	bool Lock()
74	{
75		if (atomic_add(&fCount, -1) > 0)
76			return true;
77
78		status_t error;
79		do {
80			error = acquire_sem(fSemaphore);
81		} while (error == B_INTERRUPTED);
82
83		return error == B_OK;
84	}
85
86	void Unlock()
87	{
88		if (atomic_add(&fCount, 1) < 0)
89			release_sem(fSemaphore);
90	}
91
92private:
93	int32	fCount;
94	sem_id	fSemaphore;
95};
96
97static ThreadSpinlock sThreadSpinlock;
98
99
100// #pragma mark -
101
102
103int32
104recursive_lock_get_recursion(recursive_lock *lock)
105{
106	if (RECURSIVE_LOCK_HOLDER(lock) == find_thread(NULL))
107		return lock->recursion;
108
109	return -1;
110}
111
112
113void
114recursive_lock_init(recursive_lock *lock, const char *name)
115{
116	mutex_init(&lock->lock, name != NULL ? name : "recursive lock");
117	RECURSIVE_LOCK_HOLDER(lock) = -1;
118	lock->recursion = 0;
119}
120
121
122void
123recursive_lock_init_etc(recursive_lock *lock, const char *name, uint32 flags)
124{
125	mutex_init_etc(&lock->lock, name != NULL ? name : "recursive lock", flags);
126	RECURSIVE_LOCK_HOLDER(lock) = -1;
127	lock->recursion = 0;
128}
129
130
131void
132recursive_lock_destroy(recursive_lock *lock)
133{
134	if (lock == NULL)
135		return;
136
137	mutex_destroy(&lock->lock);
138}
139
140
141status_t
142recursive_lock_lock(recursive_lock *lock)
143{
144	thread_id thread = find_thread(NULL);
145
146	if (thread != RECURSIVE_LOCK_HOLDER(lock)) {
147		mutex_lock(&lock->lock);
148#if !KDEBUG
149		lock->holder = thread;
150#endif
151	}
152
153	lock->recursion++;
154	return B_OK;
155}
156
157
158status_t
159recursive_lock_trylock(recursive_lock *lock)
160{
161	thread_id thread = find_thread(NULL);
162
163	if (thread != RECURSIVE_LOCK_HOLDER(lock)) {
164		status_t status = mutex_trylock(&lock->lock);
165		if (status != B_OK)
166			return status;
167
168#if !KDEBUG
169		lock->holder = thread;
170#endif
171	}
172
173	lock->recursion++;
174	return B_OK;
175}
176
177
178void
179recursive_lock_unlock(recursive_lock *lock)
180{
181	if (find_thread(NULL) != RECURSIVE_LOCK_HOLDER(lock))
182		panic("recursive_lock %p unlocked by non-holder thread!\n", lock);
183
184	if (--lock->recursion == 0) {
185#if !KDEBUG
186		lock->holder = -1;
187#endif
188		mutex_unlock(&lock->lock);
189	}
190}
191
192
193//	#pragma mark -
194
195
196static status_t
197rw_lock_wait(rw_lock* lock, bool writer)
198{
199	// enqueue in waiter list
200	rw_lock_waiter waiter;
201	waiter.thread = get_current_thread();
202	waiter.next = NULL;
203	waiter.writer = writer;
204
205	if (lock->waiters != NULL)
206		lock->waiters->last->next = &waiter;
207	else
208		lock->waiters = &waiter;
209
210	lock->waiters->last = &waiter;
211
212	// block
213	get_user_thread()->wait_status = 1;
214	sThreadSpinlock.Unlock();
215
216	status_t error;
217	while ((error = _kern_block_thread(0, 0)) == B_INTERRUPTED) {
218	}
219
220	sThreadSpinlock.Lock();
221	return error;
222}
223
224
225static int32
226rw_lock_unblock(rw_lock* lock)
227{
228	// Check whether there any waiting threads at all and whether anyone
229	// has the write lock.
230	rw_lock_waiter* waiter = lock->waiters;
231	if (waiter == NULL || lock->holder > 0)
232		return 0;
233
234	// writer at head of queue?
235	if (waiter->writer) {
236		if (lock->active_readers > 0 || lock->pending_readers > 0)
237			return 0;
238
239		// dequeue writer
240		lock->waiters = waiter->next;
241		if (lock->waiters != NULL)
242			lock->waiters->last = waiter->last;
243
244		lock->holder = get_thread_id(waiter->thread);
245
246		// unblock thread
247		_kern_unblock_thread(get_thread_id(waiter->thread), B_OK);
248		waiter->thread = NULL;
249		return RW_LOCK_WRITER_COUNT_BASE;
250	}
251
252	// wake up one or more readers
253	uint32 readerCount = 0;
254	do {
255		// dequeue reader
256		lock->waiters = waiter->next;
257		if (lock->waiters != NULL)
258			lock->waiters->last = waiter->last;
259
260		readerCount++;
261
262		// unblock thread
263		_kern_unblock_thread(get_thread_id(waiter->thread), B_OK);
264		waiter->thread = NULL;
265	} while ((waiter = lock->waiters) != NULL && !waiter->writer);
266
267	if (lock->count >= RW_LOCK_WRITER_COUNT_BASE)
268		lock->active_readers += readerCount;
269
270	return readerCount;
271}
272
273
274void
275rw_lock_init(rw_lock* lock, const char* name)
276{
277	lock->name = name;
278	lock->waiters = NULL;
279	lock->holder = -1;
280	lock->count = 0;
281	lock->owner_count = 0;
282	lock->active_readers = 0;
283	lock->pending_readers = 0;
284	lock->flags = 0;
285}
286
287
288void
289rw_lock_init_etc(rw_lock* lock, const char* name, uint32 flags)
290{
291	lock->name = (flags & RW_LOCK_FLAG_CLONE_NAME) != 0 ? strdup(name) : name;
292	lock->waiters = NULL;
293	lock->holder = -1;
294	lock->count = 0;
295	lock->owner_count = 0;
296	lock->active_readers = 0;
297	lock->pending_readers = 0;
298	lock->flags = flags & RW_LOCK_FLAG_CLONE_NAME;
299}
300
301
302void
303rw_lock_destroy(rw_lock* lock)
304{
305	char* name = (lock->flags & RW_LOCK_FLAG_CLONE_NAME) != 0
306		? (char*)lock->name : NULL;
307
308	// unblock all waiters
309	AutoLocker<ThreadSpinlock> locker(sThreadSpinlock);
310
311#if KDEBUG
312	if (lock->waiters != NULL && find_thread(NULL)
313			!= lock->holder) {
314		panic("rw_lock_destroy(): there are blocking threads, but the caller "
315			"doesn't hold the write lock (%p)", lock);
316
317		locker.Unlock();
318		if (rw_lock_write_lock(lock) != B_OK)
319			return;
320		locker.Lock();
321	}
322#endif
323
324	while (rw_lock_waiter* waiter = lock->waiters) {
325		// dequeue
326		lock->waiters = waiter->next;
327
328		// unblock thread
329		_kern_unblock_thread(get_thread_id(waiter->thread), B_ERROR);
330	}
331
332	lock->name = NULL;
333
334	locker.Unlock();
335
336	free(name);
337}
338
339
340#if !KDEBUG_RW_LOCK_DEBUG
341
342status_t
343_rw_lock_read_lock(rw_lock* lock)
344{
345	AutoLocker<ThreadSpinlock> locker(sThreadSpinlock);
346
347	// We might be the writer ourselves.
348	if (lock->holder == find_thread(NULL)) {
349		lock->owner_count++;
350		return B_OK;
351	}
352
353	// The writer that originally had the lock when we called atomic_add() might
354	// already have gone and another writer could have overtaken us. In this
355	// case the original writer set pending_readers, so we know that we don't
356	// have to wait.
357	if (lock->pending_readers > 0) {
358		lock->pending_readers--;
359
360		if (lock->count >= RW_LOCK_WRITER_COUNT_BASE)
361			lock->active_readers++;
362
363		return B_OK;
364	}
365
366	// we need to wait
367	return rw_lock_wait(lock, false);
368}
369
370
371status_t
372_rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags,
373	bigtime_t timeout)
374{
375	AutoLocker<ThreadSpinlock> locker(sThreadSpinlock);
376
377	// We might be the writer ourselves.
378	if (lock->holder == find_thread(NULL)) {
379		lock->owner_count++;
380		return B_OK;
381	}
382
383	// The writer that originally had the lock when we called atomic_add() might
384	// already have gone and another writer could have overtaken us. In this
385	// case the original writer set pending_readers, so we know that we don't
386	// have to wait.
387	if (lock->pending_readers > 0) {
388		lock->pending_readers--;
389
390		if (lock->count >= RW_LOCK_WRITER_COUNT_BASE)
391			lock->active_readers++;
392
393		return B_OK;
394	}
395
396	ASSERT(lock->count >= RW_LOCK_WRITER_COUNT_BASE);
397
398	// we need to wait
399
400	// enqueue in waiter list
401	rw_lock_waiter waiter;
402	waiter.thread = get_current_thread();
403	waiter.next = NULL;
404	waiter.writer = false;
405
406	if (lock->waiters != NULL)
407		lock->waiters->last->next = &waiter;
408	else
409		lock->waiters = &waiter;
410
411	lock->waiters->last = &waiter;
412
413	// block
414	get_user_thread()->wait_status = 1;
415	sThreadSpinlock.Unlock();
416
417	status_t error;
418	while ((error = _kern_block_thread(timeoutFlags, timeout))
419			== B_INTERRUPTED) {
420	}
421
422	sThreadSpinlock.Lock();
423
424	if (error == B_OK || waiter.thread == NULL) {
425		// We were unblocked successfully -- potentially our unblocker overtook
426		// us after we already failed. In either case, we've got the lock, now.
427		return B_OK;
428	}
429
430	// We failed to get the lock -- dequeue from waiter list.
431	rw_lock_waiter* previous = NULL;
432	rw_lock_waiter* other = lock->waiters;
433	while (other != &waiter) {
434		previous = other;
435		other = other->next;
436	}
437
438	if (previous == NULL) {
439		// we are the first in line
440		lock->waiters = waiter.next;
441		if (lock->waiters != NULL)
442			lock->waiters->last = waiter.last;
443	} else {
444		// one or more other waiters are before us in the queue
445		previous->next = waiter.next;
446		if (lock->waiters->last == &waiter)
447			lock->waiters->last = previous;
448	}
449
450	// Decrement the count. ATM this is all we have to do. There's at least
451	// one writer ahead of us -- otherwise the last writer would have unblocked
452	// us (writers only manipulate the lock data with thread spinlock being
453	// held) -- so our leaving doesn't make a difference to the ones behind us
454	// in the queue.
455	atomic_add(&lock->count, -1);
456
457	return error;
458}
459
460
461void
462_rw_lock_read_unlock(rw_lock* lock)
463{
464	AutoLocker<ThreadSpinlock> locker(sThreadSpinlock);
465	_rw_lock_read_unlock_threads_locked(lock);
466}
467
468
469static void
470_rw_lock_read_unlock_threads_locked(rw_lock* lock)
471{
472	// If we're still holding the write lock or if there are other readers,
473	// no-one can be woken up.
474	if (lock->holder == find_thread(NULL)) {
475		lock->owner_count--;
476		return;
477	}
478
479	if (--lock->active_readers > 0)
480		return;
481
482	if (lock->active_readers < 0) {
483		panic("rw_lock_read_unlock(): lock %p not read-locked", lock);
484		lock->active_readers = 0;
485		return;
486	}
487
488	rw_lock_unblock(lock);
489}
490
491#endif	// !KDEBUG_RW_LOCK_DEBUG
492
493
494status_t
495rw_lock_write_lock(rw_lock* lock)
496{
497	AutoLocker<ThreadSpinlock> locker(sThreadSpinlock);
498
499	// If we're already the lock holder, we just need to increment the owner
500	// count.
501	thread_id thread = find_thread(NULL);
502	if (lock->holder == thread) {
503		lock->owner_count += RW_LOCK_WRITER_COUNT_BASE;
504		return B_OK;
505	}
506
507	// announce our claim
508	int32 oldCount = atomic_add(&lock->count, RW_LOCK_WRITER_COUNT_BASE);
509
510	if (oldCount == 0) {
511		// No-one else held a read or write lock, so it's ours now.
512		lock->holder = thread;
513		lock->owner_count = RW_LOCK_WRITER_COUNT_BASE;
514		return B_OK;
515	}
516
517	// We have to wait. If we're the first writer, note the current reader
518	// count.
519	if (oldCount < RW_LOCK_WRITER_COUNT_BASE)
520		lock->active_readers = oldCount - lock->pending_readers;
521
522	status_t status = rw_lock_wait(lock, true);
523	if (status == B_OK) {
524		lock->holder = thread;
525		lock->owner_count = RW_LOCK_WRITER_COUNT_BASE;
526	}
527
528	return status;
529}
530
531
532void
533_rw_lock_write_unlock(rw_lock* lock)
534{
535	AutoLocker<ThreadSpinlock> locker(sThreadSpinlock);
536	_rw_lock_write_unlock_threads_locked(lock);
537}
538
539
540static void
541_rw_lock_write_unlock_threads_locked(rw_lock* lock)
542{
543	if (find_thread(NULL) != lock->holder) {
544		panic("rw_lock_write_unlock(): lock %p not write-locked by this thread",
545			lock);
546		return;
547	}
548
549	lock->owner_count -= RW_LOCK_WRITER_COUNT_BASE;
550	if (lock->owner_count >= RW_LOCK_WRITER_COUNT_BASE)
551		return;
552
553	// We gave up our last write lock -- clean up and unblock waiters.
554	int32 readerCount = lock->owner_count;
555	lock->holder = -1;
556	lock->owner_count = 0;
557
558	int32 oldCount = atomic_add(&lock->count, -RW_LOCK_WRITER_COUNT_BASE);
559	oldCount -= RW_LOCK_WRITER_COUNT_BASE;
560
561	if (oldCount != 0) {
562		// If writers are waiting, take over our reader count.
563		if (oldCount >= RW_LOCK_WRITER_COUNT_BASE) {
564			lock->active_readers = readerCount;
565			rw_lock_unblock(lock);
566		} else {
567			// No waiting writer, but there are one or more readers. We will
568			// unblock all waiting readers -- that's the easy part -- and must
569			// also make sure that all readers that haven't entered the critical
570			// section yet, won't start to wait. Otherwise a writer overtaking
571			// such a reader will correctly start to wait, but the reader,
572			// seeing the writer count > 0, would also start to wait. We set
573			// pending_readers to the number of readers that are still expected
574			// to enter the critical section.
575			lock->pending_readers = oldCount - readerCount
576				- rw_lock_unblock(lock);
577		}
578	}
579}
580
581
582// #pragma mark -
583
584
585void
586mutex_init(mutex* lock, const char *name)
587{
588	lock->name = name;
589	lock->waiters = NULL;
590#if KDEBUG
591	lock->holder = -1;
592#else
593	lock->count = 0;
594#endif
595	lock->flags = 0;
596}
597
598
599void
600mutex_init_etc(mutex* lock, const char *name, uint32 flags)
601{
602	lock->name = (flags & MUTEX_FLAG_CLONE_NAME) != 0 ? strdup(name) : name;
603	lock->waiters = NULL;
604#if KDEBUG
605	lock->holder = -1;
606#else
607	lock->count = 0;
608#endif
609	lock->flags = flags & MUTEX_FLAG_CLONE_NAME;
610}
611
612
613void
614mutex_destroy(mutex* lock)
615{
616	char* name = (lock->flags & MUTEX_FLAG_CLONE_NAME) != 0
617		? (char*)lock->name : NULL;
618
619	// unblock all waiters
620	AutoLocker<ThreadSpinlock> locker(sThreadSpinlock);
621
622#if KDEBUG
623	if (lock->waiters != NULL && find_thread(NULL)
624		!= lock->holder) {
625		panic("mutex_destroy(): there are blocking threads, but caller doesn't "
626			"hold the lock (%p)", lock);
627		if (_mutex_lock_threads_locked(lock) != B_OK)
628			return;
629	}
630#endif
631
632	while (mutex_waiter* waiter = lock->waiters) {
633		// dequeue
634		lock->waiters = waiter->next;
635
636		// unblock thread
637		_kern_unblock_thread(get_thread_id(waiter->thread), B_ERROR);
638	}
639
640	lock->name = NULL;
641
642	locker.Unlock();
643
644	free(name);
645}
646
647
648status_t
649mutex_switch_lock(mutex* from, mutex* to)
650{
651	AutoLocker<ThreadSpinlock> locker(sThreadSpinlock);
652
653#if !KDEBUG
654	if (atomic_add(&from->count, 1) < -1)
655#endif
656		_mutex_unlock_threads_locked(from);
657
658	return _mutex_lock_threads_locked(to);
659}
660
661
662status_t
663mutex_switch_from_read_lock(rw_lock* from, mutex* to)
664{
665	AutoLocker<ThreadSpinlock> locker(sThreadSpinlock);
666
667#if KDEBUG_RW_LOCK_DEBUG
668	_rw_lock_write_unlock_threads_locked(from);
669#else
670	int32 oldCount = atomic_add(&from->count, -1);
671	if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
672		_rw_lock_read_unlock_threads_locked(from);
673#endif
674
675	return _mutex_lock_threads_locked(to);
676}
677
678
679
680static status_t
681_mutex_lock_threads_locked(mutex* lock)
682{
683
684	// Might have been released after we decremented the count, but before
685	// we acquired the spinlock.
686#if KDEBUG
687	if (lock->holder < 0) {
688		lock->holder = find_thread(NULL);
689		return B_OK;
690	} else if (lock->holder == find_thread(NULL)) {
691		panic("_mutex_lock(): double lock of %p by thread %" B_PRId32, lock,
692			lock->holder);
693	} else if (lock->holder == 0)
694		panic("_mutex_lock(): using unitialized lock %p", lock);
695#else
696	if ((lock->flags & MUTEX_FLAG_RELEASED) != 0) {
697		lock->flags &= ~MUTEX_FLAG_RELEASED;
698		return B_OK;
699	}
700#endif
701
702	// enqueue in waiter list
703	mutex_waiter waiter;
704	waiter.thread = get_current_thread();
705	waiter.next = NULL;
706
707	if (lock->waiters != NULL) {
708		lock->waiters->last->next = &waiter;
709	} else
710		lock->waiters = &waiter;
711
712	lock->waiters->last = &waiter;
713
714	// block
715	get_user_thread()->wait_status = 1;
716	sThreadSpinlock.Unlock();
717
718	status_t error;
719	while ((error = _kern_block_thread(0, 0)) == B_INTERRUPTED) {
720	}
721
722	sThreadSpinlock.Lock();
723
724#if KDEBUG
725	if (error == B_OK)
726		lock->holder = get_thread_id(waiter.thread);
727#endif
728
729	return error;
730}
731
732
733status_t
734_mutex_lock(mutex* lock, void*)
735{
736	AutoLocker<ThreadSpinlock> locker(sThreadSpinlock);
737	return _mutex_lock_threads_locked(lock);
738}
739
740
741static void
742_mutex_unlock_threads_locked(mutex* lock)
743{
744#if KDEBUG
745	if (find_thread(NULL) != lock->holder) {
746		panic("_mutex_unlock() failure: thread %" B_PRId32 " is trying to "
747			"release mutex %p (current holder %" B_PRId32 ")\n",
748			find_thread(NULL), lock, lock->holder);
749		return;
750	}
751#endif
752
753	mutex_waiter* waiter = lock->waiters;
754	if (waiter != NULL) {
755		// dequeue the first waiter
756		lock->waiters = waiter->next;
757		if (lock->waiters != NULL)
758			lock->waiters->last = waiter->last;
759
760		// unblock thread
761		_kern_unblock_thread(get_thread_id(waiter->thread), B_OK);
762
763#if KDEBUG
764		// Already set the holder to the unblocked thread. Besides that this
765		// actually reflects the current situation, setting it to -1 would
766		// cause a race condition, since another locker could think the lock
767		// is not held by anyone.
768		lock->holder = get_thread_id(waiter->thread);
769#endif
770	} else {
771		// We've acquired the spinlock before the locker that is going to wait.
772		// Just mark the lock as released.
773#if KDEBUG
774		lock->holder = -1;
775#else
776		lock->flags |= MUTEX_FLAG_RELEASED;
777#endif
778	}
779}
780
781
782void
783_mutex_unlock(mutex* lock)
784{
785	AutoLocker<ThreadSpinlock> locker(sThreadSpinlock);
786	_mutex_unlock_threads_locked(lock);
787}
788
789
790status_t
791_mutex_trylock(mutex* lock)
792{
793#if KDEBUG
794	AutoLocker<ThreadSpinlock> _(sThreadSpinlock);
795
796	if (lock->holder <= 0) {
797		lock->holder = find_thread(NULL);
798		return B_OK;
799	}
800#endif
801	return B_WOULD_BLOCK;
802}
803