xref: /haiku/headers/private/shared/RWLocker.h (revision c302a243e15e640fae0f689e32cdf0c18749afee)
1*485f5339SAugustin Cavalier /*
2*485f5339SAugustin Cavalier  * Copyright 2006, Haiku.
3*485f5339SAugustin Cavalier  * Distributed under the terms of the MIT License.
4*485f5339SAugustin Cavalier  *
5*485f5339SAugustin Cavalier  * Authors:
6*485f5339SAugustin Cavalier  *		IngoWeinhold <bonefish@cs.tu-berlin.de>
7*485f5339SAugustin Cavalier  */
8*485f5339SAugustin Cavalier 
9*485f5339SAugustin Cavalier // This class provides a reader/writer locking mechanism:
10*485f5339SAugustin Cavalier // * A writer needs an exclusive lock.
11*485f5339SAugustin Cavalier // * For a reader a non-exclusive lock to be shared with other readers is
12*485f5339SAugustin Cavalier //   sufficient.
13*485f5339SAugustin Cavalier // * The ownership of a lock is bound to the thread that requested the lock;
14*485f5339SAugustin Cavalier //   the same thread has to call Unlock() later.
15*485f5339SAugustin Cavalier // * Nested locking is supported: a number of XXXLock() calls needs to be
16*485f5339SAugustin Cavalier //   bracketed by the same number of XXXUnlock() calls.
17*485f5339SAugustin Cavalier // * The lock acquiration strategy is fair: a lock applicant needs to wait
18*485f5339SAugustin Cavalier //   only for those threads that already own a lock or requested one before
19*485f5339SAugustin Cavalier //   the current thread. No one can overtake. E.g. if a thread owns a read
20*485f5339SAugustin Cavalier //   lock, another one is waiting for a write lock, then a third one
21*485f5339SAugustin Cavalier //   requesting a read lock has to wait until the write locker is done.
22*485f5339SAugustin Cavalier //   This does not hold for threads that already own a lock (nested locking).
23*485f5339SAugustin Cavalier //   A read lock owner is immediately granted another read lock and a write
24*485f5339SAugustin Cavalier //   lock owner another write or a read lock.
25*485f5339SAugustin Cavalier // * A write lock owner is allowed to request a read lock and a read lock
26*485f5339SAugustin Cavalier //   owner a write lock. While the first case is not problematic, the
27*485f5339SAugustin Cavalier //   second one needs some further explanation: A read lock owner requesting
28*485f5339SAugustin Cavalier //   a write lock temporarily looses its read lock(s) until the write lock
29*485f5339SAugustin Cavalier //   is granted. Otherwise two read lock owning threads trying to get
30*485f5339SAugustin Cavalier //   write locks at the same time would dead lock each other. The only
31*485f5339SAugustin Cavalier //   problem with this solution is, that the write lock acquiration must
32*485f5339SAugustin Cavalier //   not fail, because in that case the thread could not be given back
33*485f5339SAugustin Cavalier //   its read lock(s), since another thread may have been given a write lock
34*485f5339SAugustin Cavalier //   in the mean time. Fortunately locking can fail only either, if the
35*485f5339SAugustin Cavalier //   locker has been deleted, or, if a timeout occured. Therefore
36*485f5339SAugustin Cavalier //   WriteLockWithTimeout() immediatlely returns with a B_WOULD_BLOCK error
37*485f5339SAugustin Cavalier //   code, if the caller already owns a read lock (but no write lock) and
38*485f5339SAugustin Cavalier //   another thread already owns or has requested a read or write lock.
39*485f5339SAugustin Cavalier // * Calls to read and write locking methods may interleave arbitrarily,
40*485f5339SAugustin Cavalier //   e.g.: ReadLock(); WriteLock(); ReadUnlock(); WriteUnlock();
41*485f5339SAugustin Cavalier //
42*485f5339SAugustin Cavalier // Important note: Read/WriteLock() can fail only, if the locker has been
43*485f5339SAugustin Cavalier // deleted. However, it is NOT save to invoke any method on a deleted
44*485f5339SAugustin Cavalier // locker object.
45*485f5339SAugustin Cavalier //
46*485f5339SAugustin Cavalier // Implementation details:
47*485f5339SAugustin Cavalier // A locker needs three semaphores (a BLocker and two semaphores): one
48*485f5339SAugustin Cavalier // to protect the lockers data, one as a reader/writer mutex (to be
49*485f5339SAugustin Cavalier // acquired by each writer and the first reader) and one for queueing
50*485f5339SAugustin Cavalier // waiting readers and writers. The simplified locking/unlocking
51*485f5339SAugustin Cavalier // algorithm is the following:
52*485f5339SAugustin Cavalier //
53*485f5339SAugustin Cavalier //		writer				reader
54*485f5339SAugustin Cavalier //	queue.acquire()		queue.acquire()
55*485f5339SAugustin Cavalier //	mutex.acquire()		if (first reader) mutex.acquire()
56*485f5339SAugustin Cavalier //	queue.release()		queue.release()
57*485f5339SAugustin Cavalier //		 ...				 ...
58*485f5339SAugustin Cavalier //	mutex.release()		if (last reader) mutex.release()
59*485f5339SAugustin Cavalier //
60*485f5339SAugustin Cavalier // One thread at maximum waits at the mutex, the others at the queueing
61*485f5339SAugustin Cavalier // semaphore. Unfortunately features as nested locking and timeouts make
62*485f5339SAugustin Cavalier // things more difficult. Therefore readers as well as writers need to check
63*485f5339SAugustin Cavalier // whether they already own a lock before acquiring the queueing semaphore.
64*485f5339SAugustin Cavalier // The data for the readers are stored in a list of ReadLockInfo structures;
65*485f5339SAugustin Cavalier // the writer data are stored in some special fields. /fReaderCount/ and
66*485f5339SAugustin Cavalier // /fWriterCount/ contain the total count of unbalanced Read/WriteLock()
67*485f5339SAugustin Cavalier // calls, /fWriterReaderCount/ and /fWriterWriterCount/ only from those of
68*485f5339SAugustin Cavalier // the current write lock owner (/fWriter/). To be a bit more precise:
69*485f5339SAugustin Cavalier // /fWriterReaderCount/ is not contained in /fReaderCount/, but
70*485f5339SAugustin Cavalier // /fWriterWriterCount/ is contained in /fWriterCount/. Therefore
71*485f5339SAugustin Cavalier // /fReaderCount/ can be considered to be the count of true reader's read
72*485f5339SAugustin Cavalier // locks.
73*485f5339SAugustin Cavalier 
74*485f5339SAugustin Cavalier #ifndef RW_LOCKER_H
75*485f5339SAugustin Cavalier #define RW_LOCKER_H
76*485f5339SAugustin Cavalier 
77*485f5339SAugustin Cavalier #include <List.h>
78*485f5339SAugustin Cavalier #include <Locker.h>
79*485f5339SAugustin Cavalier 
80*485f5339SAugustin Cavalier #include "AutoLocker.h"
81*485f5339SAugustin Cavalier 
82*485f5339SAugustin Cavalier class RWLocker {
83*485f5339SAugustin Cavalier  public:
84*485f5339SAugustin Cavalier 								RWLocker();
85*485f5339SAugustin Cavalier 								RWLocker(const char* name);
86*485f5339SAugustin Cavalier 	virtual						~RWLocker();
87*485f5339SAugustin Cavalier 
88*485f5339SAugustin Cavalier 			bool				ReadLock();
89*485f5339SAugustin Cavalier 			status_t			ReadLockWithTimeout(bigtime_t timeout);
90*485f5339SAugustin Cavalier 			void				ReadUnlock();
91*485f5339SAugustin Cavalier 			bool				IsReadLocked() const;
92*485f5339SAugustin Cavalier 
93*485f5339SAugustin Cavalier 			bool				WriteLock();
94*485f5339SAugustin Cavalier 			status_t			WriteLockWithTimeout(bigtime_t timeout);
95*485f5339SAugustin Cavalier 			void				WriteUnlock();
96*485f5339SAugustin Cavalier 			bool				IsWriteLocked() const;
97*485f5339SAugustin Cavalier 
98*485f5339SAugustin Cavalier  private:
99*485f5339SAugustin Cavalier 	struct	ReadLockInfo;
100*485f5339SAugustin Cavalier 	struct	Benaphore {
101*485f5339SAugustin Cavalier 			sem_id	semaphore;
102*485f5339SAugustin Cavalier 			int32	counter;
103*485f5339SAugustin Cavalier 	};
104*485f5339SAugustin Cavalier 
105*485f5339SAugustin Cavalier  private:
106*485f5339SAugustin Cavalier 			void				_Init(const char* name);
107*485f5339SAugustin Cavalier 			status_t			_ReadLock(bigtime_t timeout);
108*485f5339SAugustin Cavalier 			status_t			_WriteLock(bigtime_t timeout);
109*485f5339SAugustin Cavalier 
110*485f5339SAugustin Cavalier 			int32				_AddReadLockInfo(ReadLockInfo* info);
111*485f5339SAugustin Cavalier 			int32				_NewReadLockInfo(thread_id thread,
112*485f5339SAugustin Cavalier 												 int32 count = 1);
113*485f5339SAugustin Cavalier 			void				_DeleteReadLockInfo(int32 index);
114*485f5339SAugustin Cavalier 			ReadLockInfo*		_ReadLockInfoAt(int32 index) const;
115*485f5339SAugustin Cavalier 			int32				_IndexOf(thread_id thread) const;
116*485f5339SAugustin Cavalier 
117*485f5339SAugustin Cavalier 	static	status_t			_AcquireBenaphore(Benaphore& benaphore,
118*485f5339SAugustin Cavalier 												  bigtime_t timeout);
119*485f5339SAugustin Cavalier 	static	void				_ReleaseBenaphore(Benaphore& benaphore);
120*485f5339SAugustin Cavalier 
121*485f5339SAugustin Cavalier  private:
122*485f5339SAugustin Cavalier 	mutable	BLocker				fLock;				// data lock
123*485f5339SAugustin Cavalier 			Benaphore			fMutex;				// critical code mutex
124*485f5339SAugustin Cavalier 			Benaphore			fQueue;				// queueing semaphore
125*485f5339SAugustin Cavalier 			int32				fReaderCount;		// total count...
126*485f5339SAugustin Cavalier 			int32				fWriterCount;		// total count...
127*485f5339SAugustin Cavalier 			BList				fReadLockInfos;
128*485f5339SAugustin Cavalier 			thread_id			fWriter;			// current write lock owner
129*485f5339SAugustin Cavalier 			int32				fWriterWriterCount;	// write lock owner count
130*485f5339SAugustin Cavalier 			int32				fWriterReaderCount;	// writer read lock owner
131*485f5339SAugustin Cavalier 													// count
132*485f5339SAugustin Cavalier };
133*485f5339SAugustin Cavalier 
134*485f5339SAugustin Cavalier typedef AutoLocker<RWLocker, AutoLockerReadLocking<RWLocker> > AutoReadLocker;
135*485f5339SAugustin Cavalier typedef AutoLocker<RWLocker, AutoLockerWriteLocking<RWLocker> > AutoWriteLocker;
136*485f5339SAugustin Cavalier 
137*485f5339SAugustin Cavalier #endif	// RW_LOCKER_H
138