1#ifndef LOCK_H
2#define LOCK_H
3/* Lock - simple semaphores, read/write lock implementation
4**
5** Initial version by Axel D��rfler, axeld@pinc-software.de
6** Roughly based on a Be sample code written by Nathan Schrenk.
7**
8** This file may be used under the terms of the OpenBeOS License.
9*/
10
11
12#include <KernelExport.h>
13#include <stdio.h>
14#include "Utility.h"
15#include "Debug.h"
16
17
18// Configure here if and when real benaphores should be used
19#define USE_BENAPHORE
20	// if defined, benaphores are used for the Semaphore/RecursiveLock classes
21#ifdef USER
22//#	define FAST_LOCK
23	// the ReadWriteLock class uses a second Semaphore to
24	// speed up locking - only makes sense if USE_BENAPHORE
25	// is defined, too.
26#endif
27
28
29class Semaphore {
30	public:
31		Semaphore(const char *name)
32			:
33#ifdef USE_BENAPHORE
34			fSemaphore(create_sem(0, name)),
35			fCount(1)
36#else
37			fSemaphore(create_sem(1, name))
38#endif
39		{
40#ifndef USER
41			set_sem_owner(fSemaphore, B_SYSTEM_TEAM);
42#endif
43		}
44
45		~Semaphore()
46		{
47			delete_sem(fSemaphore);
48		}
49
50		status_t InitCheck()
51		{
52			if (fSemaphore < B_OK)
53				return fSemaphore;
54
55			return B_OK;
56		}
57
58		status_t Lock()
59		{
60#ifdef USE_BENAPHORE
61			if (atomic_add(&fCount, -1) <= 0)
62#endif
63				return acquire_sem(fSemaphore);
64#ifdef USE_BENAPHORE
65			return B_OK;
66#endif
67		}
68
69		status_t Unlock()
70		{
71#ifdef USE_BENAPHORE
72			if (atomic_add(&fCount, 1) < 0)
73#endif
74				return release_sem(fSemaphore);
75#ifdef USE_BENAPHORE
76			return B_OK;
77#endif
78		}
79
80	private:
81		sem_id	fSemaphore;
82#ifdef USE_BENAPHORE
83		vint32	fCount;
84#endif
85};
86
87// a convenience class to lock a Semaphore object
88
89class Locker {
90	public:
91		Locker(Semaphore &lock)
92			: fLock(lock)
93		{
94			fStatus = lock.Lock();
95			ASSERT(fStatus == B_OK);
96		}
97
98		~Locker()
99		{
100			if (fStatus == B_OK)
101				fLock.Unlock();
102		}
103
104		status_t Status() const
105		{
106			return fStatus;
107		}
108
109	private:
110		Semaphore	&fLock;
111		status_t	fStatus;
112};
113
114
115//**** Recursive Lock
116
117class RecursiveLock {
118	public:
119		RecursiveLock(const char *name)
120			:
121#ifdef USE_BENAPHORE
122			fSemaphore(create_sem(0, name)),
123			fCount(1),
124#else
125			fSemaphore(create_sem(1, name)),
126#endif
127			fOwner(-1)
128		{
129#ifndef USER
130			set_sem_owner(fSemaphore, B_SYSTEM_TEAM);
131#endif
132		}
133
134		status_t LockWithTimeout(bigtime_t timeout)
135		{
136			thread_id thread = find_thread(NULL);
137			if (thread == fOwner) {
138				fOwnerCount++;
139				return B_OK;
140			}
141
142			status_t status;
143#ifdef USE_BENAPHORE
144			if (atomic_add(&fCount, -1) > 0)
145				status = B_OK;
146			else
147#endif
148				status = acquire_sem_etc(fSemaphore, 1, B_RELATIVE_TIMEOUT, timeout);
149
150			if (status == B_OK) {
151				fOwner = thread;
152				fOwnerCount = 1;
153			}
154
155			return status;
156		}
157
158		status_t Lock()
159		{
160			return LockWithTimeout(B_INFINITE_TIMEOUT);
161		}
162
163		status_t Unlock()
164		{
165			thread_id thread = find_thread(NULL);
166			if (thread != fOwner) {
167				#if __MWERKS__ && !USER //--- The R5 PowerPC kernel doesn't have panic()
168					char blip[255];
169					sprintf(blip,"RecursiveLock unlocked by %ld, owned by %ld\n", thread, fOwner);
170					kernel_debugger(blip);
171				#else
172					panic("RecursiveLock unlocked by %ld, owned by %ld\n", thread, fOwner);
173				#endif
174			}
175
176			if (--fOwnerCount == 0) {
177				fOwner = -1;
178#ifdef USE_BENAPHORE
179				if (atomic_add(&fCount, 1) < 0)
180#endif
181					return release_sem(fSemaphore);
182			}
183
184			return B_OK;
185		}
186
187	private:
188		sem_id	fSemaphore;
189#ifdef USE_BENAPHORE
190		vint32	fCount;
191#endif
192		thread_id	fOwner;
193		int32		fOwnerCount;
194};
195
196// a convenience class to lock an RecursiveLock object
197
198class RecursiveLocker {
199	public:
200		RecursiveLocker(RecursiveLock &lock)
201			: fLock(lock)
202		{
203			fStatus = lock.Lock();
204			ASSERT(fStatus == B_OK);
205		}
206
207		~RecursiveLocker()
208		{
209			if (fStatus == B_OK)
210				fLock.Unlock();
211		}
212
213		status_t Status() const
214		{
215			return fStatus;
216		}
217
218	private:
219		RecursiveLock	&fLock;
220		status_t		fStatus;
221};
222
223
224//**** Many Reader/Single Writer Lock
225
226// This is a "fast" implementation of a single writer/many reader
227// locking scheme. It's fast because it uses the benaphore idea
228// to do lazy semaphore locking - in most cases it will only have
229// to do some simple integer arithmetic.
230// The second semaphore (fWriteLock) is needed to prevent the situation
231// that a second writer can acquire the lock when there are still readers
232// holding it.
233
234#define MAX_READERS 100000
235
236// Note: this code will break if you actually have 100000 readers
237// at once. With the current thread/... limits in BeOS you can't
238// touch that value, but it might be possible in the future.
239// Also, you can only have about 20000 concurrent writers until
240// the semaphore count exceeds the int32 bounds
241
242// Timeouts:
243// It may be a good idea to have timeouts for the WriteLocked class,
244// in case something went wrong - we'll see if this is necessary,
245// but it would be a somewhat poor work-around for a deadlock...
246// But the only real problem with timeouts could be for things like
247// "chkbfs" - because such a tool may need to lock for some more time
248
249
250// define if you want to have fast locks as the foundation for the
251// ReadWriteLock class - the benefit is that acquire_sem() doesn't
252// have to be called when there is no one waiting.
253// The disadvantage is the use of 2 real semaphores which is quite
254// expensive regarding that BeOS only allows for a total of 64k
255// semaphores (since every open BFS inode has such a lock).
256
257#ifdef FAST_LOCK
258class ReadWriteLock {
259	public:
260		ReadWriteLock(const char *name)
261			:
262			fWriteLock(name)
263		{
264			Initialize(name);
265		}
266
267		ReadWriteLock()
268			:
269			fWriteLock("bfs r/w w-lock")
270		{
271		}
272
273		~ReadWriteLock()
274		{
275			delete_sem(fSemaphore);
276		}
277
278		status_t Initialize(const char *name = "bfs r/w lock")
279		{
280			fSemaphore = create_sem(0, name);
281			fCount = MAX_READERS;
282#ifndef USER
283			set_sem_owner(fSemaphore, B_SYSTEM_TEAM);
284#endif
285			return fSemaphore;
286		}
287
288		status_t InitCheck()
289		{
290			if (fSemaphore < B_OK)
291				return fSemaphore;
292
293			return B_OK;
294		}
295
296		status_t Lock()
297		{
298			if (atomic_add(&fCount, -1) <= 0)
299				return acquire_sem(fSemaphore);
300
301			return B_OK;
302		}
303
304		void Unlock()
305		{
306			if (atomic_add(&fCount, 1) < 0)
307				release_sem(fSemaphore);
308		}
309
310		status_t LockWrite()
311		{
312			if (fWriteLock.Lock() < B_OK)
313				return B_ERROR;
314
315			int32 readers = atomic_add(&fCount, -MAX_READERS);
316			status_t status = B_OK;
317
318			if (readers < MAX_READERS) {
319				// Acquire sem for all readers currently not using a semaphore.
320				// But if we are not the only write lock in the queue, just get
321				// the one for us
322				status = acquire_sem_etc(fSemaphore, readers <= 0 ? 1 : MAX_READERS - readers, 0, 0);
323			}
324			fWriteLock.Unlock();
325
326			return status;
327		}
328
329		void UnlockWrite()
330		{
331			int32 readers = atomic_add(&fCount, MAX_READERS);
332			if (readers < 0) {
333				// release sem for all readers only when we were the only writer
334				release_sem_etc(fSemaphore, readers <= -MAX_READERS ? 1 : -readers, 0);
335			}
336		}
337
338	private:
339		friend class ReadLocked;
340		friend class WriteLocked;
341
342		sem_id		fSemaphore;
343		vint32		fCount;
344		Semaphore	fWriteLock;
345};
346#else	// FAST_LOCK
347class ReadWriteLock {
348	public:
349		ReadWriteLock(const char *name)
350		{
351			Initialize(name);
352		}
353
354		ReadWriteLock()
355		{
356		}
357
358		~ReadWriteLock()
359		{
360			delete_sem(fSemaphore);
361		}
362
363		status_t Initialize(const char *name = "bfs r/w lock")
364		{
365			fSemaphore = create_sem(MAX_READERS, name);
366#ifndef USER
367			set_sem_owner(fSemaphore, B_SYSTEM_TEAM);
368#endif
369			return fSemaphore;
370		}
371
372		status_t InitCheck()
373		{
374			if (fSemaphore < B_OK)
375				return fSemaphore;
376
377			return B_OK;
378		}
379
380		status_t Lock()
381		{
382			return acquire_sem(fSemaphore);
383		}
384
385		void Unlock()
386		{
387			release_sem(fSemaphore);
388		}
389
390		status_t LockWrite()
391		{
392			return acquire_sem_etc(fSemaphore, MAX_READERS, 0, 0);
393		}
394
395		void UnlockWrite()
396		{
397			release_sem_etc(fSemaphore, MAX_READERS, 0);
398		}
399
400	private:
401		friend class ReadLocked;
402		friend class WriteLocked;
403
404		sem_id		fSemaphore;
405};
406#endif	// FAST_LOCK
407
408
409class ReadLocked {
410	public:
411		ReadLocked(ReadWriteLock &lock)
412			:
413			fLock(lock)
414		{
415			fStatus = lock.Lock();
416		}
417
418		~ReadLocked()
419		{
420			if (fStatus == B_OK)
421				fLock.Unlock();
422		}
423
424	private:
425		ReadWriteLock	&fLock;
426		status_t		fStatus;
427};
428
429
430class WriteLocked {
431	public:
432		WriteLocked(ReadWriteLock &lock)
433			:
434			fLock(lock)
435		{
436			fStatus = lock.LockWrite();
437		}
438
439		~WriteLocked()
440		{
441			if (fStatus == B_OK)
442				fLock.UnlockWrite();
443		}
444
445		status_t IsLocked()
446		{
447			return fStatus;
448		}
449
450	private:
451		ReadWriteLock	&fLock;
452		status_t		fStatus;
453};
454
455
456// A simple locking structure that doesn't use a semaphore - it's useful
457// if you have to protect critical parts with a short runtime.
458// It also allows to nest several locks for the same thread.
459
460class SimpleLock {
461	public:
462		SimpleLock()
463			:
464			fHolder(-1),
465			fCount(0)
466		{
467		}
468
469		status_t Lock(bigtime_t time = 500)
470		{
471			int32 thisThread = find_thread(NULL);
472			int32 current;
473			while (1) {
474				/*if (fHolder == -1) {
475					current = fHolder;
476					fHolder = thisThread;
477				}*/
478				current = _atomic_test_and_set(&fHolder, thisThread, -1);
479				if (current == -1)
480					break;
481				if (current == thisThread)
482					break;
483
484				snooze(time);
485			}
486
487			// ToDo: the lock cannot fail currently! We may want
488			// to change this
489			atomic_add(&fCount, 1);
490			return B_OK;
491		}
492
493		void Unlock()
494		{
495			if (atomic_add(&fCount, -1) == 1)
496				_atomic_set(&fHolder, -1);
497		}
498
499		bool IsLocked() const
500		{
501			return fHolder == find_thread(NULL);
502		}
503
504	private:
505		vint32	fHolder;
506		vint32	fCount;
507};
508
509// A convenience class to lock the SimpleLock, note the
510// different timing compared to the direct call
511
512class SimpleLocker {
513	public:
514		SimpleLocker(SimpleLock &lock,bigtime_t time = 1000)
515			: fLock(lock)
516		{
517			lock.Lock(time);
518		}
519
520		~SimpleLocker()
521		{
522			fLock.Unlock();
523		}
524
525	private:
526		SimpleLock	&fLock;
527};
528
529#endif	/* LOCK_H */
530