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