2 * Copyright 2005-2010 the original author or authors.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.wamblee.concurrency;
18 import junit.framework.TestCase;
21 * Testing the read-write lock class. Note: in case of problems, test cases
26 public class ReadWriteLockTest extends TestCase {
27 private static final int HALF_SECOND = 500;
29 private static final int ONE_SECOND = 1000;
31 private static final int TWO_SECONDS = 2000;
32 private ReadWriteLock lock;
37 * Constructor for ReadWriteLockTest.
41 public ReadWriteLockTest(String aName) {
45 private synchronized int getReaderCount() {
49 private synchronized int getWriterCount() {
53 synchronized void incrementReaderCount() {
57 synchronized void incrementWriterCount() {
61 synchronized void decrementReaderCount() {
65 synchronized void decrementWriterCount() {
70 * @see TestCase#setUp()
72 protected void setUp() throws Exception {
74 lock = new ReadWriteLock();
78 * @see TestCase#tearDown()
80 protected void tearDown() throws Exception {
86 * Acquire and release a read lock.
88 public void testRead() {
94 * Acquire and release a write lock.
96 public void testWrite() {
102 * Verify concurrent access by multiple readers is possible.
104 * @throws InterruptedException
107 public void testMultipleReaders() throws InterruptedException {
108 Runnable runnable = new ReadLocker(lock, this, TWO_SECONDS);
110 Thread t1 = new Thread(runnable);
113 Thread t2 = new Thread(runnable);
115 Thread.sleep(ONE_SECOND);
116 assertTrue("Not enough readers!", getReaderCount() == 2);
122 * Verify that only one writer at a time can acquire the write lock.
124 * @throws InterruptedException
127 public void testSingleWriter() throws InterruptedException {
128 WriteLocker writer = new WriteLocker(lock, this, ONE_SECOND);
129 Thread t1 = new Thread(writer);
130 Thread t2 = new Thread(writer);
134 Thread.sleep(HALF_SECOND);
135 assertTrue("Wrong writer count: " + getWriterCount(),
136 getWriterCount() == 1);
142 * Verify that multiple writers cannot acquire the write lock concurrently.
144 * @throws InterruptedException
147 public void testMultipleWriters() throws InterruptedException {
148 WriteLocker writer1 = new WriteLocker(lock, this, HALF_SECOND +
150 WriteLocker writer2 = new WriteLocker(lock, this, ONE_SECOND);
151 Thread t1 = new Thread(writer1);
152 Thread t2 = new Thread(writer2);
155 Thread.sleep(HALF_SECOND);
156 assertTrue(getWriterCount() == 1);
158 Thread.sleep(HALF_SECOND);
159 assertTrue(getWriterCount() == 1); // first writer still
162 Thread.sleep(ONE_SECOND);
164 // at t = 2, the second writer still must have
166 assertTrue(getWriterCount() == 1);
172 * Verify that after the first reader acquires a lock, a subsequent writer
173 * can only acquire the lock after the reader has released it.
175 * @throws InterruptedException
178 public void testReadWrite1() throws InterruptedException {
179 ReadLocker readLocker = new ReadLocker(lock, this, TWO_SECONDS);
180 Thread t1 = new Thread(readLocker);
181 WriteLocker writeLocker = new WriteLocker(lock, this, TWO_SECONDS);
182 Thread t2 = new Thread(writeLocker);
184 t1.start(); // acquire read lock
185 Thread.sleep(HALF_SECOND);
186 assertTrue(getReaderCount() == 1);
188 Thread.sleep(HALF_SECOND);
190 // 1 second underway, reader still holding the
191 // lock so write lock cannot be acquired.
192 assertTrue(getReaderCount() == 1);
193 assertTrue(getWriterCount() == 0);
194 Thread.sleep(ONE_SECOND + HALF_SECOND);
196 // 2.5 seconds underway, read lock released and
197 // write lock must be acquired.
198 assertTrue("Wrong no. of readers: " + getReaderCount(),
199 getReaderCount() == 0);
200 assertTrue(getWriterCount() == 1);
206 * Verify that when multiple readers have acquired a read lock, the writer
207 * can only acquire the lock after all readers have released it.
209 * @throws InterruptedException
212 public void testReadWrite2() throws InterruptedException {
213 ReadLocker readLocker1 = new ReadLocker(lock, this, TWO_SECONDS +
215 ReadLocker readLocker2 = new ReadLocker(lock, this, TWO_SECONDS +
217 Thread t1 = new Thread(readLocker1);
218 Thread t2 = new Thread(readLocker2);
219 WriteLocker writeLocker = new WriteLocker(lock, this, TWO_SECONDS);
220 Thread t3 = new Thread(writeLocker);
222 t1.start(); // acquire read lock [0, 2.5]
223 Thread.sleep(ONE_SECOND);
225 assertTrue(getReaderCount() == 1);
226 t2.start(); // acquire read lock [1, 3.5]
227 Thread.sleep(HALF_SECOND);
229 assertTrue(getReaderCount() == 2);
230 t3.start(); // write lock
231 Thread.sleep(HALF_SECOND);
234 assertTrue(getReaderCount() == 2);
235 assertTrue(getWriterCount() == 0);
236 Thread.sleep(ONE_SECOND);
238 // 3 seconds underway, first read lock must
239 // have been released.
240 assertTrue(getReaderCount() == 1);
241 assertTrue(getWriterCount() == 0);
242 Thread.sleep(ONE_SECOND);
244 // 4 seconds underway, write lock must have
246 assertTrue(getReaderCount() == 0);
247 assertTrue(getWriterCount() == 1);
255 * Verify that after a writer acquires a lock, a subsequent reader can only
256 * acquire the lock after the writer has released it.
258 * @throws InterruptedException
261 public void testReadWrite3() throws InterruptedException {
262 ReadLocker readLocker = new ReadLocker(lock, this, TWO_SECONDS);
263 Thread t1 = new Thread(readLocker);
264 WriteLocker writeLocker = new WriteLocker(lock, this, TWO_SECONDS);
265 Thread t2 = new Thread(writeLocker);
267 t2.start(); // acquire write lock
268 Thread.sleep(HALF_SECOND);
269 assertTrue(getWriterCount() == 1);
271 Thread.sleep(HALF_SECOND);
273 // 1 second underway, writer still holding the
274 // lock so read lock cannot be acquired.
275 assertTrue(getWriterCount() == 1);
276 assertTrue(getReaderCount() == 0);
277 Thread.sleep(ONE_SECOND + HALF_SECOND);
279 // 2.5 seconds underway, write lock released and
280 // read lock must be acquired.
281 assertTrue("Wrong no. of writers: " + getReaderCount(),
282 getWriterCount() == 0);
283 assertTrue(getReaderCount() == 1);
289 * The following test cases are for testing whether or not the read write
290 * lock checks the locking correctly. Strictly speaking, these checks
291 * wouldn't be necessary because it involves the contract of the
292 * ReadWriteLock which must be obeyed by users of the ReadWriteLock.
293 * Nevertheless, this is tested anyway to be absolutely sure.
297 * Acquire a read lock from one thread, release it from another. Verify that
298 * a RuntimeException is thrown.
300 * @throws InterruptedException
303 public void testReleaseReadFromWrongThread() throws InterruptedException {
307 t1 = new Thread(new Runnable() {
309 ReadWriteLockTest.this.lock.acquireRead();
313 Thread.sleep(ONE_SECOND); // wait until thread is started
314 lock.releaseRead(); // release lock from wrong thread.
315 } catch (RuntimeException e) {
325 * Acquire a write lock from one thread, release it from another. Verify
326 * that a RuntimeException is thrown.
328 * @throws InterruptedException
331 public void testReleaseWriteFromWrongThread() throws InterruptedException {
335 t1 = new Thread(new Runnable() {
337 ReadWriteLockTest.this.lock.acquireWrite();
341 Thread.sleep(ONE_SECOND); // wait until thread is started
342 lock.releaseWrite(); // release lock from wrong thread.
343 } catch (RuntimeException e) {
353 * Try to acquire a read lock multiple times. Verify that a RuntimeException
356 public void testAcquireReadTwice() {
360 } catch (RuntimeException e) {
369 * Try to acquire a write lock multiple times. Verify that a
370 * RuntimeException is thrown.
372 public void testAcquireWriteTwice() {
376 } catch (RuntimeException e) {
384 * Acquire the lock for reading and directly afterwards acquire it for
385 * writing. Verify that a RuntimeException is thrown.
387 public void testAcquireReadFollowedByWrite() {
391 } catch (RuntimeException e) {
399 * Acquire the lock for writing and directly afterwards acquire it for
400 * reading. Verify that a RuntimeException is thrown.
402 public void testAcquireWriteFollowedByRead() {
406 } catch (RuntimeException e) {
414 * Acquire a read lock and release it as a write lock. Verify that a
415 * RuntimeException is thrown.
417 public void testAcquireReadFollowedByReleaseaWrite() {
421 } catch (RuntimeException e) {
429 * Acquire a write lock and release it as a read lock. Verify that a
430 * RuntimeException is thrown.
432 public void testAcquireWriteFollowedByReleaseRead() {
436 } catch (RuntimeException e) {
445 * ReadLocker acquires a read lock and performs a callback when the lock as been
446 * acquired, sleeps for a designated amount of time, releases the read lock, and
447 * performs a callback after the lock has been released.
449 class ReadLocker implements Runnable {
450 private ReadWriteLock lock;
451 private ReadWriteLockTest lockTest;
452 private int sleepTime;
454 public ReadLocker(ReadWriteLock aLock, ReadWriteLockTest aLockTest,
457 lockTest = aLockTest;
458 sleepTime = aSleepTime;
463 lockTest.incrementReaderCount();
466 Thread.sleep(sleepTime);
467 } catch (InterruptedException e) {
468 throw new RuntimeException("ReadLocker thread was interrupted." +
469 Thread.currentThread());
473 lockTest.decrementReaderCount();
478 * WriteLocker acquires a write lock and performs a callback when the lock as
479 * been acquired, sleeps for a designated amount of time, releases the write
480 * lock, and performs a callback after the lock has been released.
482 class WriteLocker implements Runnable {
483 private ReadWriteLock lock;
484 private ReadWriteLockTest lockTest;
485 private int sleepTime;
487 public WriteLocker(ReadWriteLock aLock, ReadWriteLockTest aLockTest,
490 lockTest = aLockTest;
491 sleepTime = aSleepTime;
496 lockTest.incrementWriterCount();
499 Thread.sleep(sleepTime);
500 } catch (InterruptedException e) {
501 throw new RuntimeException("WriteLocker thread was interrupted: " +
502 Thread.currentThread());
506 lockTest.decrementWriterCount();