#include "RwMutex.h" #include #include "MutexException.h" // Two mutex scheme // // twoOrMoreMutex (timed mutex) // numReadersMutex // // When a client connects // // 1. obtain numReadersMutex // 2. If this is the second reader, obtain a lock on the twoOrMoreMutex mutex // 3. increment the number of readers // 3. release numReadersMutex // // When a write operation is to be performed // // 1. lock the twoOrMoreMutex // 2. write // 3. Unlock the twoOrMoreMutex // // When a client disconnects // // 1. obtain numReadersMutex // 2. decrement numReadersMutex // 3. If this was the second reader, then release the twoOrMoreMutex mutex // 4. release numReadersMutex namespace CCDom { typedef std::auto_ptr TimedLockAptr; struct RwMutexImpl { //! Pair of mutexes (muticies?) used to block access to writers //! when there is a reader and vice-versa boost::timed_mutex twoOrMoreMutex; //! Locks for the above mutexes TimedLockAptr twoOrMoreLock; //! Mutex to serialise access to the numReaders variable boost::mutex numReadersMutex; //! Number of readers int numReaders; RwMutexImpl() : twoOrMoreMutex(), twoOrMoreLock(), numReadersMutex(), numReaders(0) { } }; RwMutex::RwMutex() : m_impl(new RwMutexImpl) { } RwMutex::~RwMutex() { } RwMutex::Lock::Lock(RwMutex& rwMutex, const boost::xtime& timeout) : m_rwMutex(rwMutex) { RwMutexImpl& rwImpl(*(m_rwMutex.m_impl)); boost::mutex::scoped_lock numReadersLock(rwImpl.numReadersMutex); int& numReaders = rwImpl.numReaders; // Try to obtain a lock if this is the second reader (1 is incremented to 2 below). // If this fails, throw an exception if(numReaders == 1) { rwImpl.twoOrMoreLock = TimedLockAptr(new boost::timed_mutex::scoped_timed_lock (rwImpl.twoOrMoreMutex, timeout)); if(!(*(rwImpl.twoOrMoreLock))) throw MutexException("Unable to obtain read lock", __FILE__, __LINE__); } // If the lock was obtained, then increment the num readers numReaders ++; } // Destructor decrements the num readers and unlocks a mutex if necessary RwMutex::Lock::~Lock() { RwMutexImpl& rwImpl(*m_rwMutex.m_impl); boost::mutex::scoped_lock numReadersLock(rwImpl.numReadersMutex); int& numReaders = rwImpl.numReaders; numReaders --; // Unlock a mutex if required if(numReaders == 1) rwImpl.twoOrMoreLock = TimedLockAptr(); } struct RwMutex::WriteLockImpl { TimedLockAptr writeLock; WriteLockImpl(Lock& lock, const boost::xtime& timeout) : writeLock(new boost::timed_mutex::scoped_timed_lock (lock.m_rwMutex.m_impl->twoOrMoreMutex, timeout)) { if(!(*writeLock)) throw MutexException("Unable to promote read lock to a write lock", __FILE__, __LINE__); } }; RwMutex::WriteLock::WriteLock(Lock& lock, const boost::xtime& timeout) : m_impl(new WriteLockImpl(lock, timeout)) { } RwMutex::WriteLock::~WriteLock() { } }