1/*
2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2002-2009, Axel D��rfler, axeld@pinc-software.de.
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#ifndef _KERNEL_LOCK_H
10#define _KERNEL_LOCK_H
11
12
13#include <OS.h>
14
15#include <arch/atomic.h>
16#include <debug.h>
17
18
19struct mutex_waiter;
20
21typedef struct mutex {
22	const char*				name;
23	struct mutex_waiter*	waiters;
24	spinlock				lock;
25#if KDEBUG
26	thread_id				holder;
27	uint16					_unused;
28#else
29	int32					count;
30	uint16					ignore_unlock_count;
31#endif
32	uint8					flags;
33} mutex;
34
35#define MUTEX_FLAG_CLONE_NAME	0x1
36
37
38typedef struct recursive_lock {
39	mutex		lock;
40#if !KDEBUG
41	thread_id	holder;
42#else
43	int32		_unused;
44#endif
45	int			recursion;
46} recursive_lock;
47
48
49struct rw_lock_waiter;
50
51typedef struct rw_lock {
52	const char*				name;
53	struct rw_lock_waiter*	waiters;
54	spinlock				lock;
55	thread_id				holder;
56	int32					count;
57	int32					owner_count;
58	int16					active_readers;
59								// Only > 0 while a writer is waiting: number
60								// of active readers when the first waiting
61								// writer started waiting.
62	int16					pending_readers;
63								// Number of readers that have already
64								// incremented "count", but have not yet started
65								// to wait at the time the last writer unlocked.
66	uint32					flags;
67} rw_lock;
68
69#define RW_LOCK_WRITER_COUNT_BASE	0x10000
70
71#define RW_LOCK_FLAG_CLONE_NAME	0x1
72
73
74#if KDEBUG
75#	define KDEBUG_RW_LOCK_DEBUG 0
76		// Define to 1 if you want to use ASSERT_READ_LOCKED_RW_LOCK().
77		// The rw_lock will just behave like a recursive locker then.
78#	define ASSERT_LOCKED_RECURSIVE(r) \
79		{ ASSERT(find_thread(NULL) == (r)->lock.holder); }
80#	define ASSERT_LOCKED_MUTEX(m) { ASSERT(find_thread(NULL) == (m)->holder); }
81#	define ASSERT_WRITE_LOCKED_RW_LOCK(l) \
82		{ ASSERT(find_thread(NULL) == (l)->holder); }
83#	if KDEBUG_RW_LOCK_DEBUG
84#		define ASSERT_READ_LOCKED_RW_LOCK(l) \
85			{ ASSERT(find_thread(NULL) == (l)->holder); }
86#	else
87#		define ASSERT_READ_LOCKED_RW_LOCK(l) do {} while (false)
88#	endif
89#else
90#	define ASSERT_LOCKED_RECURSIVE(r)		do {} while (false)
91#	define ASSERT_LOCKED_MUTEX(m)			do {} while (false)
92#	define ASSERT_WRITE_LOCKED_RW_LOCK(m)	do {} while (false)
93#	define ASSERT_READ_LOCKED_RW_LOCK(l)	do {} while (false)
94#endif
95
96
97// static initializers
98#if KDEBUG
99#	define MUTEX_INITIALIZER(name) \
100	{ name, NULL, B_SPINLOCK_INITIALIZER, -1, 0 }
101#	define RECURSIVE_LOCK_INITIALIZER(name)	{ MUTEX_INITIALIZER(name), 0 }
102#else
103#	define MUTEX_INITIALIZER(name) \
104	{ name, NULL, B_SPINLOCK_INITIALIZER, 0, 0, 0 }
105#	define RECURSIVE_LOCK_INITIALIZER(name)	{ MUTEX_INITIALIZER(name), -1, 0 }
106#endif
107
108#define RW_LOCK_INITIALIZER(name) \
109	{ name, NULL, B_SPINLOCK_INITIALIZER, -1, 0, 0, 0, 0, 0 }
110
111
112#if KDEBUG
113#	define RECURSIVE_LOCK_HOLDER(recursiveLock)	((recursiveLock)->lock.holder)
114#else
115#	define RECURSIVE_LOCK_HOLDER(recursiveLock)	((recursiveLock)->holder)
116#endif
117
118
119#ifdef __cplusplus
120extern "C" {
121#endif
122
123extern void	recursive_lock_init(recursive_lock *lock, const char *name);
124	// name is *not* cloned nor freed in recursive_lock_destroy()
125extern void recursive_lock_init_etc(recursive_lock *lock, const char *name,
126	uint32 flags);
127extern void recursive_lock_destroy(recursive_lock *lock);
128extern status_t recursive_lock_lock(recursive_lock *lock);
129extern status_t recursive_lock_trylock(recursive_lock *lock);
130extern void recursive_lock_unlock(recursive_lock *lock);
131extern int32 recursive_lock_get_recursion(recursive_lock *lock);
132
133extern void rw_lock_init(rw_lock* lock, const char* name);
134	// name is *not* cloned nor freed in rw_lock_destroy()
135extern void rw_lock_init_etc(rw_lock* lock, const char* name, uint32 flags);
136extern void rw_lock_destroy(rw_lock* lock);
137extern status_t rw_lock_write_lock(rw_lock* lock);
138
139extern void mutex_init(mutex* lock, const char* name);
140	// name is *not* cloned nor freed in mutex_destroy()
141extern void mutex_init_etc(mutex* lock, const char* name, uint32 flags);
142extern void mutex_destroy(mutex* lock);
143extern void mutex_transfer_lock(mutex* lock, thread_id thread);
144extern status_t mutex_switch_lock(mutex* from, mutex* to);
145	// Unlocks "from" and locks "to" such that unlocking and starting to wait
146	// for the lock is atomically. I.e. if "from" guards the object "to" belongs
147	// to, the operation is safe as long as "from" is held while destroying
148	// "to".
149extern status_t mutex_switch_from_read_lock(rw_lock* from, mutex* to);
150	// Like mutex_switch_lock(), just for a switching from a read-locked
151	// rw_lock.
152
153
154// implementation private:
155
156extern status_t _rw_lock_read_lock(rw_lock* lock);
157extern status_t _rw_lock_read_lock_with_timeout(rw_lock* lock,
158	uint32 timeoutFlags, bigtime_t timeout);
159extern void _rw_lock_read_unlock(rw_lock* lock);
160extern void _rw_lock_write_unlock(rw_lock* lock);
161
162extern status_t _mutex_lock(mutex* lock, void* locker);
163extern void _mutex_unlock(mutex* lock);
164extern status_t _mutex_trylock(mutex* lock);
165extern status_t _mutex_lock_with_timeout(mutex* lock, uint32 timeoutFlags,
166	bigtime_t timeout);
167
168
169static inline status_t
170rw_lock_read_lock(rw_lock* lock)
171{
172#if KDEBUG_RW_LOCK_DEBUG
173	return rw_lock_write_lock(lock);
174#else
175	int32 oldCount = atomic_add(&lock->count, 1);
176	if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
177		return _rw_lock_read_lock(lock);
178	return B_OK;
179#endif
180}
181
182
183static inline status_t
184rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags,
185	bigtime_t timeout)
186{
187#if KDEBUG_RW_LOCK_DEBUG
188	return mutex_lock_with_timeout(lock, timeoutFlags, timeout);
189#else
190	int32 oldCount = atomic_add(&lock->count, 1);
191	if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
192		return _rw_lock_read_lock_with_timeout(lock, timeoutFlags, timeout);
193	return B_OK;
194#endif
195}
196
197
198static inline void
199rw_lock_read_unlock(rw_lock* lock)
200{
201#if KDEBUG_RW_LOCK_DEBUG
202	rw_lock_write_unlock(lock);
203#else
204	int32 oldCount = atomic_add(&lock->count, -1);
205	if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
206		_rw_lock_read_unlock(lock);
207#endif
208}
209
210
211static inline void
212rw_lock_write_unlock(rw_lock* lock)
213{
214	_rw_lock_write_unlock(lock);
215}
216
217
218static inline status_t
219mutex_lock(mutex* lock)
220{
221#if KDEBUG
222	return _mutex_lock(lock, NULL);
223#else
224	if (atomic_add(&lock->count, -1) < 0)
225		return _mutex_lock(lock, NULL);
226	return B_OK;
227#endif
228}
229
230
231static inline status_t
232mutex_trylock(mutex* lock)
233{
234#if KDEBUG
235	return _mutex_trylock(lock);
236#else
237	if (atomic_test_and_set(&lock->count, -1, 0) != 0)
238		return B_WOULD_BLOCK;
239	return B_OK;
240#endif
241}
242
243
244static inline status_t
245mutex_lock_with_timeout(mutex* lock, uint32 timeoutFlags, bigtime_t timeout)
246{
247#if KDEBUG
248	return _mutex_lock_with_timeout(lock, timeoutFlags, timeout);
249#else
250	if (atomic_add(&lock->count, -1) < 0)
251		return _mutex_lock_with_timeout(lock, timeoutFlags, timeout);
252	return B_OK;
253#endif
254}
255
256
257static inline void
258mutex_unlock(mutex* lock)
259{
260#if !KDEBUG
261	if (atomic_add(&lock->count, 1) < -1)
262#endif
263		_mutex_unlock(lock);
264}
265
266
267static inline void
268recursive_lock_transfer_lock(recursive_lock* lock, thread_id thread)
269{
270	if (lock->recursion != 1)
271		panic("invalid recursion level for lock transfer!");
272
273#if KDEBUG
274	mutex_transfer_lock(&lock->lock, thread);
275#else
276	lock->holder = thread;
277#endif
278}
279
280
281extern void lock_debug_init();
282
283#ifdef __cplusplus
284}
285#endif
286
287#endif	/* _KERNEL_LOCK_H */
288