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.Assert;
19 import junit.framework.TestCase;
22 * Testing the read-write lock class. Note: in case of problems, test cases
27 public class ReadWriteLockTest extends TestCase {
28 private static final int HALF_SECOND = 500;
30 private static final int ONE_SECOND = 1000;
32 private static final int TWO_SECONDS = 2000;
33 private ReadWriteLock lock;
38 * Constructor for ReadWriteLockTest.
42 public ReadWriteLockTest(String aName) {
46 private synchronized int getReaderCount() {
50 private synchronized int getWriterCount() {
54 synchronized void incrementReaderCount() {
58 synchronized void incrementWriterCount() {
62 synchronized void decrementReaderCount() {
66 synchronized void decrementWriterCount() {
71 * @see TestCase#setUp()
73 protected void setUp() throws Exception {
75 lock = new ReadWriteLock();
79 * @see TestCase#tearDown()
81 protected void tearDown() throws Exception {
87 * Acquire and release a read lock.
89 public void testRead() {
95 * Acquire and release a write lock.
97 public void testWrite() {
103 * Verify concurrent access by multiple readers is possible.
105 * @throws InterruptedException
108 public void testMultipleReaders() throws InterruptedException {
109 Runnable runnable = new ReadLocker(lock, this, TWO_SECONDS);
111 Thread t1 = new Thread(runnable);
114 Thread t2 = new Thread(runnable);
116 Thread.sleep(ONE_SECOND);
117 assertTrue("Not enough readers!", getReaderCount() == 2);
123 * Verify that only one writer at a time can acquire the write lock.
125 * @throws InterruptedException
128 public void testSingleWriter() throws InterruptedException {
129 WriteLocker writer = new WriteLocker(lock, this, ONE_SECOND);
130 Thread t1 = new Thread(writer);
131 Thread t2 = new Thread(writer);
135 Thread.sleep(HALF_SECOND);
136 assertTrue("Wrong writer count: " + getWriterCount(),
137 getWriterCount() == 1);
143 * Verify that multiple writers cannot acquire the write lock concurrently.
145 * @throws InterruptedException
148 public void testMultipleWriters() throws InterruptedException {
149 WriteLocker writer1 = new WriteLocker(lock, this, HALF_SECOND +
151 WriteLocker writer2 = new WriteLocker(lock, this, ONE_SECOND);
152 Thread t1 = new Thread(writer1);
153 Thread t2 = new Thread(writer2);
156 Thread.sleep(HALF_SECOND);
157 assertTrue(getWriterCount() == 1);
159 Thread.sleep(HALF_SECOND);
160 assertTrue(getWriterCount() == 1); // first writer still
163 Thread.sleep(ONE_SECOND);
165 // at t = 2, the second writer still must have
167 assertTrue(getWriterCount() == 1);
173 * Verify that after the first reader acquires a lock, a subsequent writer
174 * can only acquire the lock after the reader has released it.
176 * @throws InterruptedException
179 public void testReadWrite1() throws InterruptedException {
180 ReadLocker readLocker = new ReadLocker(lock, this, TWO_SECONDS);
181 Thread t1 = new Thread(readLocker);
182 WriteLocker writeLocker = new WriteLocker(lock, this, TWO_SECONDS);
183 Thread t2 = new Thread(writeLocker);
185 t1.start(); // acquire read lock
186 Thread.sleep(HALF_SECOND);
187 assertTrue(getReaderCount() == 1);
189 Thread.sleep(HALF_SECOND);
191 // 1 second underway, reader still holding the
192 // lock so write lock cannot be acquired.
193 assertTrue(getReaderCount() == 1);
194 assertTrue(getWriterCount() == 0);
195 Thread.sleep(ONE_SECOND + HALF_SECOND);
197 // 2.5 seconds underway, read lock released and
198 // write lock must be acquired.
199 assertTrue("Wrong no. of readers: " + getReaderCount(),
200 getReaderCount() == 0);
201 assertTrue(getWriterCount() == 1);
207 * Verify that when multiple readers have acquired a read lock, the writer
208 * can only acquire the lock after all readers have released it.
210 * @throws InterruptedException
213 public void testReadWrite2() throws InterruptedException {
214 ReadLocker readLocker1 = new ReadLocker(lock, this, TWO_SECONDS +
216 ReadLocker readLocker2 = new ReadLocker(lock, this, TWO_SECONDS +
218 Thread t1 = new Thread(readLocker1);
219 Thread t2 = new Thread(readLocker2);
220 WriteLocker writeLocker = new WriteLocker(lock, this, TWO_SECONDS);
221 Thread t3 = new Thread(writeLocker);
223 t1.start(); // acquire read lock [0, 2.5]
224 Thread.sleep(ONE_SECOND);
226 assertTrue(getReaderCount() == 1);
227 t2.start(); // acquire read lock [1, 3.5]
228 Thread.sleep(HALF_SECOND);
230 assertTrue(getReaderCount() == 2);
231 t3.start(); // write lock
232 Thread.sleep(HALF_SECOND);
235 assertTrue(getReaderCount() == 2);
236 assertTrue(getWriterCount() == 0);
237 Thread.sleep(ONE_SECOND);
239 // 3 seconds underway, first read lock must
240 // have been released.
241 assertTrue(getReaderCount() == 1);
242 assertTrue(getWriterCount() == 0);
243 Thread.sleep(ONE_SECOND);
245 // 4 seconds underway, write lock must have
247 assertTrue(getReaderCount() == 0);
248 assertTrue(getWriterCount() == 1);
256 * Verify that after a writer acquires a lock, a subsequent reader can only
257 * acquire the lock after the writer has released it.
259 * @throws InterruptedException
262 public void testReadWrite3() throws InterruptedException {
263 ReadLocker readLocker = new ReadLocker(lock, this, TWO_SECONDS);
264 Thread t1 = new Thread(readLocker);
265 WriteLocker writeLocker = new WriteLocker(lock, this, TWO_SECONDS);
266 Thread t2 = new Thread(writeLocker);
268 t2.start(); // acquire write lock
269 Thread.sleep(HALF_SECOND);
270 assertTrue(getWriterCount() == 1);
272 Thread.sleep(HALF_SECOND);
274 // 1 second underway, writer still holding the
275 // lock so read lock cannot be acquired.
276 assertTrue(getWriterCount() == 1);
277 assertTrue(getReaderCount() == 0);
278 Thread.sleep(ONE_SECOND + HALF_SECOND);
280 // 2.5 seconds underway, write lock released and
281 // read lock must be acquired.
282 assertTrue("Wrong no. of writers: " + getReaderCount(),
283 getWriterCount() == 0);
284 assertTrue(getReaderCount() == 1);
290 * The following test cases are for testing whether or not the read write
291 * lock checks the locking correctly. Strictly speaking, these checks
292 * wouldn't be necessary because it involves the contract of the
293 * ReadWriteLock which must be obeyed by users of the ReadWriteLock.
294 * Nevertheless, this is tested anyway to be absolutely sure.
298 * Acquire a read lock from one thread, release it from another. Verify that
299 * a RuntimeException is thrown.
301 * @throws InterruptedException
304 public void testReleaseReadFromWrongThread() throws InterruptedException {
308 t1 = new Thread(new Runnable() {
310 ReadWriteLockTest.this.lock.acquireRead();
314 Thread.sleep(ONE_SECOND); // wait until thread is started
315 lock.releaseRead(); // release lock from wrong thread.
316 } catch (RuntimeException e) {
326 * Acquire a write lock from one thread, release it from another. Verify
327 * that a RuntimeException is thrown.
329 * @throws InterruptedException
332 public void testReleaseWriteFromWrongThread() throws InterruptedException {
336 t1 = new Thread(new Runnable() {
338 ReadWriteLockTest.this.lock.acquireWrite();
342 Thread.sleep(ONE_SECOND); // wait until thread is started
343 lock.releaseWrite(); // release lock from wrong thread.
344 } catch (RuntimeException e) {
354 * Try to acquire a read lock multiple times. Verify that a RuntimeException
357 public void testAcquireReadTwice() {
361 } catch (RuntimeException e) {
370 * Try to acquire a write lock multiple times. Verify that a
371 * RuntimeException is thrown.
373 public void testAcquireWriteTwice() {
377 } catch (RuntimeException e) {
385 * Acquire the lock for reading and directly afterwards acquire it for
386 * writing. Verify that a RuntimeException is thrown.
388 public void testAcquireReadFollowedByWrite() {
392 } catch (RuntimeException e) {
400 * Acquire the lock for writing and directly afterwards acquire it for
401 * reading. Verify that a RuntimeException is thrown.
403 public void testAcquireWriteFollowedByRead() {
407 } catch (RuntimeException e) {
415 * Acquire a read lock and release it as a write lock. Verify that a
416 * RuntimeException is thrown.
418 public void testAcquireReadFollowedByReleaseaWrite() {
422 } catch (RuntimeException e) {
430 * Acquire a write lock and release it as a read lock. Verify that a
431 * RuntimeException is thrown.
433 public void testAcquireWriteFollowedByReleaseRead() {
437 } catch (RuntimeException e) {
446 * ReadLocker acquires a read lock and performs a callback when the lock as been
447 * acquired, sleeps for a designated amount of time, releases the read lock, and
448 * performs a callback after the lock has been released.
450 class ReadLocker implements Runnable {
451 private ReadWriteLock lock;
452 private ReadWriteLockTest lockTest;
453 private int sleepTime;
455 public ReadLocker(ReadWriteLock aLock, ReadWriteLockTest aLockTest,
458 lockTest = aLockTest;
459 sleepTime = aSleepTime;
464 lockTest.incrementReaderCount();
467 Thread.sleep(sleepTime);
468 } catch (InterruptedException e) {
469 throw new RuntimeException("ReadLocker thread was interrupted." +
470 Thread.currentThread());
474 lockTest.decrementReaderCount();
479 * WriteLocker acquires a write lock and performs a callback when the lock as
480 * been acquired, sleeps for a designated amount of time, releases the write
481 * lock, and performs a callback after the lock has been released.
483 class WriteLocker implements Runnable {
484 private ReadWriteLock lock;
485 private ReadWriteLockTest lockTest;
486 private int sleepTime;
488 public WriteLocker(ReadWriteLock aLock, ReadWriteLockTest aLockTest,
491 lockTest = aLockTest;
492 sleepTime = aSleepTime;
497 lockTest.incrementWriterCount();
500 Thread.sleep(sleepTime);
501 } catch (InterruptedException e) {
502 throw new RuntimeException("WriteLocker thread was interrupted: " +
503 Thread.currentThread());
507 lockTest.decrementWriterCount();