X-Git-Url: http://wamblee.org/gitweb/?a=blobdiff_plain;ds=inline;f=support%2Fgeneral%2Fsrc%2Ftest%2Fjava%2Forg%2Fwamblee%2Fconcurrency%2FReadWriteLockTest.java;fp=support%2Fgeneral%2Fsrc%2Ftest%2Fjava%2Forg%2Fwamblee%2Fconcurrency%2FReadWriteLockTest.java;h=2c159e9437639c4c563b12d0b0010003ffedfe81;hb=32a62ca2c752e33a7873ac868a7a1f289caedcd4;hp=0000000000000000000000000000000000000000;hpb=d2bdf4e813c6a3964958c87b2ce56eaadf8a1f0a;p=utils diff --git a/support/general/src/test/java/org/wamblee/concurrency/ReadWriteLockTest.java b/support/general/src/test/java/org/wamblee/concurrency/ReadWriteLockTest.java new file mode 100644 index 00000000..2c159e94 --- /dev/null +++ b/support/general/src/test/java/org/wamblee/concurrency/ReadWriteLockTest.java @@ -0,0 +1,507 @@ +/* + * Copyright 2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.concurrency; + +import junit.framework.Assert; +import junit.framework.TestCase; + + +/** + * Testing the read-write lock class. Note: in case of problems, test cases + * could hang. + * + * @see ReadWriteLock + */ +public class ReadWriteLockTest extends TestCase { + /** + * + */ + private static final int HALF_SECOND = 500; + /** + * + */ + private static final int ONE_SECOND = 1000; + /** + * + */ + private static final int TWO_SECONDS = 2000; + private ReadWriteLock _lock; + private int _nReaders; + private int _nWriters; + + /** + * Constructor for ReadWriteLockTest. + * + * @param aName + */ + public ReadWriteLockTest(String aName) { + super(aName); + } + + private synchronized int getReaderCount() { + return _nReaders; + } + + private synchronized int getWriterCount() { + return _nWriters; + } + + synchronized void incrementReaderCount() { + _nReaders++; + } + + synchronized void incrementWriterCount() { + _nWriters++; + } + + synchronized void decrementReaderCount() { + _nReaders--; + } + + synchronized void decrementWriterCount() { + _nWriters--; + } + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + _lock = new ReadWriteLock(); + } + + /* + * @see TestCase#tearDown() + */ + protected void tearDown() throws Exception { + _lock = null; + super.tearDown(); + } + + /** + * Acquire and release a read lock. + */ + public void testRead() { + _lock.acquireRead(); + _lock.releaseRead(); + } + + /** + * Acquire and release a write lock. + */ + public void testWrite() { + _lock.acquireWrite(); + _lock.releaseWrite(); + } + + /** + * Verify concurrent access by multiple readers is possible. + * + * @throws InterruptedException May not occur. + */ + public void testMultipleReaders() throws InterruptedException { + Runnable runnable = new ReadLocker(_lock, this, TWO_SECONDS); + + Thread t1 = new Thread(runnable); + t1.start(); + + Thread t2 = new Thread(runnable); + t2.start(); + Thread.sleep(ONE_SECOND); + assertTrue("Not enough readers!", getReaderCount() == 2); + t1.join(); + t2.join(); + } + + /** + * Verify that only one writer at a time can acquire the write lock. + * + * @throws InterruptedException May not occur. + */ + public void testSingleWriter() throws InterruptedException { + WriteLocker writer = new WriteLocker(_lock, this, ONE_SECOND); + Thread t1 = new Thread(writer); + Thread t2 = new Thread(writer); + + t1.start(); + t2.start(); + Thread.sleep(HALF_SECOND); + assertTrue("Wrong writer count: " + getWriterCount(), + getWriterCount() == 1); + t1.join(); + t2.join(); + } + + /** + * Verify that multiple writers cannot acquire the write lock concurrently. + * + * @throws InterruptedException May not occur. + */ + public void testMultipleWriters() throws InterruptedException { + WriteLocker writer1 = new WriteLocker(_lock, this, HALF_SECOND + ONE_SECOND); + WriteLocker writer2 = new WriteLocker(_lock, this, ONE_SECOND); + Thread t1 = new Thread(writer1); + Thread t2 = new Thread(writer2); + + t1.start(); + Thread.sleep(HALF_SECOND); + assertTrue(getWriterCount() == 1); + t2.start(); + Thread.sleep(HALF_SECOND); + assertTrue(getWriterCount() == 1); // first writer still + + // has the lock. + Thread.sleep(ONE_SECOND); + + // at t = 2, the second writer still must have + // a lock. + assertTrue(getWriterCount() == 1); + t1.join(); + t2.join(); + } + + /** + * Verify that after the first reader acquires a lock, a subsequent writer + * can only acquire the lock after the reader has released it. + * + * @throws InterruptedException May not occur. + */ + public void testReadWrite1() throws InterruptedException { + ReadLocker readLocker = new ReadLocker(_lock, this, TWO_SECONDS); + Thread t1 = new Thread(readLocker); + WriteLocker writeLocker = new WriteLocker(_lock, this, TWO_SECONDS); + Thread t2 = new Thread(writeLocker); + + t1.start(); // acquire read lock + Thread.sleep(HALF_SECOND); + assertTrue(getReaderCount() == 1); + t2.start(); + Thread.sleep(HALF_SECOND); + + // 1 second underway, reader still holding the + // lock so write lock cannot be acquired. + assertTrue(getReaderCount() == 1); + assertTrue(getWriterCount() == 0); + Thread.sleep(ONE_SECOND + HALF_SECOND); + + // 2.5 seconds underway, read lock released and + // write lock must be acquired. + assertTrue("Wrong no. of readers: " + getReaderCount(), + getReaderCount() == 0); + assertTrue(getWriterCount() == 1); + t1.join(); + t2.join(); + } + + /** + * Verify that when multiple readers have acquired a read lock, the writer + * can only acquire the lock after all readers have released it. + * + * @throws InterruptedException May not occur. + */ + public void testReadWrite2() throws InterruptedException { + ReadLocker readLocker1 = new ReadLocker(_lock, this, TWO_SECONDS + HALF_SECOND); + ReadLocker readLocker2 = new ReadLocker(_lock, this, TWO_SECONDS + HALF_SECOND); + Thread t1 = new Thread(readLocker1); + Thread t2 = new Thread(readLocker2); + WriteLocker writeLocker = new WriteLocker(_lock, this, TWO_SECONDS); + Thread t3 = new Thread(writeLocker); + + t1.start(); // acquire read lock + Thread.sleep(ONE_SECOND); + assertTrue(getReaderCount() == 1); + t2.start(); + Thread.sleep(HALF_SECOND); + assertTrue(getReaderCount() == 2); + t3.start(); + Thread.sleep(HALF_SECOND); + + // 2 seconds, + assertTrue(getReaderCount() == 2); + assertTrue(getWriterCount() == 0); + Thread.sleep(ONE_SECOND); + + // 3 seconds underway, first read lock must + // have been released. + assertTrue(getReaderCount() == 1); + assertTrue(getWriterCount() == 0); + Thread.sleep(HALF_SECOND); + + // 4 seconds underway, write lock must have + // been acquired. + assertTrue(getReaderCount() == 0); + assertTrue(getWriterCount() == 1); + + t1.join(); + t2.join(); + t3.join(); + } + + /** + * Verify that after a writer acquires a lock, a subsequent reader can + * only acquire the lock after the writer has released it. + * + * @throws InterruptedException May not occur. + */ + public void testReadWrite3() throws InterruptedException { + ReadLocker readLocker = new ReadLocker(_lock, this, TWO_SECONDS); + Thread t1 = new Thread(readLocker); + WriteLocker writeLocker = new WriteLocker(_lock, this, TWO_SECONDS); + Thread t2 = new Thread(writeLocker); + + t2.start(); // acquire write lock + Thread.sleep(HALF_SECOND); + assertTrue(getWriterCount() == 1); + t1.start(); + Thread.sleep(HALF_SECOND); + + // 1 second underway, writer still holding the + // lock so read lock cannot be acquired. + assertTrue(getWriterCount() == 1); + assertTrue(getReaderCount() == 0); + Thread.sleep(ONE_SECOND + HALF_SECOND); + + // 2.5 seconds underway, write lock released and + // read lock must be acquired. + assertTrue("Wrong no. of writers: " + getReaderCount(), + getWriterCount() == 0); + assertTrue(getReaderCount() == 1); + t1.join(); + t2.join(); + } + + /* + * The following test cases are for testing whether or not + * the read write lock checks the locking correctly. + * Strictly speaking, these checks wouldn't be necessary + * because it involves the contract of the ReadWriteLock which + * must be obeyed by users of the ReadWriteLock. Nevertheless, + * this is tested anyway to be absolutely sure. + */ + + /** + * Acquire a read lock from one thread, release it from another. Verify + * that a RuntimeException is thrown. + * + * @throws InterruptedException May not occur. + */ + public void testReleaseReadFromWrongThread() throws InterruptedException { + Thread t1 = null; + + try { + t1 = new Thread(new Runnable() { + public void run() { + ReadWriteLockTest.this._lock.acquireRead(); + } + }); + t1.start(); + Thread.sleep(ONE_SECOND); // wait until thread is started + _lock.releaseRead(); // release lock from wrong thread. + } catch (RuntimeException e) { + return; // ok + } finally { + t1.join(); + } + + fail(); + } + + /** + * Acquire a write lock from one thread, release it from another. Verify + * that a RuntimeException is thrown. + * + * @throws InterruptedException May not occur. + */ + public void testReleaseWriteFromWrongThread() throws InterruptedException { + Thread t1 = null; + + try { + t1 = new Thread(new Runnable() { + public void run() { + ReadWriteLockTest.this._lock.acquireWrite(); + } + }); + t1.start(); + Thread.sleep(ONE_SECOND); // wait until thread is started + _lock.releaseWrite(); // release lock from wrong thread. + } catch (RuntimeException e) { + return; // ok + } finally { + t1.join(); + } + + fail(); + } + + /** + * Try to acquire a read lock multiple times. Verify that a + * RuntimeException is thrown. + */ + public void testAcquireReadTwice() { + try { + _lock.acquireRead(); + _lock.acquireRead(); + } catch (RuntimeException e) { + // ok + return; + } + + fail(); + } + + /** + * Try to acquire a write lock multiple times. Verify that a + * RuntimeException is thrown. + */ + public void testAcquireWriteTwice() { + try { + _lock.acquireWrite(); + _lock.acquireWrite(); + } catch (RuntimeException e) { + return; // ok + } + + fail(); + } + + /** + * Acquire the lock for reading and directly afterwards acquire it for + * writing. Verify that a RuntimeException is thrown. + */ + public void testAcquireReadFollowedByWrite() { + try { + _lock.acquireRead(); + _lock.acquireWrite(); + } catch (RuntimeException e) { + return; // ok + } + + fail(); + } + + /** + * Acquire the lock for writing and directly afterwards acquire it for + * reading. Verify that a RuntimeException is thrown. + */ + public void testAcquireWriteFollowedByRead() { + try { + _lock.acquireWrite(); + _lock.acquireRead(); + } catch (RuntimeException e) { + return; // ok + } + + fail(); + } + + /** + * Acquire a read lock and release it as a write lock. Verify that a + * RuntimeException is thrown. + */ + public void testAcquireReadFollowedByReleaseaWrite() { + try { + _lock.acquireRead(); + _lock.releaseWrite(); + } catch (RuntimeException e) { + return; // ok + } + + fail(); + } + + /** + * Acquire a write lock and release it as a read lock. Verify that a + * RuntimeException is thrown. + */ + public void testAcquireWriteFollowedByReleaseRead() { + try { + _lock.acquireWrite(); + _lock.releaseRead(); + } catch (RuntimeException e) { + return; // ok + } + + fail(); + } +} + + +/** + * ReadLocker acquires a read lock and performs a callback when the lock as + * been acquired, sleeps for a designated amount of time, releases the read + * lock, and performs a callback after the lock has been released. + */ +class ReadLocker implements Runnable { + private ReadWriteLock _lock; + private ReadWriteLockTest _lockTest; + private int _sleepTime; + + public ReadLocker(ReadWriteLock aLock, ReadWriteLockTest aLockTest, + int aSleepTime) { + _lock = aLock; + _lockTest = aLockTest; + _sleepTime = aSleepTime; + } + + public void run() { + _lock.acquireRead(); + _lockTest.incrementReaderCount(); + + try { + Thread.sleep(_sleepTime); + } catch (InterruptedException e) { + Assert.fail("ReadLocker thread was interrupted." + + Thread.currentThread()); + } + + _lock.releaseRead(); + _lockTest.decrementReaderCount(); + } +} + + +/** + * WriteLocker acquires a write lock and performs a callback when the lock as + * been acquired, sleeps for a designated amount of time, releases the write + * lock, and performs a callback after the lock has been released. + */ +class WriteLocker implements Runnable { + private ReadWriteLock _lock; + private ReadWriteLockTest _lockTest; + private int _sleepTime; + + public WriteLocker(ReadWriteLock aLock, ReadWriteLockTest aLockTest, + int aSleepTime) { + _lock = aLock; + _lockTest = aLockTest; + _sleepTime = aSleepTime; + } + + public void run() { + _lock.acquireWrite(); + _lockTest.incrementWriterCount(); + + try { + Thread.sleep(_sleepTime); + } catch (InterruptedException e) { + Assert.fail("WriteLocker thread was interrupted: " + + Thread.currentThread()); + } + + _lock.releaseWrite(); + _lockTest.decrementWriterCount(); + } +}