1/*
2 * Copyright 2005-2009, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT license.
4 *
5 * Copyright 1999, Be Incorporated. All Rights Reserved.
6 * This file may be used under the terms of the Be Sample Code License.
7 */
8#ifndef MULTI_LOCKER_H
9#define MULTI_LOCKER_H
10
11
12/*! multiple-reader single-writer locking class
13
14	IMPORTANT:
15	 * nested read locks are not supported
16	 * a reader becoming the write is not supported
17	 * nested write locks are supported
18	 * a writer can do read locks, even nested ones
19	 * in case of problems, #define DEBUG 1 in the .cpp
20*/
21
22
23#include <OS.h>
24#include <locks.h>
25
26
27#define MULTI_LOCKER_TIMING	0
28#if DEBUG
29#	include <assert.h>
30#	define MULTI_LOCKER_DEBUG	DEBUG
31#endif
32
33#if MULTI_LOCKER_DEBUG
34#	define ASSERT_MULTI_LOCKED(x) assert((x).IsWriteLocked() || (x).IsReadLocked())
35#	define ASSERT_MULTI_READ_LOCKED(x) assert((x).IsReadLocked())
36#	define ASSERT_MULTI_WRITE_LOCKED(x) assert((x).IsWriteLocked())
37#else
38#	define MULTI_LOCKER_DEBUG	0
39#	define ASSERT_MULTI_LOCKED(x) ;
40#	define ASSERT_MULTI_READ_LOCKED(x) ;
41#	define ASSERT_MULTI_WRITE_LOCKED(x) ;
42#endif
43
44
45class MultiLocker {
46public:
47								MultiLocker(const char* baseName);
48	virtual						~MultiLocker();
49
50			status_t			InitCheck();
51
52			// locking for reading or writing
53			bool				ReadLock();
54			bool				WriteLock();
55
56			// unlocking after reading or writing
57			bool				ReadUnlock();
58			bool				WriteUnlock();
59
60			// does the current thread hold a write lock?
61			bool				IsWriteLocked() const;
62
63#if MULTI_LOCKER_DEBUG
64			// in DEBUG mode returns whether the lock is held
65			// in non-debug mode returns true
66			bool				IsReadLocked() const;
67#endif
68
69private:
70								MultiLocker();
71								MultiLocker(const MultiLocker& other);
72			MultiLocker&		operator=(const MultiLocker& other);
73									// not implemented
74
75#if !MULTI_LOCKER_DEBUG
76			rw_lock				fLock;
77#else
78			// functions for managing the DEBUG reader array
79			void				_RegisterThread();
80			void				_UnregisterThread();
81
82			sem_id				fLock;
83			int32*				fDebugArray;
84			int32				fMaxThreads;
85			int32				fWriterNest;
86			thread_id			fWriterThread;
87#endif	// MULTI_LOCKER_DEBUG
88
89			status_t			fInit;
90
91#if MULTI_LOCKER_TIMING
92			uint32 				rl_count;
93			bigtime_t 			rl_time;
94			uint32 				ru_count;
95			bigtime_t	 		ru_time;
96			uint32				wl_count;
97			bigtime_t			wl_time;
98			uint32				wu_count;
99			bigtime_t			wu_time;
100			uint32				islock_count;
101			bigtime_t			islock_time;
102#endif
103};
104
105
106class AutoWriteLocker {
107public:
108	AutoWriteLocker(MultiLocker* lock)
109		:
110		fLock(*lock)
111	{
112		fLocked = fLock.WriteLock();
113	}
114
115	AutoWriteLocker(MultiLocker& lock)
116		:
117		fLock(lock)
118	{
119		fLocked = fLock.WriteLock();
120	}
121
122	~AutoWriteLocker()
123	{
124		if (fLocked)
125			fLock.WriteUnlock();
126	}
127
128	bool IsLocked() const
129	{
130		return fLock.IsWriteLocked();
131	}
132
133	void Unlock()
134	{
135		if (fLocked) {
136			fLock.WriteUnlock();
137			fLocked = false;
138		}
139	}
140
141private:
142 	MultiLocker&	fLock;
143	bool			fLocked;
144};
145
146
147class AutoReadLocker {
148public:
149	AutoReadLocker(MultiLocker* lock)
150		:
151		fLock(*lock)
152	{
153		fLocked = fLock.ReadLock();
154	}
155
156	AutoReadLocker(MultiLocker& lock)
157		:
158		fLock(lock)
159	{
160		fLocked = fLock.ReadLock();
161	}
162
163	~AutoReadLocker()
164	{
165		Unlock();
166	}
167
168	void Unlock()
169	{
170		if (fLocked) {
171			fLock.ReadUnlock();
172			fLocked = false;
173		}
174	}
175
176private:
177	MultiLocker&	fLock;
178	bool			fLocked;
179};
180
181#endif	// MULTI_LOCKER_H
182