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