197dda329SAxel Dörfler/*
224df6592SIngo Weinhold * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
34baa8659SAxel Dörfler * Copyright 2002-2009, Axel D��rfler, axeld@pinc-software.de.
497dda329SAxel Dörfler * Distributed under the terms of the MIT License.
597dda329SAxel Dörfler *
697dda329SAxel Dörfler * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
797dda329SAxel Dörfler * Distributed under the terms of the NewOS License.
897dda329SAxel Dörfler */
952a38012Sejakowatz#ifndef _KERNEL_LOCK_H
1052a38012Sejakowatz#define _KERNEL_LOCK_H
1152a38012Sejakowatz
128cf8e537SPawel Dziepak
13e96a8449SAxel Dörfler#include <OS.h>
148cf8e537SPawel Dziepak
158cf8e537SPawel Dziepak#include <arch/atomic.h>
16bc934681SAxel Dörfler#include <debug.h>
1752a38012Sejakowatz
18b0f5179aSIngo Weinhold
19b0f5179aSIngo Weinholdstruct mutex_waiter;
20b0f5179aSIngo Weinhold
21b0f5179aSIngo Weinholdtypedef struct mutex {
22b0f5179aSIngo Weinhold	const char*				name;
23b0f5179aSIngo Weinhold	struct mutex_waiter*	waiters;
2431a75d40SPawel Dziepak	spinlock				lock;
251894a0a9SIngo Weinhold#if KDEBUG
26b0f5179aSIngo Weinhold	thread_id				holder;
27cf344027SAugustin Cavalier	uint16					_unused;
28b0f5179aSIngo Weinhold#else
29b0f5179aSIngo Weinhold	int32					count;
30e182b46dSIngo Weinhold	uint16					ignore_unlock_count;
31b0f5179aSIngo Weinhold#endif
32b0f5179aSIngo Weinhold	uint8					flags;
33b0f5179aSIngo Weinhold} mutex;
34b0f5179aSIngo Weinhold
35b0f5179aSIngo Weinhold#define MUTEX_FLAG_CLONE_NAME	0x1
36b0f5179aSIngo Weinhold
37b0f5179aSIngo Weinhold
3852a38012Sejakowatztypedef struct recursive_lock {
39b0f5179aSIngo Weinhold	mutex		lock;
401894a0a9SIngo Weinhold#if !KDEBUG
410281fb7bSAxel Dörfler	thread_id	holder;
42cf344027SAugustin Cavalier#else
43cf344027SAugustin Cavalier	int32		_unused;
44b0f5179aSIngo Weinhold#endif
450281fb7bSAxel Dörfler	int			recursion;
4652a38012Sejakowatz} recursive_lock;
4752a38012Sejakowatz
48b0f5179aSIngo Weinhold
491c18a5dfSIngo Weinholdstruct rw_lock_waiter;
501c18a5dfSIngo Weinhold
510281fb7bSAxel Dörflertypedef struct rw_lock {
521c18a5dfSIngo Weinhold	const char*				name;
531c18a5dfSIngo Weinhold	struct rw_lock_waiter*	waiters;
5431a75d40SPawel Dziepak	spinlock				lock;
551c18a5dfSIngo Weinhold	thread_id				holder;
5673ad2473SPawel Dziepak	int32					count;
5778b32dd0SAxel Dörfler	int32					owner_count;
582ea2527fSIngo Weinhold	int16					active_readers;
592ea2527fSIngo Weinhold								// Only > 0 while a writer is waiting: number
602ea2527fSIngo Weinhold								// of active readers when the first waiting
612ea2527fSIngo Weinhold								// writer started waiting.
622ea2527fSIngo Weinhold	int16					pending_readers;
632ea2527fSIngo Weinhold								// Number of readers that have already
642ea2527fSIngo Weinhold								// incremented "count", but have not yet started
652ea2527fSIngo Weinhold								// to wait at the time the last writer unlocked.
661c18a5dfSIngo Weinhold	uint32					flags;
670281fb7bSAxel Dörfler} rw_lock;
680281fb7bSAxel Dörfler
692ea2527fSIngo Weinhold#define RW_LOCK_WRITER_COUNT_BASE	0x10000
702ea2527fSIngo Weinhold
711c18a5dfSIngo Weinhold#define RW_LOCK_FLAG_CLONE_NAME	0x1
721c18a5dfSIngo Weinhold
730281fb7bSAxel Dörfler
745c99d639SIngo Weinhold#if KDEBUG
7537de5a0dSAxel Dörfler#	define KDEBUG_RW_LOCK_DEBUG 0
7637de5a0dSAxel Dörfler		// Define to 1 if you want to use ASSERT_READ_LOCKED_RW_LOCK().
7737de5a0dSAxel Dörfler		// The rw_lock will just behave like a recursive locker then.
785c99d639SIngo Weinhold#	define ASSERT_LOCKED_RECURSIVE(r) \
795c99d639SIngo Weinhold		{ ASSERT(find_thread(NULL) == (r)->lock.holder); }
805c99d639SIngo Weinhold#	define ASSERT_LOCKED_MUTEX(m) { ASSERT(find_thread(NULL) == (m)->holder); }
8137de5a0dSAxel Dörfler#	define ASSERT_WRITE_LOCKED_RW_LOCK(l) \
8237de5a0dSAxel Dörfler		{ ASSERT(find_thread(NULL) == (l)->holder); }
8337de5a0dSAxel Dörfler#	if KDEBUG_RW_LOCK_DEBUG
8437de5a0dSAxel Dörfler#		define ASSERT_READ_LOCKED_RW_LOCK(l) \
8537de5a0dSAxel Dörfler			{ ASSERT(find_thread(NULL) == (l)->holder); }
8637de5a0dSAxel Dörfler#	else
8737de5a0dSAxel Dörfler#		define ASSERT_READ_LOCKED_RW_LOCK(l) do {} while (false)
8837de5a0dSAxel Dörfler#	endif
89c9788912STravis Geiselbrecht#else
9037de5a0dSAxel Dörfler#	define ASSERT_LOCKED_RECURSIVE(r)		do {} while (false)
9137de5a0dSAxel Dörfler#	define ASSERT_LOCKED_MUTEX(m)			do {} while (false)
9237de5a0dSAxel Dörfler#	define ASSERT_WRITE_LOCKED_RW_LOCK(m)	do {} while (false)
9337de5a0dSAxel Dörfler#	define ASSERT_READ_LOCKED_RW_LOCK(l)	do {} while (false)
94c9788912STravis Geiselbrecht#endif
9597dda329SAxel Dörfler
960281fb7bSAxel Dörfler
972daa4194SIngo Weinhold// static initializers
981894a0a9SIngo Weinhold#if KDEBUG
9931a75d40SPawel Dziepak#	define MUTEX_INITIALIZER(name) \
10031a75d40SPawel Dziepak	{ name, NULL, B_SPINLOCK_INITIALIZER, -1, 0 }
1013066f3dbSAxel Dörfler#	define RECURSIVE_LOCK_INITIALIZER(name)	{ MUTEX_INITIALIZER(name), 0 }
1022daa4194SIngo Weinhold#else
10331a75d40SPawel Dziepak#	define MUTEX_INITIALIZER(name) \
10431a75d40SPawel Dziepak	{ name, NULL, B_SPINLOCK_INITIALIZER, 0, 0, 0 }
1053066f3dbSAxel Dörfler#	define RECURSIVE_LOCK_INITIALIZER(name)	{ MUTEX_INITIALIZER(name), -1, 0 }
1062daa4194SIngo Weinhold#endif
1072daa4194SIngo Weinhold
10831a75d40SPawel Dziepak#define RW_LOCK_INITIALIZER(name) \
10986c12bf0SAugustin Cavalier	{ name, NULL, B_SPINLOCK_INITIALIZER, -1, 0, 0, 0, 0, 0 }
1102daa4194SIngo Weinhold
1112daa4194SIngo Weinhold
112a7edf1f7SIngo Weinhold#if KDEBUG
113a7edf1f7SIngo Weinhold#	define RECURSIVE_LOCK_HOLDER(recursiveLock)	((recursiveLock)->lock.holder)
114a7edf1f7SIngo Weinhold#else
115a7edf1f7SIngo Weinhold#	define RECURSIVE_LOCK_HOLDER(recursiveLock)	((recursiveLock)->holder)
116a7edf1f7SIngo Weinhold#endif
117a7edf1f7SIngo Weinhold
118a7edf1f7SIngo Weinhold
1190281fb7bSAxel Dörfler#ifdef __cplusplus
1200281fb7bSAxel Dörflerextern "C" {
1210281fb7bSAxel Dörfler#endif
1220281fb7bSAxel Dörfler
123b0f5179aSIngo Weinholdextern void	recursive_lock_init(recursive_lock *lock, const char *name);
124b0f5179aSIngo Weinhold	// name is *not* cloned nor freed in recursive_lock_destroy()
125b0f5179aSIngo Weinholdextern void recursive_lock_init_etc(recursive_lock *lock, const char *name,
126b0f5179aSIngo Weinhold	uint32 flags);
1270281fb7bSAxel Dörflerextern void recursive_lock_destroy(recursive_lock *lock);
128aa547f5fSAxel Dörflerextern status_t recursive_lock_lock(recursive_lock *lock);
129af370c70SAxel Dörflerextern status_t recursive_lock_trylock(recursive_lock *lock);
130aa547f5fSAxel Dörflerextern void recursive_lock_unlock(recursive_lock *lock);
131aa547f5fSAxel Dörflerextern int32 recursive_lock_get_recursion(recursive_lock *lock);
1320281fb7bSAxel Dörfler
1331c18a5dfSIngo Weinholdextern void rw_lock_init(rw_lock* lock, const char* name);
1341c18a5dfSIngo Weinhold	// name is *not* cloned nor freed in rw_lock_destroy()
1351c18a5dfSIngo Weinholdextern void rw_lock_init_etc(rw_lock* lock, const char* name, uint32 flags);
1361c18a5dfSIngo Weinholdextern void rw_lock_destroy(rw_lock* lock);
1371c18a5dfSIngo Weinholdextern status_t rw_lock_write_lock(rw_lock* lock);
13852a38012Sejakowatz
1391c18a5dfSIngo Weinholdextern void mutex_init(mutex* lock, const char* name);
1400c615a01SIngo Weinhold	// name is *not* cloned nor freed in mutex_destroy()
1411c18a5dfSIngo Weinholdextern void mutex_init_etc(mutex* lock, const char* name, uint32 flags);
1420c615a01SIngo Weinholdextern void mutex_destroy(mutex* lock);
143c2cbf958SAugustin Cavalierextern void mutex_transfer_lock(mutex* lock, thread_id thread);
1444d0fd41dSAugustin Cavalierextern status_t mutex_switch_lock(mutex* from, mutex* to);
1455c99d639SIngo Weinhold	// Unlocks "from" and locks "to" such that unlocking and starting to wait
1465c99d639SIngo Weinhold	// for the lock is atomically. I.e. if "from" guards the object "to" belongs
1475c99d639SIngo Weinhold	// to, the operation is safe as long as "from" is held while destroying
1485c99d639SIngo Weinhold	// "to".
149c4f98312SIngo Weinholdextern status_t mutex_switch_from_read_lock(rw_lock* from, mutex* to);
150c4f98312SIngo Weinhold	// Like mutex_switch_lock(), just for a switching from a read-locked
151c4f98312SIngo Weinhold	// rw_lock.
1528562499fSIngo Weinhold
1532ea2527fSIngo Weinhold
1548562499fSIngo Weinhold// implementation private:
1552ea2527fSIngo Weinhold
1562ea2527fSIngo Weinholdextern status_t _rw_lock_read_lock(rw_lock* lock);
1574e08fb85SIngo Weinholdextern status_t _rw_lock_read_lock_with_timeout(rw_lock* lock,
1584e08fb85SIngo Weinhold	uint32 timeoutFlags, bigtime_t timeout);
15931a75d40SPawel Dziepakextern void _rw_lock_read_unlock(rw_lock* lock);
16031a75d40SPawel Dziepakextern void _rw_lock_write_unlock(rw_lock* lock);
1612ea2527fSIngo Weinhold
16231a75d40SPawel Dziepakextern status_t _mutex_lock(mutex* lock, void* locker);
16331a75d40SPawel Dziepakextern void _mutex_unlock(mutex* lock);
1640c615a01SIngo Weinholdextern status_t _mutex_trylock(mutex* lock);
165e182b46dSIngo Weinholdextern status_t _mutex_lock_with_timeout(mutex* lock, uint32 timeoutFlags,
166e182b46dSIngo Weinhold	bigtime_t timeout);
1670c615a01SIngo Weinhold
1680c615a01SIngo Weinhold
1692ea2527fSIngo Weinholdstatic inline status_t
1702ea2527fSIngo Weinholdrw_lock_read_lock(rw_lock* lock)
1712ea2527fSIngo Weinhold{
1722ea2527fSIngo Weinhold#if KDEBUG_RW_LOCK_DEBUG
1732ea2527fSIngo Weinhold	return rw_lock_write_lock(lock);
1742ea2527fSIngo Weinhold#else
1752ea2527fSIngo Weinhold	int32 oldCount = atomic_add(&lock->count, 1);
1762ea2527fSIngo Weinhold	if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
1772ea2527fSIngo Weinhold		return _rw_lock_read_lock(lock);
1782ea2527fSIngo Weinhold	return B_OK;
1792ea2527fSIngo Weinhold#endif
1802ea2527fSIngo Weinhold}
1812ea2527fSIngo Weinhold
1822ea2527fSIngo Weinhold
1834e08fb85SIngo Weinholdstatic inline status_t
1844e08fb85SIngo Weinholdrw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags,
1854e08fb85SIngo Weinhold	bigtime_t timeout)
1864e08fb85SIngo Weinhold{
1874e08fb85SIngo Weinhold#if KDEBUG_RW_LOCK_DEBUG
1884e08fb85SIngo Weinhold	return mutex_lock_with_timeout(lock, timeoutFlags, timeout);
1894e08fb85SIngo Weinhold#else
1904e08fb85SIngo Weinhold	int32 oldCount = atomic_add(&lock->count, 1);
1914e08fb85SIngo Weinhold	if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
1924e08fb85SIngo Weinhold		return _rw_lock_read_lock_with_timeout(lock, timeoutFlags, timeout);
1934e08fb85SIngo Weinhold	return B_OK;
1944e08fb85SIngo Weinhold#endif
1954e08fb85SIngo Weinhold}
1964e08fb85SIngo Weinhold
1974e08fb85SIngo Weinhold
1982ea2527fSIngo Weinholdstatic inline void
1992ea2527fSIngo Weinholdrw_lock_read_unlock(rw_lock* lock)
2002ea2527fSIngo Weinhold{
2012ea2527fSIngo Weinhold#if KDEBUG_RW_LOCK_DEBUG
2022ea2527fSIngo Weinhold	rw_lock_write_unlock(lock);
2032ea2527fSIngo Weinhold#else
2042ea2527fSIngo Weinhold	int32 oldCount = atomic_add(&lock->count, -1);
2052ea2527fSIngo Weinhold	if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
20631a75d40SPawel Dziepak		_rw_lock_read_unlock(lock);
2072ea2527fSIngo Weinhold#endif
2082ea2527fSIngo Weinhold}
2092ea2527fSIngo Weinhold
2102ea2527fSIngo Weinhold
211c4f98312SIngo Weinholdstatic inline void
212c4f98312SIngo Weinholdrw_lock_write_unlock(rw_lock* lock)
213c4f98312SIngo Weinhold{
21431a75d40SPawel Dziepak	_rw_lock_write_unlock(lock);
215c4f98312SIngo Weinhold}
216c4f98312SIngo Weinhold
217c4f98312SIngo Weinhold
2180c615a01SIngo Weinholdstatic inline status_t
2190c615a01SIngo Weinholdmutex_lock(mutex* lock)
2200c615a01SIngo Weinhold{
2211894a0a9SIngo Weinhold#if KDEBUG
22231a75d40SPawel Dziepak	return _mutex_lock(lock, NULL);
2230c615a01SIngo Weinhold#else
2240c615a01SIngo Weinhold	if (atomic_add(&lock->count, -1) < 0)
22531a75d40SPawel Dziepak		return _mutex_lock(lock, NULL);
226ee96aa8fSIngo Weinhold	return B_OK;
2278562499fSIngo Weinhold#endif
2288562499fSIngo Weinhold}
2298562499fSIngo Weinhold
2308562499fSIngo Weinhold
2318562499fSIngo Weinholdstatic inline status_t
2320c615a01SIngo Weinholdmutex_trylock(mutex* lock)
2338562499fSIngo Weinhold{
2341894a0a9SIngo Weinhold#if KDEBUG
2350c615a01SIngo Weinhold	return _mutex_trylock(lock);
2368562499fSIngo Weinhold#else
2378562499fSIngo Weinhold	if (atomic_test_and_set(&lock->count, -1, 0) != 0)
2388562499fSIngo Weinhold		return B_WOULD_BLOCK;
239ee96aa8fSIngo Weinhold	return B_OK;
2408562499fSIngo Weinhold#endif
2418562499fSIngo Weinhold}
2428562499fSIngo Weinhold
2438562499fSIngo Weinhold
244e182b46dSIngo Weinholdstatic inline status_t
245e182b46dSIngo Weinholdmutex_lock_with_timeout(mutex* lock, uint32 timeoutFlags, bigtime_t timeout)
246e182b46dSIngo Weinhold{
247e182b46dSIngo Weinhold#if KDEBUG
248e182b46dSIngo Weinhold	return _mutex_lock_with_timeout(lock, timeoutFlags, timeout);
249e182b46dSIngo Weinhold#else
250e182b46dSIngo Weinhold	if (atomic_add(&lock->count, -1) < 0)
251e182b46dSIngo Weinhold		return _mutex_lock_with_timeout(lock, timeoutFlags, timeout);
252e182b46dSIngo Weinhold	return B_OK;
253e182b46dSIngo Weinhold#endif
254e182b46dSIngo Weinhold}
255e182b46dSIngo Weinhold
256e182b46dSIngo Weinhold
2578562499fSIngo Weinholdstatic inline void
2580c615a01SIngo Weinholdmutex_unlock(mutex* lock)
2598562499fSIngo Weinhold{
2601894a0a9SIngo Weinhold#if !KDEBUG
2618562499fSIngo Weinhold	if (atomic_add(&lock->count, 1) < -1)
2628562499fSIngo Weinhold#endif
26331a75d40SPawel Dziepak		_mutex_unlock(lock);
2648562499fSIngo Weinhold}
2658562499fSIngo Weinhold
2668562499fSIngo Weinhold
267fd8b9d43SAxel Dörflerstatic inline void
268fd8b9d43SAxel Dörflerrecursive_lock_transfer_lock(recursive_lock* lock, thread_id thread)
269fd8b9d43SAxel Dörfler{
270fd8b9d43SAxel Dörfler	if (lock->recursion != 1)
271fd8b9d43SAxel Dörfler		panic("invalid recursion level for lock transfer!");
272fd8b9d43SAxel Dörfler
273fd8b9d43SAxel Dörfler#if KDEBUG
274c2cbf958SAugustin Cavalier	mutex_transfer_lock(&lock->lock, thread);
275fd8b9d43SAxel Dörfler#else
276fd8b9d43SAxel Dörfler	lock->holder = thread;
277fd8b9d43SAxel Dörfler#endif
278fd8b9d43SAxel Dörfler}
279fd8b9d43SAxel Dörfler
280fd8b9d43SAxel Dörfler
2818562499fSIngo Weinholdextern void lock_debug_init();
2828562499fSIngo Weinhold
2830281fb7bSAxel Dörfler#ifdef __cplusplus
2840281fb7bSAxel Dörfler}
2850281fb7bSAxel Dörfler#endif
28652a38012Sejakowatz
287bc934681SAxel Dörfler#endif	/* _KERNEL_LOCK_H */
288