Removed DOCUMENT ME comments that were generated and applied source code
[utils] / support / general / src / test / java / org / wamblee / concurrency / ReadWriteLockTest.java
1 /*
2  * Copyright 2005 the original author or authors.
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package org.wamblee.concurrency;
17
18 import junit.framework.Assert;
19 import junit.framework.TestCase;
20
21 /**
22  * Testing the read-write lock class. Note: in case of problems, test cases
23  * could hang.
24  * 
25  * @see ReadWriteLock
26  */
27 public class ReadWriteLockTest extends TestCase {
28     private static final int HALF_SECOND = 500;
29
30     private static final int ONE_SECOND = 1000;
31
32     private static final int TWO_SECONDS = 2000;
33     private ReadWriteLock lock;
34     private int nReaders;
35     private int nWriters;
36
37     /**
38      * Constructor for ReadWriteLockTest.
39      * 
40      * @param aName
41      */
42     public ReadWriteLockTest(String aName) {
43         super(aName);
44     }
45
46     private synchronized int getReaderCount() {
47         return nReaders;
48     }
49
50     private synchronized int getWriterCount() {
51         return nWriters;
52     }
53
54     synchronized void incrementReaderCount() {
55         nReaders++;
56     }
57
58     synchronized void incrementWriterCount() {
59         nWriters++;
60     }
61
62     synchronized void decrementReaderCount() {
63         nReaders--;
64     }
65
66     synchronized void decrementWriterCount() {
67         nWriters--;
68     }
69
70     /*
71      * @see TestCase#setUp()
72      */
73     protected void setUp() throws Exception {
74         super.setUp();
75         lock = new ReadWriteLock();
76     }
77
78     /*
79      * @see TestCase#tearDown()
80      */
81     protected void tearDown() throws Exception {
82         lock = null;
83         super.tearDown();
84     }
85
86     /**
87      * Acquire and release a read lock.
88      */
89     public void testRead() {
90         lock.acquireRead();
91         lock.releaseRead();
92     }
93
94     /**
95      * Acquire and release a write lock.
96      */
97     public void testWrite() {
98         lock.acquireWrite();
99         lock.releaseWrite();
100     }
101
102     /**
103      * Verify concurrent access by multiple readers is possible.
104      * 
105      * @throws InterruptedException
106      *             May not occur.
107      */
108     public void testMultipleReaders() throws InterruptedException {
109         Runnable runnable = new ReadLocker(lock, this, TWO_SECONDS);
110
111         Thread t1 = new Thread(runnable);
112         t1.start();
113
114         Thread t2 = new Thread(runnable);
115         t2.start();
116         Thread.sleep(ONE_SECOND);
117         assertTrue("Not enough readers!", getReaderCount() == 2);
118         t1.join();
119         t2.join();
120     }
121
122     /**
123      * Verify that only one writer at a time can acquire the write lock.
124      * 
125      * @throws InterruptedException
126      *             May not occur.
127      */
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);
132
133         t1.start();
134         t2.start();
135         Thread.sleep(HALF_SECOND);
136         assertTrue("Wrong writer count: " + getWriterCount(),
137             getWriterCount() == 1);
138         t1.join();
139         t2.join();
140     }
141
142     /**
143      * Verify that multiple writers cannot acquire the write lock concurrently.
144      * 
145      * @throws InterruptedException
146      *             May not occur.
147      */
148     public void testMultipleWriters() throws InterruptedException {
149         WriteLocker writer1 = new WriteLocker(lock, this, HALF_SECOND +
150             ONE_SECOND);
151         WriteLocker writer2 = new WriteLocker(lock, this, ONE_SECOND);
152         Thread t1 = new Thread(writer1);
153         Thread t2 = new Thread(writer2);
154
155         t1.start();
156         Thread.sleep(HALF_SECOND);
157         assertTrue(getWriterCount() == 1);
158         t2.start();
159         Thread.sleep(HALF_SECOND);
160         assertTrue(getWriterCount() == 1); // first writer still
161
162         // has the lock.
163         Thread.sleep(ONE_SECOND);
164
165         // at t = 2, the second writer still must have
166         // a lock.
167         assertTrue(getWriterCount() == 1);
168         t1.join();
169         t2.join();
170     }
171
172     /**
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.
175      * 
176      * @throws InterruptedException
177      *             May not occur.
178      */
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);
184
185         t1.start(); // acquire read lock
186         Thread.sleep(HALF_SECOND);
187         assertTrue(getReaderCount() == 1);
188         t2.start();
189         Thread.sleep(HALF_SECOND);
190
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);
196
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);
202         t1.join();
203         t2.join();
204     }
205
206     /**
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.
209      * 
210      * @throws InterruptedException
211      *             May not occur.
212      */
213     public void testReadWrite2() throws InterruptedException {
214         ReadLocker readLocker1 = new ReadLocker(lock, this, TWO_SECONDS +
215             HALF_SECOND);
216         ReadLocker readLocker2 = new ReadLocker(lock, this, TWO_SECONDS +
217             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);
222
223         t1.start(); // acquire read lock [0, 2.5]
224         Thread.sleep(ONE_SECOND);
225         // t = 1
226         assertTrue(getReaderCount() == 1);
227         t2.start(); // acquire read lock [1, 3.5]
228         Thread.sleep(HALF_SECOND);
229         // t = 1.5
230         assertTrue(getReaderCount() == 2);
231         t3.start(); // write lock
232         Thread.sleep(HALF_SECOND);
233
234         // 2 seconds,
235         assertTrue(getReaderCount() == 2);
236         assertTrue(getWriterCount() == 0);
237         Thread.sleep(ONE_SECOND);
238
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);
244
245         // 4 seconds underway, write lock must have
246         // been acquired.
247         assertTrue(getReaderCount() == 0);
248         assertTrue(getWriterCount() == 1);
249
250         t1.join();
251         t2.join();
252         t3.join();
253     }
254
255     /**
256      * Verify that after a writer acquires a lock, a subsequent reader can only
257      * acquire the lock after the writer has released it.
258      * 
259      * @throws InterruptedException
260      *             May not occur.
261      */
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);
267
268         t2.start(); // acquire write lock
269         Thread.sleep(HALF_SECOND);
270         assertTrue(getWriterCount() == 1);
271         t1.start();
272         Thread.sleep(HALF_SECOND);
273
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);
279
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);
285         t1.join();
286         t2.join();
287     }
288
289     /*
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.
295      */
296
297     /**
298      * Acquire a read lock from one thread, release it from another. Verify that
299      * a RuntimeException is thrown.
300      * 
301      * @throws InterruptedException
302      *             May not occur.
303      */
304     public void testReleaseReadFromWrongThread() throws InterruptedException {
305         Thread t1 = null;
306
307         try {
308             t1 = new Thread(new Runnable() {
309                 public void run() {
310                     ReadWriteLockTest.this.lock.acquireRead();
311                 }
312             });
313             t1.start();
314             Thread.sleep(ONE_SECOND); // wait until thread is started
315             lock.releaseRead(); // release lock from wrong thread.
316         } catch (RuntimeException e) {
317             return; // ok
318         } finally {
319             t1.join();
320         }
321
322         fail();
323     }
324
325     /**
326      * Acquire a write lock from one thread, release it from another. Verify
327      * that a RuntimeException is thrown.
328      * 
329      * @throws InterruptedException
330      *             May not occur.
331      */
332     public void testReleaseWriteFromWrongThread() throws InterruptedException {
333         Thread t1 = null;
334
335         try {
336             t1 = new Thread(new Runnable() {
337                 public void run() {
338                     ReadWriteLockTest.this.lock.acquireWrite();
339                 }
340             });
341             t1.start();
342             Thread.sleep(ONE_SECOND); // wait until thread is started
343             lock.releaseWrite(); // release lock from wrong thread.
344         } catch (RuntimeException e) {
345             return; // ok
346         } finally {
347             t1.join();
348         }
349
350         fail();
351     }
352
353     /**
354      * Try to acquire a read lock multiple times. Verify that a RuntimeException
355      * is thrown.
356      */
357     public void testAcquireReadTwice() {
358         try {
359             lock.acquireRead();
360             lock.acquireRead();
361         } catch (RuntimeException e) {
362             // ok
363             return;
364         }
365
366         fail();
367     }
368
369     /**
370      * Try to acquire a write lock multiple times. Verify that a
371      * RuntimeException is thrown.
372      */
373     public void testAcquireWriteTwice() {
374         try {
375             lock.acquireWrite();
376             lock.acquireWrite();
377         } catch (RuntimeException e) {
378             return; // ok
379         }
380
381         fail();
382     }
383
384     /**
385      * Acquire the lock for reading and directly afterwards acquire it for
386      * writing. Verify that a RuntimeException is thrown.
387      */
388     public void testAcquireReadFollowedByWrite() {
389         try {
390             lock.acquireRead();
391             lock.acquireWrite();
392         } catch (RuntimeException e) {
393             return; // ok
394         }
395
396         fail();
397     }
398
399     /**
400      * Acquire the lock for writing and directly afterwards acquire it for
401      * reading. Verify that a RuntimeException is thrown.
402      */
403     public void testAcquireWriteFollowedByRead() {
404         try {
405             lock.acquireWrite();
406             lock.acquireRead();
407         } catch (RuntimeException e) {
408             return; // ok
409         }
410
411         fail();
412     }
413
414     /**
415      * Acquire a read lock and release it as a write lock. Verify that a
416      * RuntimeException is thrown.
417      */
418     public void testAcquireReadFollowedByReleaseaWrite() {
419         try {
420             lock.acquireRead();
421             lock.releaseWrite();
422         } catch (RuntimeException e) {
423             return; // ok
424         }
425
426         fail();
427     }
428
429     /**
430      * Acquire a write lock and release it as a read lock. Verify that a
431      * RuntimeException is thrown.
432      */
433     public void testAcquireWriteFollowedByReleaseRead() {
434         try {
435             lock.acquireWrite();
436             lock.releaseRead();
437         } catch (RuntimeException e) {
438             return; // ok
439         }
440
441         fail();
442     }
443 }
444
445 /**
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.
449  */
450 class ReadLocker implements Runnable {
451     private ReadWriteLock lock;
452     private ReadWriteLockTest lockTest;
453     private int sleepTime;
454
455     public ReadLocker(ReadWriteLock aLock, ReadWriteLockTest aLockTest,
456         int aSleepTime) {
457         lock = aLock;
458         lockTest = aLockTest;
459         sleepTime = aSleepTime;
460     }
461
462     public void run() {
463         lock.acquireRead();
464         lockTest.incrementReaderCount();
465
466         try {
467             Thread.sleep(sleepTime);
468         } catch (InterruptedException e) {
469             Assert.fail("ReadLocker thread was interrupted." +
470                 Thread.currentThread());
471         }
472
473         lock.releaseRead();
474         lockTest.decrementReaderCount();
475     }
476 }
477
478 /**
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.
482  */
483 class WriteLocker implements Runnable {
484     private ReadWriteLock lock;
485     private ReadWriteLockTest lockTest;
486     private int sleepTime;
487
488     public WriteLocker(ReadWriteLock aLock, ReadWriteLockTest aLockTest,
489         int aSleepTime) {
490         lock = aLock;
491         lockTest = aLockTest;
492         sleepTime = aSleepTime;
493     }
494
495     public void run() {
496         lock.acquireWrite();
497         lockTest.incrementWriterCount();
498
499         try {
500             Thread.sleep(sleepTime);
501         } catch (InterruptedException e) {
502             Assert.fail("WriteLocker thread was interrupted: " +
503                 Thread.currentThread());
504         }
505
506         lock.releaseWrite();
507         lockTest.decrementWriterCount();
508     }
509 }