KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > transaction > locking > GenericLockTest


1 /*
2  * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//transaction/src/test/org/apache/commons/transaction/locking/GenericLockTest.java,v 1.12 2005/01/13 16:44:03 ozeigermann Exp $
3  * $Revision$
4  * $Date: 2005-02-26 14:16:14 +0100 (Sa, 26 Feb 2005) $
5  *
6  * ====================================================================
7  *
8  * Copyright 2004 The Apache Software Foundation
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  *
22  */

23
24 package org.apache.commons.transaction.locking;
25
26 import java.io.PrintWriter JavaDoc;
27
28 import junit.framework.Test;
29 import junit.framework.TestCase;
30 import junit.framework.TestSuite;
31
32 import org.apache.commons.transaction.util.LoggerFacade;
33 import org.apache.commons.transaction.util.PrintWriterLogger;
34 import org.apache.commons.transaction.util.RendezvousBarrier;
35 import org.apache.commons.transaction.util.TurnBarrier;
36
37 /**
38  * Tests for generic locks.
39  *
40  * @version $Revision$
41  */

42 public class GenericLockTest extends TestCase {
43
44     private static final LoggerFacade sLogger = new PrintWriterLogger(new PrintWriter JavaDoc(System.out),
45             GenericLockTest.class.getName(), false);
46
47     protected static final int READ_LOCK = 1;
48     protected static final int WRITE_LOCK = 2;
49     
50     private static final int CONCURRENT_TESTS = 25;
51     
52     protected static final long TIMEOUT = 1000000;
53     
54     private static int deadlockCnt = 0;
55     private static String JavaDoc first = null;
56
57     public static Test suite() {
58         TestSuite suite = new TestSuite(GenericLockTest.class);
59         return suite;
60     }
61
62     public static void main(java.lang.String JavaDoc[] args) {
63         junit.textui.TestRunner.run(suite());
64     }
65
66     public GenericLockTest(String JavaDoc testName) {
67         super(testName);
68     }
69
70     // we do not wait, as we only want the check the results and do not want real locking
71
protected boolean acquireNoWait(GenericLock lock, String JavaDoc owner, int targetLockLevel) {
72         try {
73             return lock.acquire(owner, targetLockLevel, false, true, -1);
74         } catch (InterruptedException JavaDoc e) {
75             return false;
76         }
77     }
78
79     public void testBasic() throws Throwable JavaDoc {
80
81         sLogger.logInfo("\n\nChecking basic map features\n\n");
82
83         String JavaDoc owner1 = "owner1";
84         String JavaDoc owner2 = "owner2";
85         String JavaDoc owner3 = "owner3";
86         
87         // a read / write lock
88
GenericLock lock = new GenericLock("Test read write lock", WRITE_LOCK, sLogger);
89         
90         // of course more than one can read
91
boolean canRead1 = acquireNoWait(lock, owner1, READ_LOCK);
92         assertTrue(canRead1);
93         boolean canRead2 = acquireNoWait(lock, owner2, READ_LOCK);
94         assertTrue(canRead2);
95         
96         // as there already are read locks, this write should not be possible
97
boolean canWrite3 = acquireNoWait(lock, owner3, WRITE_LOCK);
98         assertFalse(canWrite3);
99         
100         // release one read lock
101
lock.release(owner2);
102         // this should not change anything with the write as there is still one read lock left
103
canWrite3 = acquireNoWait(lock, owner3, WRITE_LOCK);
104         assertFalse(canWrite3);
105
106         // release the other and final read lock as well
107
lock.release(owner1);
108         // no we should be able to get write access
109
canWrite3 = acquireNoWait(lock, owner3, WRITE_LOCK);
110         assertTrue(canWrite3);
111         // but of course no more read access
112
canRead2 = acquireNoWait(lock, owner2, READ_LOCK);
113         assertFalse(canRead2);
114         
115         // relase the write lock and make sure we can read again
116
lock.release(owner3);
117         canRead2 = acquireNoWait(lock, owner2, READ_LOCK);
118         assertTrue(canRead2);
119         
120         // now we do something weired, we try to block all locks lower than write...
121
boolean canBlock3 = lock.acquire(owner3, WRITE_LOCK, false, GenericLock.COMPATIBILITY_SUPPORT, -1);
122         // which of course does not work, as there already is an incompatible read lock
123
assertFalse(canBlock3);
124         
125         // ok, release read lock (no we have no more locks) and try again
126
lock.release(owner2);
127         canBlock3 = lock.acquire(owner3, WRITE_LOCK, false, GenericLock.COMPATIBILITY_SUPPORT, -1);
128         // which now should work creating an ordinary lock
129
assertTrue(canBlock3);
130         
131         // as this just an ordinary lock, we should not get a read lock:
132
canRead1 = acquireNoWait(lock, owner1, READ_LOCK);
133         assertFalse(canRead1);
134         
135         // this is the trick now, we *can* get an addtional write lock with this request as it has
136
// the same level as the write lock already set. This works, as we do not care for the
137
// write lock level, but only want to inhibit the read lock:
138
boolean canBlock2 = lock.acquire(owner2, WRITE_LOCK, false, GenericLock.COMPATIBILITY_SUPPORT, -1);
139         assertTrue(canBlock2);
140         
141         // now if we release one of the blocks supporting each other we still should not get a
142
// read lock
143
lock.release(owner3);
144         canRead1 = acquireNoWait(lock, owner1, READ_LOCK);
145         assertFalse(canRead1);
146
147         // but of course after we release the second as well
148
lock.release(owner2);
149         canRead1 = acquireNoWait(lock, owner1, READ_LOCK);
150         assertTrue(canRead1);
151     }
152
153     public void testTimeout() {
154         
155         sLogger.logInfo("\n\nChecking timeouts\n\n");
156
157         ReadWriteLockManager lockManager = new ReadWriteLockManager(sLogger, 1000);
158         boolean timedOut = false;
159         try {
160             lockManager.readLock("owner1", "resource");
161             lockManager.writeLock("owner2", "resource");
162         } catch (LockException le) {
163             assertEquals(le.getCode(), LockException.CODE_TIMED_OUT);
164             timedOut = true;
165         }
166         assertTrue(timedOut);
167         lockManager = new ReadWriteLockManager(sLogger, 100);
168         timedOut = false;
169         try {
170             lockManager.readLock("owner1", "resource");
171             lockManager.writeLock("owner2", "resource");
172         } catch (LockException le) {
173             assertEquals(le.getCode(), LockException.CODE_TIMED_OUT);
174             timedOut = true;
175         }
176         assertTrue(timedOut);
177         lockManager = new ReadWriteLockManager(sLogger, 0);
178         timedOut = false;
179         try {
180             lockManager.readLock("owner1", "resource");
181             lockManager.writeLock("owner2", "resource");
182         } catch (LockException le) {
183             assertEquals(le.getCode(), LockException.CODE_TIMED_OUT);
184             timedOut = true;
185         }
186         assertTrue(timedOut);
187     }
188     
189
190     public void testDeadlock() throws Throwable JavaDoc {
191
192         sLogger.logInfo("\n\nChecking deadlock detection\n\n");
193
194         final String JavaDoc owner1 = "owner1";
195         final String JavaDoc owner2 = "owner2";
196
197         final String JavaDoc res1 = "res1";
198         final String JavaDoc res2 = "res2";
199
200         // a read / write lock
201
final ReadWriteLockManager manager = new ReadWriteLockManager(sLogger, TIMEOUT);
202         
203         final RendezvousBarrier restart = new RendezvousBarrier("restart",
204                 TIMEOUT, sLogger);
205
206         for (int i = 0; i < CONCURRENT_TESTS; i++) {
207
208            System.out.print(".");
209             
210             final RendezvousBarrier deadlockBarrier1 = new RendezvousBarrier("deadlock1" + i,
211                     TIMEOUT, sLogger);
212
213             Thread JavaDoc deadlock = new Thread JavaDoc(new Runnable JavaDoc() {
214                 public void run() {
215                     try {
216                         // first both threads get a lock, this one on res2
217
manager.writeLock(owner2, res2);
218                         synchronized (deadlockBarrier1) {
219                             deadlockBarrier1.meet();
220                             deadlockBarrier1.reset();
221                         }
222                         // if I am first, the other thread will be dead, i.e.
223
// exactly one
224
manager.writeLock(owner2, res1);
225                     } catch (LockException le) {
226                         assertEquals(le.getCode(), LockException.CODE_DEADLOCK_VICTIM);
227                         deadlockCnt++;
228                     } catch (InterruptedException JavaDoc ie) {
229                     } finally {
230                         manager.releaseAll(owner2);
231                         try {
232                             synchronized (restart) {
233                                 restart.meet();
234                                 restart.reset();
235                             }
236                             } catch (InterruptedException JavaDoc ie) {}
237                     }
238                 }
239             }, "Deadlock Thread");
240
241             deadlock.start();
242
243             try {
244                 // first both threads get a lock, this one on res2
245
manager.readLock(owner1, res1);
246                 synchronized (deadlockBarrier1) {
247                     deadlockBarrier1.meet();
248                     deadlockBarrier1.reset();
249                 }
250                 // if I am first, the other thread will be dead, i.e. exactly
251
// one
252
manager.readLock(owner1, res2);
253             } catch (LockException le) {
254                 assertEquals(le.getCode(), LockException.CODE_DEADLOCK_VICTIM);
255                 deadlockCnt++;
256             } finally {
257                 manager.releaseAll(owner1);
258                 synchronized (restart) {
259                     restart.meet();
260                     restart.reset();
261                 }
262             }
263
264             // XXX in special scenarios the current implementation might cause both
265
// owners to be deadlock victims
266
if (deadlockCnt != 1) {
267                 sLogger.logWarning("More than one thread was deadlock victim!");
268             }
269             assertTrue(deadlockCnt >= 1);
270             deadlockCnt = 0;
271         }
272     }
273
274     /*
275      *
276      * Test detection of an indirect deadlock:
277      *
278      * Owner Owner Owner
279      * Step #1 #2 #3
280      * 1 read res1 (ok)
281      * 2 read res2 (ok)
282      * 3 read res3 (ok)
283      * 4 write res2 (blocked because of #2)
284      * 5 write res1
285      * (blocked
286      * because of #1)
287      * 6 write res3
288      * (blocked
289      * because #3)
290      *
291      * - Thread#1 waits for Thread#3 on res3
292      * - Thread#2 waits for Thread#1 on res1
293      * - Thread#3 waits for Thread#2 on res2
294      *
295      * This needs recursion of the deadlock detection algorithm
296      *
297      */

298     public void testIndirectDeadlock() throws Throwable JavaDoc {
299
300         sLogger.logInfo("\n\nChecking detection of indirect deadlock \n\n");
301
302         final String JavaDoc jamowner1 = "jamowner1";
303         final String JavaDoc jamowner2 = "jamowner2";
304
305         final String JavaDoc owner1 = "owner1";
306         final String JavaDoc owner2 = "owner2";
307         final String JavaDoc owner3 = "owner3";
308
309         final String JavaDoc res1 = "res1";
310         final String JavaDoc res2 = "res2";
311         final String JavaDoc res3 = "res3";
312
313         // a read / write lock
314
final ReadWriteLockManager manager = new ReadWriteLockManager(sLogger,
315                 TIMEOUT);
316
317         final RendezvousBarrier restart = new RendezvousBarrier("restart", 5, TIMEOUT, sLogger);
318
319         final TurnBarrier cb = new TurnBarrier("cb1", TIMEOUT, sLogger, 1);
320
321         for (int i = 0; i < CONCURRENT_TESTS; i++) {
322             
323             System.out.print(".");
324
325             // thread that accesses lock of res1 just to cause interference and
326
// possibly detect concurrency problems
327
Thread JavaDoc jamThread1 = new Thread JavaDoc(new Runnable JavaDoc() {
328                 public void run() {
329                     try {
330                         for (int i = 0; i < 10; i++) {
331                             manager.readLock(jamowner1, res1);
332                             Thread.sleep(10);
333                             manager.releaseAll(jamowner1);
334                             Thread.sleep(10);
335                             manager.writeLock(jamowner1, res1);
336                             Thread.sleep(10);
337                             manager.releaseAll(jamowner1);
338                             Thread.sleep(10);
339                         }
340                     } catch (LockException le) {
341                         fail("Jam Thread should not fail");
342                     } catch (InterruptedException JavaDoc ie) {
343                     } finally {
344                         manager.releaseAll(jamowner1);
345                         synchronized (restart) {
346                             try {
347                                 synchronized (restart) {
348                                     restart.meet();
349                                     restart.reset();
350                                 }
351                                 } catch (InterruptedException JavaDoc ie) {}
352                         }
353                     }
354                 }
355             }, "Jam Thread #1");
356
357             jamThread1.start();
358
359             // thread that accesses lock of res1 just to cause interference and
360
// possibly detect concurrency problems
361
Thread JavaDoc jamThread2 = new Thread JavaDoc(new Runnable JavaDoc() {
362                 public void run() {
363                     try {
364                         for (int i = 0; i < 10; i++) {
365                             manager.writeLock(jamowner2, res1);
366                             Thread.sleep(10);
367                             manager.releaseAll(jamowner2);
368                             Thread.sleep(10);
369                             manager.readLock(jamowner2, res1);
370                             Thread.sleep(10);
371                             manager.releaseAll(jamowner2);
372                             Thread.sleep(10);
373                         }
374                     } catch (LockException le) {
375                         fail("Jam Thread should not fail");
376                     } catch (InterruptedException JavaDoc ie) {
377                     } finally {
378                         manager.releaseAll(jamowner2);
379                         synchronized (restart) {
380                             try {
381                                 synchronized (restart) {
382                                     restart.meet();
383                                     restart.reset();
384                                 }
385                                 } catch (InterruptedException JavaDoc ie) {}
386                         }
387                     }
388                 }
389             }, "Jam Thread #2");
390
391             jamThread2.start();
392
393             Thread JavaDoc t1 = new Thread JavaDoc(new Runnable JavaDoc() {
394                 public void run() {
395                     try {
396                         cb.waitForTurn(2);
397                         manager.readLock(owner2, res2);
398                         cb.signalTurn(3);
399                         cb.waitForTurn(5);
400                         synchronized (manager.getLock(res1)) {
401                             cb.signalTurn(6);
402                             manager.writeLock(owner2, res1);
403                         }
404                     } catch (LockException le) {
405                         assertEquals(le.getCode(), LockException.CODE_DEADLOCK_VICTIM);
406                         deadlockCnt++;
407                     } catch (InterruptedException JavaDoc ie) {
408                     } finally {
409                         manager.releaseAll(owner2);
410                         synchronized (restart) {
411                             try {
412                                 synchronized (restart) {
413                                     restart.meet();
414                                     restart.reset();
415                                 }
416                                 } catch (InterruptedException JavaDoc ie) {}
417                         }
418                     }
419                 }
420             }, "Thread #1");
421
422             t1.start();
423
424             Thread JavaDoc t2 = new Thread JavaDoc(new Runnable JavaDoc() {
425                 public void run() {
426                     try {
427                         cb.waitForTurn(3);
428                         manager.readLock(owner3, res3);
429                         synchronized (manager.getLock(res2)) {
430                             cb.signalTurn(5);
431                             manager.writeLock(owner3, res2);
432                         }
433                     } catch (LockException le) {
434                         assertEquals(le.getCode(), LockException.CODE_DEADLOCK_VICTIM);
435                         deadlockCnt++;
436                     } catch (InterruptedException JavaDoc ie) {
437                     } finally {
438                         manager.releaseAll(owner3);
439                         synchronized (restart) {
440                             try {
441                                 synchronized (restart) {
442                                     restart.meet();
443                                     restart.reset();
444                                 }
445                                 } catch (InterruptedException JavaDoc ie) {}
446                         }
447                     }
448                 }
449             }, "Thread #2");
450
451             t2.start();
452
453             try {
454                 cb.waitForTurn(1);
455                 manager.readLock(owner1, res1);
456                 cb.signalTurn(2);
457                 cb.waitForTurn(6);
458                 manager.writeLock(owner1, res3);
459             } catch (LockException le) {
460                 assertEquals(le.getCode(), LockException.CODE_DEADLOCK_VICTIM);
461                 deadlockCnt++;
462             } catch (InterruptedException JavaDoc ie) {
463             } finally {
464                 manager.releaseAll(owner1);
465                 synchronized (restart) {
466                     try {
467                         synchronized (restart) {
468                             restart.meet();
469                             restart.reset();
470                         }
471                     } catch (InterruptedException JavaDoc ie) {
472                     }
473                 }
474             }
475
476             // XXX in special scenarios the current implementation might cause more than one
477
// owner to be a deadlock victim
478
if (deadlockCnt != 1) {
479                 sLogger.logWarning("\nMore than one thread was deadlock victim!\n");
480             }
481             assertTrue(deadlockCnt >= 1);
482             deadlockCnt = 0;
483             cb.reset();
484         }
485     }
486
487     /*
488      *
489      * Test shows the following
490      * - upgrade works with read locks no matter if they are acquired before or later (1-4)
491      * - write is blocked by read (5)
492      * - read is blocked by intention lock (6)
493      * - write lock coming from an intention lock always has preference over others (7)
494      *
495      *
496      * Owner Owner Owner
497      * Step #1 #2 #3
498      * 1 read (ok)
499      * 2 upgrade (ok)
500      * 3 release (ok)
501      * 4 read (ok)
502      * 5 write (blocked
503      * because of #1)
504      * 6 read (blocked
505      * because intention of #2)
506      * 7 release resumed
507      * 8 release resumed
508      * 9 release
509      */

510     public void testUpgrade() throws Throwable JavaDoc {
511
512         sLogger.logInfo("\n\nChecking upgrade and preference lock\n\n");
513         
514         final String JavaDoc owner1 = "owner1";
515         final String JavaDoc owner2 = "owner2";
516         final String JavaDoc owner3 = "owner3";
517
518         final String JavaDoc res1 = "res1";
519
520         // a read / write lock
521
final ReadWriteUpgradeLockManager manager = new ReadWriteUpgradeLockManager(sLogger,
522                 TIMEOUT);
523
524         final RendezvousBarrier restart = new RendezvousBarrier("restart", 3, TIMEOUT, sLogger);
525
526         final TurnBarrier cb = new TurnBarrier("cb1", TIMEOUT, sLogger, 1);
527
528         for (int i = 0; i < CONCURRENT_TESTS; i++) {
529             
530             System.out.print(".");
531
532             Thread JavaDoc t1 = new Thread JavaDoc(new Runnable JavaDoc() {
533                 public void run() {
534                     try {
535                         cb.waitForTurn(2);
536                         manager.upgradeLock(owner2, res1);
537                         cb.signalTurn(3);
538                         cb.waitForTurn(5);
539                         synchronized (manager.getLock(res1)) {
540                             cb.signalTurn(6);
541                             manager.writeLock(owner2, res1);
542                         }
543                         // we must always be first as we will be preferred over
544
// as I had the upgrade
545
// lock before
546
synchronized (this) {
547                             if (first == null)
548                                 first = owner2;
549                         }
550                         manager.releaseAll(owner2);
551                         synchronized (restart) {
552                             restart.meet();
553                             restart.reset();
554                         }
555                     } catch (InterruptedException JavaDoc ie) {
556                     }
557                 }
558             }, "Thread #1");
559
560             t1.start();
561
562             Thread JavaDoc t2 = new Thread JavaDoc(new Runnable JavaDoc() {
563                 public void run() {
564                     try {
565                         // I wait until the others are blocked
566
// when I release my single read lock, thread #1 always
567
// should be the
568
// next to get the lock as it is preferred over the main
569
// thread
570
// that only waits for a read lock
571
cb.waitForTurn(6);
572                         synchronized (manager.getLock(res1)) {
573                             cb.signalTurn(7);
574                             manager.readLock(owner3, res1);
575                         }
576                         synchronized (this) {
577                             if (first == null)
578                                 first = owner3;
579                         }
580                         manager.releaseAll(owner3);
581                         synchronized (restart) {
582                             restart.meet();
583                             restart.reset();
584                         }
585                     } catch (InterruptedException JavaDoc ie) {
586                     }
587                 }
588             }, "Thread #2");
589
590             t2.start();
591
592             cb.waitForTurn(1);
593             manager.readLock(owner1, res1);
594             cb.signalTurn(2);
595             cb.waitForTurn(3);
596             manager.release(owner1, res1);
597             manager.readLock(owner1, res1);
598             cb.signalTurn(5);
599             cb.waitForTurn(7);
600             synchronized (manager.getLock(res1)) {
601                 manager.releaseAll(owner1);
602             }
603             synchronized (restart) {
604                 restart.meet();
605                 restart.reset();
606             }
607
608             assertEquals(first, owner2);
609             first = null;
610             cb.reset();
611         }
612
613     }
614     
615     /*
616      *
617      * Test shows that two preference locks that are imcompatible do not cause a lock out
618      * which was the case with GenericLock 1.5
619      * Before the fix this test would dealock
620      *
621      * Owner Owner Owner
622      * Step #1 #2 #3
623      * 1 read (ok)
624      * 2 write preferred
625      * (blocked
626      * because of #1)
627      * 3 write preferred
628      * (blocked
629      * because of #1 and #2)
630      * 4 release
631      * 5 resumed or resumed
632      * (as both are preferred, problem
633      * is that that would exclude each other
634      * in the algorithm used)
635      * 6 released or released
636      * 7 resumed or resumed
637      * 8 released or released
638      *
639      *
640      */

641     public void testPreference() throws Throwable JavaDoc {
642
643         sLogger.logInfo("\n\nChecking incompatible preference locks\n\n");
644         
645         final String JavaDoc owner1 = "owner1";
646         final String JavaDoc owner2 = "owner2";
647         final String JavaDoc owner3 = "owner3";
648
649         final String JavaDoc res1 = "res1";
650
651         final ReadWriteLock lock = new ReadWriteLock(res1, sLogger);
652
653         final RendezvousBarrier restart = new RendezvousBarrier("restart", 3, TIMEOUT, sLogger);
654
655         final TurnBarrier cb = new TurnBarrier("cb1", TIMEOUT, sLogger, 1);
656
657         for (int i = 0; i < CONCURRENT_TESTS; i++) {
658             
659             System.out.print(".");
660
661             Thread JavaDoc t1 = new Thread JavaDoc(new Runnable JavaDoc() {
662                 public void run() {
663                     try {
664                         cb.waitForTurn(2);
665                         synchronized (lock) {
666                             cb.signalTurn(3);
667                             lock.acquire(owner2, ReadWriteLock.WRITE_LOCK, true,
668                                     GenericLock.COMPATIBILITY_REENTRANT, true, TIMEOUT);
669                         }
670                         lock.release(owner2);
671                         synchronized (restart) {
672                             restart.meet();
673                             restart.reset();
674                         }
675                     } catch (InterruptedException JavaDoc ie) {
676                     }
677                 }
678             }, "Thread #1");
679
680             t1.start();
681
682             Thread JavaDoc t2 = new Thread JavaDoc(new Runnable JavaDoc() {
683                 public void run() {
684                     try {
685