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