#ifndef PTHREADSRWLOCK_HEADER #define PTHREADSRWLOCK_HEADER 1 ////////////////////////////////////////////////////////////// //! author="Charles Galambos" //! docentry="Utilities.Threads" //! rcsid="$Id: RWLock.hh,v 1.9 2000/10/04 12:43:54 ees1cg Exp $" //! file="amma/StdType/System/PThreads/Posix/RWLock.hh" //! lib=PThreadCXX //! userlevel=Default //! date="02/07/99" #if !defined(_POSIX_SOURCE) && !defined(__sgi__) #define _POSIX_SOURCE 1 #endif #if defined(__sol2__) #include #endif #include #include #include #include "amma/Config.hh" // Hack to test rwlock on solaris 2.7 //#if defined(__sol2__) //#undef CPUG_HAVE_POSIX_THREADS_RWLOCK //#define CPUG_HAVE_POSIX_THREADS_RWLOCK 1 //#endif #if !CPUG_HAVE_POSIX_THREADS_RWLOCK #include #include "amma/PThread/Mutex.hh" #include "amma/PThread/Semaphore.hh" #endif #include "amma/StdType.hh" namespace PThread { #if CPUG_HAVE_POSIX_THREADS_RWLOCK //! userlevel=Normal //: Read/Write lock. class RWLockC { public: RWLockC() { pthread_rwlock_init(&id,0); } //: Constructor. ~RWLockC(); //: Destructor. protected: void Error(const char *msg); //: Print an error. public: BooleanT RdLock(void) { do { if(pthread_rwlock_rdlock(&id) == 0) return TRUE; } while(errno == EINTR) ; Error("Failed to get RdLock"); return FALSE; } //: Get a read lock. BooleanT TryRdLock(void) { return (pthread_rwlock_tryrdlock(&id) == 0); } //: Try and get a read lock. BooleanT WrLock(void) { do { if(pthread_rwlock_wrlock(&id) == 0) return TRUE; } while(errno == EINTR) ; Error("Failed to get WrLock"); return FALSE; } //: Get a write lock. BooleanT TryWrLock(void) { return (pthread_rwlock_trywrlock(&id) == 0); } //: Try and get a write lock. BooleanT UnlockWr(void) { return (pthread_rwlock_unlock(&id) == 0); } //: Unlock write lock. BooleanT UnlockRd(void) { return (pthread_rwlock_unlock(&id) == 0); } //: Unlock read lock. private: pthread_rwlock_t id; }; #else //: Read Write Lock. // There may be a better way, any suggestions ?? // Gives priority to write requests. class RWLockC { public: inline RWLockC(); // Constructor. inline BooleanT RdLock(void); // Get a read lock. inline BooleanT TryRdLock(void); // Try and get a read lock. inline BooleanT WrLock(void); // Get a write lock. inline BooleanT TryWrLock(void); // Try and get a write lock. inline BooleanT UnlockRd(void); // Unlock a read lock. inline BooleanT UnlockWr(void); // Unlock a write lock. protected: inline BooleanT Unlock(void); // Unlock. void Error(const char *msg); //: Print an error. private: MutexC AccM; // Access control. int RdCount; // Count of readers with locks. -1=Writing int WrWait; // Count of writers waiting. int RdWait; // Count of readers waiting. SemaphoreC WriteQueue; // Writers queue. SemaphoreC ReadQueue; // Readers queue. }; /////////////////////// // Constructor. inline RWLockC::RWLockC() : AccM(), RdCount(0), WrWait(0), RdWait(0), WriteQueue(0), ReadQueue(0) {} inline BooleanT RWLockC::RdLock() { AccM.Lock(); while(WrWait > 0 || RdCount < 0) { RdWait++; // Should only go aroung this loop once ! AccM.Unlock(); ReadQueue.Wait(); AccM.Lock(); RdWait--; } assert(RdCount >= 0); RdCount++; AccM.Unlock(); return TRUE; } inline BooleanT RWLockC::TryRdLock() { AccM.Lock(); if(WrWait > 0 || RdCount < 0) { AccM.Unlock(); return FALSE; } RdCount++; AccM.Unlock(); return TRUE; } inline BooleanT RWLockC::WrLock(void) { AccM.Lock(); while(RdCount != 0) { WrWait++; // Should only go through here once ! AccM.Unlock(); WriteQueue.Wait(); AccM.Lock(); WrWait--; } RdCount = -1; // Flag write lock. AccM.Unlock(); return TRUE; } inline BooleanT RWLockC::TryWrLock(void) { AccM.Lock(); if(RdCount > 0) { AccM.Unlock(); return FALSE; } RdCount = -1; // Flag write lock. AccM.Unlock(); return TRUE; } inline BooleanT RWLockC::Unlock(void) { AccM.Lock(); if(RdCount < 0) { // Unlock a write lock. RdCount = 0; if(WrWait > 0) WriteQueue.Post(); // Wake up a waiting writer. else { for(int i = 0;i < RdWait;i++) ReadQueue.Post(); // Wakeup all waiting readers. } AccM.Unlock(); return TRUE; } // Unlock a read lock. RdCount--; if(WrWait < 1) { for(int i = 0;i < RdWait;i++) ReadQueue.Post(); // Wakeup all waiting readers. } else { if(RdCount <= 0) WriteQueue.Post(); // Wake up a waiting writer. } AccM.Unlock(); return TRUE; } inline BooleanT RWLockC::UnlockRd(void) { return Unlock(); } inline BooleanT RWLockC::UnlockWr(void) { return Unlock(); } #endif //! userlevel=Normal //: RWLock locking class. // Used to control locks on RWLockC for exception safe // resoures locking. It is the users responsability // to ensure that the RWLockC remains valid for the // lifetime of any RWLockHoldC instance.

// SMALL OBJECT class RWLockHoldC { public: RWLockHoldC(const RWLockC &m,BooleanT readOnly = true,BooleanT tryOnly = false) : rwlock(const_cast(m)), rLocked(FALSE), wLocked(FALSE) { if(!tryOnly) { if(readOnly) { rwlock.RdLock(); rLocked = TRUE; } else { rwlock.WrLock(); wLocked = TRUE; } } else { if(readOnly) { if(rwlock.TryRdLock()) rLocked = TRUE; } else { if(rwlock.TryWrLock()) wLocked = TRUE; } } } //: Create a lock on a rwlock. // This may not seem like a good idea, // but it allows otherwise constant functions to // lock out other accesses to data without undue // faffing. void Unlock() { assert(!(wLocked && rLocked)); // Not both at once! if(wLocked) rwlock.UnlockWr(); if(rLocked) rwlock.UnlockRd(); rLocked = FALSE; wLocked = FALSE; } //: Unlock the rwlock. ~RWLockHoldC() { Unlock(); } //: Create a lock on a rwlock. void LockRd() { assert(!(wLocked || rLocked)); // Must be no locks. rwlock.RdLock(); rLocked = true; } //: relock for read void LockWr() { assert(!(wLocked || rLocked)); // Must be no locks. rwlock.WrLock(); wLocked = true; } //: relock for write BooleanT IsReadLocked() const { return rLocked || wLocked; } //: Test if safe for reading. // This will return true if either a write or read lock is inplace. BooleanT IsWriteLocked() const { return wLocked; } //: Test if safe for writing. protected: RWLockC &rwlock; BooleanT rLocked; BooleanT wLocked; }; } #endif