diff options
Diffstat (limited to 'cpp/test/IceUtil/thread/RWRecMutexTest.cpp')
-rw-r--r-- | cpp/test/IceUtil/thread/RWRecMutexTest.cpp | 270 |
1 files changed, 265 insertions, 5 deletions
diff --git a/cpp/test/IceUtil/thread/RWRecMutexTest.cpp b/cpp/test/IceUtil/thread/RWRecMutexTest.cpp index e36d0036a06..c6704341549 100644 --- a/cpp/test/IceUtil/thread/RWRecMutexTest.cpp +++ b/cpp/test/IceUtil/thread/RWRecMutexTest.cpp @@ -59,7 +59,8 @@ public: { } - virtual void run() + virtual void + run() { try { @@ -78,6 +79,168 @@ public: } }; +class RWRecMutexReadTestThread2 : public RWRecMutexTestThread +{ +public: + + RWRecMutexReadTestThread2(RWRecMutex& m) : + RWRecMutexTestThread(m) + { + } + + virtual void + run() + { + try + { + RWRecMutex::TryRLock lock(_mutex); + test(false); + } + catch(const LockedException&) + { + // Expected + } + + _trylock = true; + _trylockCond.signal(); + + RWRecMutex::RLock lock(_mutex); + } +}; + +class RWRecMutexUpgradeReadThread : public Thread +{ +public: + + RWRecMutexUpgradeReadThread(RWRecMutex& m) : + _mutex(m), + _unlock(false), + _lock(false) + { + } + virtual void + run() + { + RWRecMutex::RLock lock(_mutex); + + signalLock(); + waitUnlock(); + } + + void + waitUnlock() + { + Mutex::Lock lock(_unlockMutex); + while (!_unlock) + { + _unlockCond.wait(lock); + } + } + + void + signalUnlock() + { + Mutex::Lock lock(_unlockMutex); + _unlock = true; + _unlockCond.signal(); + } + + void + signalLock() + { + Mutex::Lock lock(_lockMutex); + _lock = true; + _lockCond.signal(); + } + + void + waitLock() + { + Mutex::Lock lock(_lockMutex); + while (!_lock) + { + _lockCond.wait(lock); + } + } + +private: + + RWRecMutex& _mutex; + + // + // Use native Condition variable here, not Monitor. + // + Cond _unlockCond; + Mutex _unlockMutex; + bool _unlock; + + Cond _lockCond; + Mutex _lockMutex; + bool _lock; +}; +typedef Handle<RWRecMutexUpgradeReadThread> RWRecMutexUpgradeReadThreadPtr; + +class RWRecMutexUpgradeTestThread : public Thread +{ +public: + + RWRecMutexUpgradeTestThread(RWRecMutex& m) : + _mutex(m), + _lock(false), + _upgradeAcquired(false) + { + } + + virtual void + run() + { + RWRecMutex::RLock lock(_mutex); + + signalLock(); + lock.upgrade(); + + _upgradeAcquired = true; + } + + void + signalLock() + { + Mutex::Lock lock(_lockMutex); + _lock = true; + _lockCond.signal(); + } + + void + waitLock() + { + Mutex::Lock lock(_lockMutex); + while (!_lock) + { + _lockCond.wait(lock); + } + } + + bool + upgradeAcquired() const + { + return _upgradeAcquired; + } + +private: + + RWRecMutex& _mutex; + + // + // Use native Condition variable here, not Monitor. + // + Cond _lockCond; + Mutex _lockMutex; + bool _lock; + + bool _upgradeAcquired; +}; +typedef Handle<RWRecMutexUpgradeTestThread> RWRecMutexUpgradeTestThreadPtr; + class RWRecMutexWriteTestThread : public RWRecMutexTestThread { public: @@ -87,7 +250,8 @@ public: { } - virtual void run() + virtual void + run() { try { @@ -105,7 +269,6 @@ public: RWRecMutex::WLock lock(_mutex); } }; - typedef Handle<RWRecMutexTestThread> RWRecMutexTestThreadPtr; RWRecMutexTest::RWRecMutexTest() : @@ -162,11 +325,11 @@ RWRecMutexTest::run() try { RWRecMutex::TryWLock wlock(mutex); - test(false); + // Expected } catch(const LockedException&) { - // Expected + test(false); } } @@ -238,4 +401,101 @@ RWRecMutexTest::run() // acquire the mutex and then terminate. // control.join(); + + // TEST: Lock upgrading. + { + RWRecMutex::RLock rlock(mutex); + + // + // Mutex now holds a write lock. + // + mutex.upgrade(); + + // Start thread that tries to acquire write lock + t = new RWRecMutexReadTestThread2(mutex); + control = t->start(); + + // TEST: Wait until the trylock has been tested. The thread is + // now waiting on a read lock. + t->waitTrylock(); + + // It's necessary for a small sleep here to ensure that the + // thread is actually waiting on a read lock. + ThreadControl::sleep(1000); + } + + // + // TEST: Once the mutex has been released, the thread should + // acquire the mutex and then terminate. + // + control.join(); + + // TEST: Lock upgrading. This time a reader thread is started + // first. + { + RWRecMutexUpgradeReadThreadPtr t = new RWRecMutexUpgradeReadThread(mutex); + control = t->start(); + + // Wait for the thread to acquire the read lock. + t->waitLock(); + + // Spawn a thread to try acquiring the lock + RWRecMutexUpgradeTestThreadPtr t2 = new RWRecMutexUpgradeTestThread(mutex); + ThreadControl control2 = t2->start(); + t2->waitLock(); + + // + // Small sleep to find out whether the thread actually + // terminates (which means that the write lock upgrade was + // mistakenly acquired). + // + ThreadControl::sleep(1000); + + test(!t2->upgradeAcquired()); + + // + // A read lock at this point should fail. + // + try + { + RWRecMutex::TryRLock rlock2(mutex); + test(false); + } + catch(const LockedException&) + { + // Expected + } + + // + // As should a write lock. + // + try + { + RWRecMutex::TryWLock rlock2(mutex); + test(false); + } + catch(const LockedException&) + { + // Expected + } + + // + // Once the read lock is released then the upgrade should + // succeed & the thread should terminate. + // + t->signalUnlock(); + + control2.join(); + control.join(); + + // + // Now both a read & write lock should be available. + // + { + RWRecMutex::WLock rlock2(mutex); + } + { + RWRecMutex::RLock rlock2(mutex); + } + } } |