2 * Copyright 2005 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;
23 * Testing the read-write lock class. Note: in case of problems, test cases
28 public class ReadWriteLockTest extends TestCase {
32 private static final int HALF_SECOND = 500;
36 private static final int ONE_SECOND = 1000;
40 private static final int TWO_SECONDS = 2000;
41 private ReadWriteLock _lock;
42 private int _nReaders;
43 private int _nWriters;
46 * Constructor for ReadWriteLockTest.
50 public ReadWriteLockTest(String aName) {
54 private synchronized int getReaderCount() {
58 private synchronized int getWriterCount() {
62 synchronized void incrementReaderCount() {
66 synchronized void incrementWriterCount() {
70 synchronized void decrementReaderCount() {
74 synchronized void decrementWriterCount() {
79 * @see TestCase#setUp()
81 protected void setUp() throws Exception {
83 _lock = new ReadWriteLock();
87 * @see TestCase#tearDown()
89 protected void tearDown() throws Exception {
95 * Acquire and release a read lock.
97 public void testRead() {
103 * Acquire and release a write lock.
105 public void testWrite() {
106 _lock.acquireWrite();
107 _lock.releaseWrite();
111 * Verify concurrent access by multiple readers is possible.
113 * @throws InterruptedException May not occur.
115 public void testMultipleReaders() throws InterruptedException {
116 Runnable runnable = new ReadLocker(_lock, this, TWO_SECONDS);
118 Thread t1 = new Thread(runnable);
121 Thread t2 = new Thread(runnable);
123 Thread.sleep(ONE_SECOND);
124 assertTrue("Not enough readers!", getReaderCount() == 2);
130 * Verify that only one writer at a time can acquire the write lock.
132 * @throws InterruptedException May not occur.
134 public void testSingleWriter() throws InterruptedException {
135 WriteLocker writer = new WriteLocker(_lock, this, ONE_SECOND);
136 Thread t1 = new Thread(writer);
137 Thread t2 = new Thread(writer);
141 Thread.sleep(HALF_SECOND);
142 assertTrue("Wrong writer count: " + getWriterCount(),
143 getWriterCount() == 1);
149 * Verify that multiple writers cannot acquire the write lock concurrently.
151 * @throws InterruptedException May not occur.
153 public void testMultipleWriters() throws InterruptedException {
154 WriteLocker writer1 = new WriteLocker(_lock, this, HALF_SECOND + ONE_SECOND);
155 WriteLocker writer2 = new WriteLocker(_lock, this, ONE_SECOND);
156 Thread t1 = new Thread(writer1);
157 Thread t2 = new Thread(writer2);
160 Thread.sleep(HALF_SECOND);
161 assertTrue(getWriterCount() == 1);
163 Thread.sleep(HALF_SECOND);
164 assertTrue(getWriterCount() == 1); // first writer still
167 Thread.sleep(ONE_SECOND);
169 // at t = 2, the second writer still must have
171 assertTrue(getWriterCount() == 1);
177 * Verify that after the first reader acquires a lock, a subsequent writer
178 * can only acquire the lock after the reader has released it.
180 * @throws InterruptedException May not occur.
182 public void testReadWrite1() throws InterruptedException {
183 ReadLocker readLocker = new ReadLocker(_lock, this, TWO_SECONDS);
184 Thread t1 = new Thread(readLocker);
185 WriteLocker writeLocker = new WriteLocker(_lock, this, TWO_SECONDS);
186 Thread t2 = new Thread(writeLocker);
188 t1.start(); // acquire read lock
189 Thread.sleep(HALF_SECOND);
190 assertTrue(getReaderCount() == 1);
192 Thread.sleep(HALF_SECOND);
194 // 1 second underway, reader still holding the
195 // lock so write lock cannot be acquired.
196 assertTrue(getReaderCount() == 1);
197 assertTrue(getWriterCount() == 0);
198 Thread.sleep(ONE_SECOND + HALF_SECOND);
200 // 2.5 seconds underway, read lock released and
201 // write lock must be acquired.
202 assertTrue("Wrong no. of readers: " + getReaderCount(),
203 getReaderCount() == 0);
204 assertTrue(getWriterCount() == 1);
210 * Verify that when multiple readers have acquired a read lock, the writer
211 * can only acquire the lock after all readers have released it.
213 * @throws InterruptedException May not occur.
215 public void testReadWrite2() throws InterruptedException {
216 ReadLocker readLocker1 = new ReadLocker(_lock, this, TWO_SECONDS + HALF_SECOND);
217 ReadLocker readLocker2 = new ReadLocker(_lock, this, TWO_SECONDS + HALF_SECOND);
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
257 * only acquire the lock after the writer has released it.
259 * @throws InterruptedException May not occur.
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
290 * the read write lock checks the locking correctly.
291 * Strictly speaking, these checks wouldn't be necessary
292 * because it involves the contract of the ReadWriteLock which
293 * must be obeyed by users of the ReadWriteLock. Nevertheless,
294 * this is tested anyway to be absolutely sure.
298 * Acquire a read lock from one thread, release it from another. Verify
299 * that a RuntimeException is thrown.
301 * @throws InterruptedException May not occur.
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 May not occur.
330 public void testReleaseWriteFromWrongThread() throws InterruptedException {
334 t1 = new Thread(new Runnable() {
336 ReadWriteLockTest.this._lock.acquireWrite();
340 Thread.sleep(ONE_SECOND); // wait until thread is started
341 _lock.releaseWrite(); // release lock from wrong thread.
342 } catch (RuntimeException e) {
352 * Try to acquire a read lock multiple times. Verify that a
353 * RuntimeException is thrown.
355 public void testAcquireReadTwice() {
359 } catch (RuntimeException e) {
368 * Try to acquire a write lock multiple times. Verify that a
369 * RuntimeException is thrown.
371 public void testAcquireWriteTwice() {
373 _lock.acquireWrite();
374 _lock.acquireWrite();
375 } catch (RuntimeException e) {
383 * Acquire the lock for reading and directly afterwards acquire it for
384 * writing. Verify that a RuntimeException is thrown.
386 public void testAcquireReadFollowedByWrite() {
389 _lock.acquireWrite();
390 } catch (RuntimeException e) {
398 * Acquire the lock for writing and directly afterwards acquire it for
399 * reading. Verify that a RuntimeException is thrown.
401 public void testAcquireWriteFollowedByRead() {
403 _lock.acquireWrite();
405 } catch (RuntimeException e) {
413 * Acquire a read lock and release it as a write lock. Verify that a
414 * RuntimeException is thrown.
416 public void testAcquireReadFollowedByReleaseaWrite() {
419 _lock.releaseWrite();
420 } catch (RuntimeException e) {
428 * Acquire a write lock and release it as a read lock. Verify that a
429 * RuntimeException is thrown.
431 public void testAcquireWriteFollowedByReleaseRead() {
433 _lock.acquireWrite();
435 } catch (RuntimeException e) {
445 * ReadLocker acquires a read lock and performs a callback when the lock as
446 * been acquired, sleeps for a designated amount of time, releases the read
447 * lock, and 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 Assert.fail("ReadLocker thread was interrupted."
469 + Thread.currentThread());
473 _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;
496 _lock.acquireWrite();
497 _lockTest.incrementWriterCount();
500 Thread.sleep(_sleepTime);
501 } catch (InterruptedException e) {
502 Assert.fail("WriteLocker thread was interrupted: "
503 + Thread.currentThread());
506 _lock.releaseWrite();
507 _lockTest.decrementWriterCount();