e0d22f774923620b3e2a9ade3c7042bc4bf51b2e
[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     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 [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
257      * only acquire the lock after the writer has released it.
258      *
259      * @throws InterruptedException May not occur.
260      */
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);
266
267         t2.start(); // acquire write lock
268         Thread.sleep(HALF_SECOND);
269         assertTrue(getWriterCount() == 1);
270         t1.start();
271         Thread.sleep(HALF_SECOND);
272
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);
278
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);
284         t1.join();
285         t2.join();
286     }
287
288     /*
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.
295      */
296
297     /**
298      * Acquire a read lock from one thread, release it from another. Verify
299      * that a RuntimeException is thrown.
300      *
301      * @throws InterruptedException May not occur.
302      */
303     public void testReleaseReadFromWrongThread() throws InterruptedException {
304         Thread t1 = null;
305
306         try {
307             t1 = new Thread(new Runnable() {
308                         public void run() {
309                             ReadWriteLockTest.this.lock.acquireRead();
310                         }
311                     });
312             t1.start();
313             Thread.sleep(ONE_SECOND); // wait until thread is started
314             lock.releaseRead(); // release lock from wrong thread.
315         } catch (RuntimeException e) {
316             return; // ok
317         } finally {
318             t1.join();
319         }
320
321         fail();
322     }
323
324     /**
325      * Acquire a write lock from one thread, release it from another. Verify
326      * that a RuntimeException is thrown.
327      *
328      * @throws InterruptedException May not occur.
329      */
330     public void testReleaseWriteFromWrongThread() throws InterruptedException {
331         Thread t1 = null;
332
333         try {
334             t1 = new Thread(new Runnable() {
335                         public void run() {
336                             ReadWriteLockTest.this.lock.acquireWrite();
337                         }
338                     });
339             t1.start();
340             Thread.sleep(ONE_SECOND); // wait until thread is started
341             lock.releaseWrite(); // release lock from wrong thread.
342         } catch (RuntimeException e) {
343             return; // ok
344         } finally {
345             t1.join();
346         }
347
348         fail();
349     }
350
351     /**
352      * Try to acquire a read lock multiple times. Verify that a
353      * RuntimeException is thrown.
354      */
355     public void testAcquireReadTwice() {
356         try {
357             lock.acquireRead();
358             lock.acquireRead();
359         } catch (RuntimeException e) {
360             // ok
361             return;
362         }
363
364         fail();
365     }
366
367     /**
368      * Try to acquire a write lock multiple times. Verify that a
369      * RuntimeException is thrown.
370      */
371     public void testAcquireWriteTwice() {
372         try {
373             lock.acquireWrite();
374             lock.acquireWrite();
375         } catch (RuntimeException e) {
376             return; // ok
377         }
378
379         fail();
380     }
381
382     /**
383      * Acquire the lock for reading and directly afterwards acquire it for
384      * writing. Verify that a RuntimeException is thrown.
385      */
386     public void testAcquireReadFollowedByWrite() {
387         try {
388             lock.acquireRead();
389             lock.acquireWrite();
390         } catch (RuntimeException e) {
391             return; // ok
392         }
393
394         fail();
395     }
396
397     /**
398      * Acquire the lock for writing and directly afterwards acquire it for
399      * reading. Verify that a RuntimeException is thrown.
400      */
401     public void testAcquireWriteFollowedByRead() {
402         try {
403             lock.acquireWrite();
404             lock.acquireRead();
405         } catch (RuntimeException e) {
406             return; // ok
407         }
408
409         fail();
410     }
411
412     /**
413      * Acquire a read lock and release it as a write lock. Verify that a
414      * RuntimeException is thrown.
415      */
416     public void testAcquireReadFollowedByReleaseaWrite() {
417         try {
418             lock.acquireRead();
419             lock.releaseWrite();
420         } catch (RuntimeException e) {
421             return; // ok
422         }
423
424         fail();
425     }
426
427     /**
428      * Acquire a write lock and release it as a read lock. Verify that a
429      * RuntimeException is thrown.
430      */
431     public void testAcquireWriteFollowedByReleaseRead() {
432         try {
433             lock.acquireWrite();
434             lock.releaseRead();
435         } catch (RuntimeException e) {
436             return; // ok
437         }
438
439         fail();
440     }
441 }
442
443
444 /**
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.
448  */
449 class ReadLocker implements Runnable {
450     private ReadWriteLock     lock;
451     private ReadWriteLockTest lockTest;
452     private int               sleepTime;
453
454     public ReadLocker(ReadWriteLock aLock, ReadWriteLockTest aLockTest,
455         int aSleepTime) {
456         lock          = aLock;
457         lockTest      = aLockTest;
458         sleepTime     = aSleepTime;
459     }
460
461     public void run() {
462         lock.acquireRead();
463         lockTest.incrementReaderCount();
464
465         try {
466             Thread.sleep(sleepTime);
467         } catch (InterruptedException e) {
468             Assert.fail("ReadLocker thread was interrupted."
469                 + Thread.currentThread());
470         }
471
472         lock.releaseRead();
473         lockTest.decrementReaderCount();
474     }
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 }