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