001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.transaction.locking; 018 019import java.io.PrintWriter; 020 021import junit.framework.Test; 022import junit.framework.TestCase; 023import junit.framework.TestSuite; 024 025import org.apache.commons.transaction.util.LoggerFacade; 026import org.apache.commons.transaction.util.PrintWriterLogger; 027import org.apache.commons.transaction.util.RendezvousBarrier; 028import org.apache.commons.transaction.util.TurnBarrier; 029 030/** 031 * Tests for generic locks. 032 * 033 * @version $Id: GenericLockTest.java 493628 2007-01-07 01:42:48Z joerg $ 034 */ 035public class GenericLockTest extends TestCase { 036 037 private static final LoggerFacade sLogger = new PrintWriterLogger(new PrintWriter(System.out), 038 GenericLockTest.class.getName(), false); 039 040 protected static final int READ_LOCK = 1; 041 protected static final int WRITE_LOCK = 2; 042 043 private static final int CONCURRENT_TESTS = 25; 044 045 protected static final long TIMEOUT = 1000000; 046 047 private static int deadlockCnt = 0; 048 private static String first = null; 049 050 public static Test suite() { 051 TestSuite suite = new TestSuite(GenericLockTest.class); 052 return suite; 053 } 054 055 public static void main(java.lang.String[] args) { 056 junit.textui.TestRunner.run(suite()); 057 } 058 059 public GenericLockTest(String testName) { 060 super(testName); 061 } 062 063 // we do not wait, as we only want the check the results and do not want real locking 064 protected boolean acquireNoWait(GenericLock lock, String owner, int targetLockLevel) { 065 try { 066 return lock.acquire(owner, targetLockLevel, false, true, -1); 067 } catch (InterruptedException e) { 068 return false; 069 } 070 } 071 072 public void testBasic() throws Throwable { 073 074 sLogger.logInfo("\n\nChecking basic map features\n\n"); 075 076 String owner1 = "owner1"; 077 String owner2 = "owner2"; 078 String owner3 = "owner3"; 079 080 // a read / write lock 081 GenericLock lock = new GenericLock("Test read write lock", WRITE_LOCK, sLogger); 082 083 // of course more than one can read 084 boolean canRead1 = acquireNoWait(lock, owner1, READ_LOCK); 085 assertTrue(canRead1); 086 boolean canRead2 = acquireNoWait(lock, owner2, READ_LOCK); 087 assertTrue(canRead2); 088 089 // as there already are read locks, this write should not be possible 090 boolean canWrite3 = acquireNoWait(lock, owner3, WRITE_LOCK); 091 assertFalse(canWrite3); 092 093 // release one read lock 094 lock.release(owner2); 095 // this should not change anything with the write as there is still one read lock left 096 canWrite3 = acquireNoWait(lock, owner3, WRITE_LOCK); 097 assertFalse(canWrite3); 098 099 // release the other and final read lock as well 100 lock.release(owner1); 101 // no we should be able to get write access 102 canWrite3 = acquireNoWait(lock, owner3, WRITE_LOCK); 103 assertTrue(canWrite3); 104 // but of course no more read access 105 canRead2 = acquireNoWait(lock, owner2, READ_LOCK); 106 assertFalse(canRead2); 107 108 // relase the write lock and make sure we can read again 109 lock.release(owner3); 110 canRead2 = acquireNoWait(lock, owner2, READ_LOCK); 111 assertTrue(canRead2); 112 113 // now we do something weired, we try to block all locks lower than write... 114 boolean canBlock3 = lock.acquire(owner3, WRITE_LOCK, false, GenericLock.COMPATIBILITY_SUPPORT, -1); 115 // which of course does not work, as there already is an incompatible read lock 116 assertFalse(canBlock3); 117 118 // ok, release read lock (no we have no more locks) and try again 119 lock.release(owner2); 120 canBlock3 = lock.acquire(owner3, WRITE_LOCK, false, GenericLock.COMPATIBILITY_SUPPORT, -1); 121 // which now should work creating an ordinary lock 122 assertTrue(canBlock3); 123 124 // as this just an ordinary lock, we should not get a read lock: 125 canRead1 = acquireNoWait(lock, owner1, READ_LOCK); 126 assertFalse(canRead1); 127 128 // this is the trick now, we *can* get an addtional write lock with this request as it has 129 // the same level as the write lock already set. This works, as we do not care for the 130 // write lock level, but only want to inhibit the read lock: 131 boolean canBlock2 = lock.acquire(owner2, WRITE_LOCK, false, GenericLock.COMPATIBILITY_SUPPORT, -1); 132 assertTrue(canBlock2); 133 134 // now if we release one of the blocks supporting each other we still should not get a 135 // read lock 136 lock.release(owner3); 137 canRead1 = acquireNoWait(lock, owner1, READ_LOCK); 138 assertFalse(canRead1); 139 140 // but of course after we release the second as well 141 lock.release(owner2); 142 canRead1 = acquireNoWait(lock, owner1, READ_LOCK); 143 assertTrue(canRead1); 144 } 145 146 public void testTimeout() { 147 148 sLogger.logInfo("\n\nChecking timeouts\n\n"); 149 150 ReadWriteLockManager lockManager = new ReadWriteLockManager(sLogger, 1000); 151 boolean timedOut = false; 152 try { 153 lockManager.readLock("owner1", "resource"); 154 lockManager.writeLock("owner2", "resource"); 155 } catch (LockException le) { 156 assertEquals(le.getCode(), LockException.CODE_TIMED_OUT); 157 timedOut = true; 158 } 159 assertTrue(timedOut); 160 lockManager = new ReadWriteLockManager(sLogger, 100); 161 timedOut = false; 162 try { 163 lockManager.readLock("owner1", "resource"); 164 lockManager.writeLock("owner2", "resource"); 165 } catch (LockException le) { 166 assertEquals(le.getCode(), LockException.CODE_TIMED_OUT); 167 timedOut = true; 168 } 169 assertTrue(timedOut); 170 lockManager = new ReadWriteLockManager(sLogger, 0); 171 timedOut = false; 172 try { 173 lockManager.readLock("owner1", "resource"); 174 lockManager.writeLock("owner2", "resource"); 175 } catch (LockException le) { 176 assertEquals(le.getCode(), LockException.CODE_TIMED_OUT); 177 timedOut = true; 178 } 179 assertTrue(timedOut); 180 } 181 182 183 public void testDeadlock() throws Throwable { 184 185 sLogger.logInfo("\n\nChecking deadlock detection\n\n"); 186 187 final String owner1 = "owner1"; 188 final String owner2 = "owner2"; 189 190 final String res1 = "res1"; 191 final String res2 = "res2"; 192 193 // a read / write lock 194 final ReadWriteLockManager manager = new ReadWriteLockManager(sLogger, TIMEOUT); 195 196 final RendezvousBarrier restart = new RendezvousBarrier("restart", 197 TIMEOUT, sLogger); 198 199 for (int i = 0; i < CONCURRENT_TESTS; i++) { 200 201 System.out.print("."); 202 203 final RendezvousBarrier deadlockBarrier1 = new RendezvousBarrier("deadlock1" + i, 204 TIMEOUT, sLogger); 205 206 Thread deadlock = new Thread(new Runnable() { 207 public void run() { 208 try { 209 // first both threads get a lock, this one on res2 210 manager.writeLock(owner2, res2); 211 synchronized (deadlockBarrier1) { 212 deadlockBarrier1.meet(); 213 deadlockBarrier1.reset(); 214 } 215 // if I am first, the other thread will be dead, i.e. 216 // exactly one 217 manager.writeLock(owner2, res1); 218 } catch (LockException le) { 219 assertEquals(le.getCode(), LockException.CODE_DEADLOCK_VICTIM); 220 deadlockCnt++; 221 } catch (InterruptedException ie) { 222 } finally { 223 manager.releaseAll(owner2); 224 try { 225 synchronized (restart) { 226 restart.meet(); 227 restart.reset(); 228 } 229 } catch (InterruptedException ie) {} 230 } 231 } 232 }, "Deadlock Thread"); 233 234 deadlock.start(); 235 236 try { 237 // first both threads get a lock, this one on res2 238 manager.readLock(owner1, res1); 239 synchronized (deadlockBarrier1) { 240 deadlockBarrier1.meet(); 241 deadlockBarrier1.reset(); 242 } 243 // if I am first, the other thread will be dead, i.e. exactly 244 // one 245 manager.readLock(owner1, res2); 246 } catch (LockException le) { 247 assertEquals(le.getCode(), LockException.CODE_DEADLOCK_VICTIM); 248 deadlockCnt++; 249 } finally { 250 manager.releaseAll(owner1); 251 synchronized (restart) { 252 restart.meet(); 253 restart.reset(); 254 } 255 } 256 257 // XXX in special scenarios the current implementation might cause both 258 // owners to be deadlock victims 259 if (deadlockCnt != 1) { 260 sLogger.logWarning("More than one thread was deadlock victim!"); 261 } 262 assertTrue(deadlockCnt >= 1); 263 deadlockCnt = 0; 264 } 265 } 266 267 /* 268 * 269 * Test detection of an indirect deadlock: 270 * 271 * Owner Owner Owner 272 * Step #1 #2 #3 273 * 1 read res1 (ok) 274 * 2 read res2 (ok) 275 * 3 read res3 (ok) 276 * 4 write res2 (blocked because of #2) 277 * 5 write res1 278 * (blocked 279 * because of #1) 280 * 6 write res3 281 * (blocked 282 * because #3) 283 * 284 * - Thread#1 waits for Thread#3 on res3 285 * - Thread#2 waits for Thread#1 on res1 286 * - Thread#3 waits for Thread#2 on res2 287 * 288 * This needs recursion of the deadlock detection algorithm 289 * 290 */ 291 public void testIndirectDeadlock() throws Throwable { 292 293 sLogger.logInfo("\n\nChecking detection of indirect deadlock \n\n"); 294 295 final String jamowner1 = "jamowner1"; 296 final String jamowner2 = "jamowner2"; 297 298 final String owner1 = "owner1"; 299 final String owner2 = "owner2"; 300 final String owner3 = "owner3"; 301 302 final String res1 = "res1"; 303 final String res2 = "res2"; 304 final String res3 = "res3"; 305 306 // a read / write lock 307 final ReadWriteLockManager manager = new ReadWriteLockManager(sLogger, 308 TIMEOUT); 309 310 final RendezvousBarrier restart = new RendezvousBarrier("restart", 5, TIMEOUT, sLogger); 311 312 final TurnBarrier cb = new TurnBarrier("cb1", TIMEOUT, sLogger, 1); 313 314 for (int i = 0; i < CONCURRENT_TESTS; i++) { 315 316 System.out.print("."); 317 318 // thread that accesses lock of res1 just to cause interference and 319 // possibly detect concurrency problems 320 Thread jamThread1 = new Thread(new Runnable() { 321 public void run() { 322 try { 323 for (int i = 0; i < 10; i++) { 324 manager.readLock(jamowner1, res1); 325 Thread.sleep(10); 326 manager.releaseAll(jamowner1); 327 Thread.sleep(10); 328 manager.writeLock(jamowner1, res1); 329 Thread.sleep(10); 330 manager.releaseAll(jamowner1); 331 Thread.sleep(10); 332 } 333 } catch (LockException le) { 334 fail("Jam Thread should not fail"); 335 } catch (InterruptedException ie) { 336 } finally { 337 manager.releaseAll(jamowner1); 338 synchronized (restart) { 339 try { 340 synchronized (restart) { 341 restart.meet(); 342 restart.reset(); 343 } 344 } catch (InterruptedException ie) {} 345 } 346 } 347 } 348 }, "Jam Thread #1"); 349 350 jamThread1.start(); 351 352 // thread that accesses lock of res1 just to cause interference and 353 // possibly detect concurrency problems 354 Thread jamThread2 = new Thread(new Runnable() { 355 public void run() { 356 try { 357 for (int i = 0; i < 10; i++) { 358 manager.writeLock(jamowner2, res1); 359 Thread.sleep(10); 360 manager.releaseAll(jamowner2); 361 Thread.sleep(10); 362 manager.readLock(jamowner2, res1); 363 Thread.sleep(10); 364 manager.releaseAll(jamowner2); 365 Thread.sleep(10); 366 } 367 } catch (LockException le) { 368 fail("Jam Thread should not fail"); 369 } catch (InterruptedException ie) { 370 } finally { 371 manager.releaseAll(jamowner2); 372 synchronized (restart) { 373 try { 374 synchronized (restart) { 375 restart.meet(); 376 restart.reset(); 377 } 378 } catch (InterruptedException ie) {} 379 } 380 } 381 } 382 }, "Jam Thread #2"); 383 384 jamThread2.start(); 385 386 Thread t1 = new Thread(new Runnable() { 387 public void run() { 388 try { 389 cb.waitForTurn(2); 390 manager.readLock(owner2, res2); 391 cb.signalTurn(3); 392 cb.waitForTurn(5); 393 synchronized (manager.getLock(res1)) { 394 cb.signalTurn(6); 395 manager.writeLock(owner2, res1); 396 } 397 } catch (LockException le) { 398 assertEquals(le.getCode(), LockException.CODE_DEADLOCK_VICTIM); 399 deadlockCnt++; 400 } catch (InterruptedException ie) { 401 } finally { 402 manager.releaseAll(owner2); 403 synchronized (restart) { 404 try { 405 synchronized (restart) { 406 restart.meet(); 407 restart.reset(); 408 } 409 } catch (InterruptedException ie) {} 410 } 411 } 412 } 413 }, "Thread #1"); 414 415 t1.start(); 416 417 Thread t2 = new Thread(new Runnable() { 418 public void run() { 419 try { 420 cb.waitForTurn(3); 421 manager.readLock(owner3, res3); 422 synchronized (manager.getLock(res2)) { 423 cb.signalTurn(5); 424 manager.writeLock(owner3, res2); 425 } 426 } catch (LockException le) { 427 assertEquals(le.getCode(), LockException.CODE_DEADLOCK_VICTIM); 428 deadlockCnt++; 429 } catch (InterruptedException ie) { 430 } finally { 431 manager.releaseAll(owner3); 432 synchronized (restart) { 433 try { 434 synchronized (restart) { 435 restart.meet(); 436 restart.reset(); 437 } 438 } catch (InterruptedException ie) {} 439 } 440 } 441 } 442 }, "Thread #2"); 443 444 t2.start(); 445 446 try { 447 cb.waitForTurn(1); 448 manager.readLock(owner1, res1); 449 cb.signalTurn(2); 450 cb.waitForTurn(6); 451 manager.writeLock(owner1, res3); 452 } catch (LockException le) { 453 assertEquals(le.getCode(), LockException.CODE_DEADLOCK_VICTIM); 454 deadlockCnt++; 455 } catch (InterruptedException ie) { 456 } finally { 457 manager.releaseAll(owner1); 458 synchronized (restart) { 459 try { 460 synchronized (restart) { 461 restart.meet(); 462 restart.reset(); 463 } 464 } catch (InterruptedException ie) { 465 } 466 } 467 } 468 469 // XXX in special scenarios the current implementation might cause more than one 470 // owner to be a deadlock victim 471 if (deadlockCnt != 1) { 472 sLogger.logWarning("\nMore than one thread was deadlock victim!\n"); 473 } 474 assertTrue(deadlockCnt >= 1); 475 deadlockCnt = 0; 476 cb.reset(); 477 } 478 } 479 480 /* 481 * 482 * Test shows the following 483 * - upgrade works with read locks no matter if they are acquired before or later (1-4) 484 * - write is blocked by read (5) 485 * - read is blocked by intention lock (6) 486 * - write lock coming from an intention lock always has preference over others (7) 487 * 488 * 489 * Owner Owner Owner 490 * Step #1 #2 #3 491 * 1 read (ok) 492 * 2 upgrade (ok) 493 * 3 release (ok) 494 * 4 read (ok) 495 * 5 write (blocked 496 * because of #1) 497 * 6 read (blocked 498 * because intention of #2) 499 * 7 release resumed 500 * 8 release resumed 501 * 9 release 502 */ 503 public void testUpgrade() throws Throwable { 504 505 sLogger.logInfo("\n\nChecking upgrade and preference lock\n\n"); 506 507 final String owner1 = "owner1"; 508 final String owner2 = "owner2"; 509 final String owner3 = "owner3"; 510 511 final String res1 = "res1"; 512 513 // a read / write lock 514 final ReadWriteUpgradeLockManager manager = new ReadWriteUpgradeLockManager(sLogger, 515 TIMEOUT); 516 517 final RendezvousBarrier restart = new RendezvousBarrier("restart", 3, TIMEOUT, sLogger); 518 519 final TurnBarrier cb = new TurnBarrier("cb1", TIMEOUT, sLogger, 1); 520 521 for (int i = 0; i < CONCURRENT_TESTS; i++) { 522 523 System.out.print("."); 524 525 Thread t1 = new Thread(new Runnable() { 526 public void run() { 527 try { 528 cb.waitForTurn(2); 529 manager.upgradeLock(owner2, res1); 530 cb.signalTurn(3); 531 cb.waitForTurn(5); 532 synchronized (manager.getLock(res1)) { 533 cb.signalTurn(6); 534 manager.writeLock(owner2, res1); 535 } 536 // we must always be first as we will be preferred over 537 // as I had the upgrade 538 // lock before 539 synchronized (this) { 540 if (first == null) 541 first = owner2; 542 } 543 manager.releaseAll(owner2); 544 synchronized (restart) { 545 restart.meet(); 546 restart.reset(); 547 } 548 } catch (InterruptedException ie) { 549 } 550 } 551 }, "Thread #1"); 552 553 t1.start(); 554 555 Thread t2 = new Thread(new Runnable() { 556 public void run() { 557 try { 558 // I wait until the others are blocked 559 // when I release my single read lock, thread #1 always 560 // should be the 561 // next to get the lock as it is preferred over the main 562 // thread 563 // that only waits for a read lock 564 cb.waitForTurn(6); 565 synchronized (manager.getLock(res1)) { 566 cb.signalTurn(7); 567 manager.readLock(owner3, res1); 568 } 569 synchronized (this) { 570 if (first == null) 571 first = owner3; 572 } 573 manager.releaseAll(owner3); 574 synchronized (restart) { 575 restart.meet(); 576 restart.reset(); 577 } 578 } catch (InterruptedException ie) { 579 } 580 } 581 }, "Thread #2"); 582 583 t2.start(); 584 585 cb.waitForTurn(1); 586 manager.readLock(owner1, res1); 587 cb.signalTurn(2); 588 cb.waitForTurn(3); 589 manager.release(owner1, res1); 590 manager.readLock(owner1, res1); 591 cb.signalTurn(5); 592 cb.waitForTurn(7); 593 synchronized (manager.getLock(res1)) { 594 manager.releaseAll(owner1); 595 } 596 synchronized (restart) { 597 restart.meet(); 598 restart.reset(); 599 } 600 601 assertEquals(first, owner2); 602 first = null; 603 cb.reset(); 604 } 605 606 } 607 608 /* 609 * 610 * Test shows that two preference locks that are imcompatible do not cause a lock out 611 * which was the case with GenericLock 1.5 612 * Before the fix this test would dealock 613 * 614 * Owner Owner Owner 615 * Step #1 #2 #3 616 * 1 read (ok) 617 * 2 write preferred 618 * (blocked 619 * because of #1) 620 * 3 write preferred 621 * (blocked 622 * because of #1 and #2) 623 * 4 release 624 * 5 resumed or resumed 625 * (as both are preferred, problem 626 * is that that would exclude each other 627 * in the algorithm used) 628 * 6 released or released 629 * 7 resumed or resumed 630 * 8 released or released 631 * 632 * 633 */ 634 public void testPreference() throws Throwable { 635 636 sLogger.logInfo("\n\nChecking incompatible preference locks\n\n"); 637 638 final String owner1 = "owner1"; 639 final String owner2 = "owner2"; 640 final String owner3 = "owner3"; 641 642 final String res1 = "res1"; 643 644 final ReadWriteLock lock = new ReadWriteLock(res1, sLogger); 645 646 final RendezvousBarrier restart = new RendezvousBarrier("restart", 3, TIMEOUT, sLogger); 647 648 final TurnBarrier cb = new TurnBarrier("cb1", TIMEOUT, sLogger, 1); 649 650 for (int i = 0; i < CONCURRENT_TESTS; i++) { 651 652 System.out.print("."); 653 654 Thread t1 = new Thread(new Runnable() { 655 public void run() { 656 try { 657 cb.waitForTurn(2); 658 synchronized (lock) { 659 cb.signalTurn(3); 660 lock.acquire(owner2, ReadWriteLock.WRITE_LOCK, true, 661 GenericLock.COMPATIBILITY_REENTRANT, true, TIMEOUT); 662 } 663 lock.release(owner2); 664 synchronized (restart) { 665 restart.meet(); 666 restart.reset(); 667 } 668 } catch (InterruptedException ie) { 669 } 670 } 671 }, "Thread #1"); 672 673 t1.start(); 674 675 Thread t2 = new Thread(new Runnable() { 676 public void run() { 677 try { 678 cb.waitForTurn(3); 679 synchronized (lock) { 680 cb.signalTurn(4); 681 lock.acquire(owner3, ReadWriteLock.WRITE_LOCK, true, 682 GenericLock.COMPATIBILITY_REENTRANT, true, TIMEOUT); 683 } 684 lock.release(owner3); 685 synchronized (restart) { 686 restart.meet(); 687 restart.reset(); 688 } 689 } catch (InterruptedException ie) { 690 } 691 } 692 }, "Thread #2"); 693 694 t2.start(); 695 696 cb.waitForTurn(1); 697 lock.acquireRead(owner1, TIMEOUT); 698 cb.signalTurn(2); 699 cb.waitForTurn(4); 700 synchronized (lock) { 701 lock.release(owner1); 702 } 703 synchronized (restart) { 704 restart.meet(); 705 restart.reset(); 706 } 707 708 cb.reset(); 709 } 710 711 } 712 713 public void testGlobalTimeout() throws Throwable { 714 715 sLogger.logInfo("\n\nChecking global timeouts\n\n"); 716 717 final String owner1 = "owner1"; 718 final String owner2 = "owner2"; 719 720 final String res1 = "res1"; 721 722 final GenericLockManager manager = new GenericLockManager(1, sLogger, TIMEOUT, -1); 723 724 final RendezvousBarrier restart = new RendezvousBarrier("restart", 2, TIMEOUT, sLogger); 725 726 final TurnBarrier cb = new TurnBarrier("cb1", TIMEOUT, sLogger, 1); 727 728 for (int i = 0; i < CONCURRENT_TESTS; i++) { 729 730 System.out.print("."); 731 732 Thread t1 = new Thread(new Runnable() { 733 public void run() { 734 try { 735 cb.waitForTurn(2); 736 manager.lock(owner2, res1, 1, true); 737 cb.signalTurn(3); 738 manager.releaseAll(owner2); 739 synchronized (restart) { 740 restart.meet(); 741 restart.reset(); 742 } 743 } catch (InterruptedException ie) { 744 } 745 } 746 }, "Thread #1"); 747 748 t1.start(); 749 750 cb.waitForTurn(1); 751 manager.startGlobalTimeout(owner1, 500); 752 manager.lock(owner1, res1, 1, true); 753 cb.signalTurn(2); 754 cb.waitForTurn(3); 755 boolean failed = false; 756 try { 757 manager.tryLock(owner1, res1, 1, true); 758 } catch (LockException le) { 759 failed = true; 760 } 761 assertTrue(failed); 762 manager.releaseAll(owner1); 763 failed = false; 764 try { 765 manager.tryLock(owner1, res1, 1, true); 766 } catch (LockException le) { 767 failed = true; 768 } 769 assertFalse(failed); 770 manager.releaseAll(owner1); 771 synchronized (restart) { 772 restart.meet(); 773 restart.reset(); 774 } 775 776 cb.reset(); 777 } 778 779 } 780 781 public void testStress() throws Throwable { 782 783 sLogger.logInfo("\n\nStress checking locks\n\n"); 784 785 final String owner1 = "owner1"; 786 final String owner2 = "owner2"; 787 final String owner3 = "owner3"; 788 final String owner4 = "owner4"; 789 final String owner5 = "owner5"; 790 final String owner6 = "owner6"; 791 final String owner7 = "owner7"; 792 final String owner8 = "owner8"; 793 final String owner9 = "owner9"; 794 final String owner10 = "owner10"; 795 796 final String res1 = "res1"; 797 final String res2 = "res2"; 798 final String res3 = "res3"; 799 800 // choose low timeout so sometimes an owner times out 801 final ReadWriteUpgradeLockManager manager = new ReadWriteUpgradeLockManager(sLogger, 100); 802 803 final RendezvousBarrier restart = new RendezvousBarrier("restart", 5, TIMEOUT, sLogger); 804 final RendezvousBarrier start = new RendezvousBarrier("start", 5, TIMEOUT, sLogger); 805 806 for (int i = 0; i < CONCURRENT_TESTS; i++) { 807 808 System.out.print("."); 809 810 Thread t1 = new Thread(new Runnable() { 811 public void run() { 812 try { 813 try { 814 synchronized (start) { 815 start.meet(); 816 start.reset(); 817 } 818 manager.readLock(owner1, res1); 819 manager.readLock(owner1, res2); 820 manager.upgradeLock(owner1, res3); 821 manager.writeLock(owner1, res3); 822 } catch (LockException ie) { 823 } finally { 824 manager.releaseAll(owner1); 825 synchronized (restart) { 826 restart.meet(); 827 restart.reset(); 828 } 829 } 830 } catch (InterruptedException ie) { 831 } 832 } 833 }, "Thread #1"); 834 t1.start(); 835 836 Thread t2 = new Thread(new Runnable() { 837 public void run() { 838 try { 839 try { 840 synchronized (start) { 841 start.meet(); 842 start.reset(); 843 } 844 manager.readLock(owner2, res1); 845 manager.readLock(owner2, res2); 846 manager.upgradeLock(owner2, res3); 847 manager.writeLock(owner2, res3); 848 } catch (LockException ie) { 849 } finally { 850 manager.releaseAll(owner2); 851 synchronized (restart) { 852 restart.meet(); 853 restart.reset(); 854 } 855 } 856 } catch (InterruptedException ie) { 857 } 858 } 859 }, "Thread #2"); 860 t2.start(); 861 862 Thread t3 = new Thread(new Runnable() { 863 public void run() { 864 try { 865 try { 866 synchronized (start) { 867 start.meet(); 868 start.reset(); 869 } 870 manager.readLock(owner3, res1); 871 manager.readLock(owner3, res2); 872 manager.upgradeLock(owner3, res3); 873 manager.writeLock(owner3, res3); 874 } catch (LockException ie) { 875 } finally { 876 manager.releaseAll(owner3); 877 synchronized (restart) { 878 restart.meet(); 879 restart.reset(); 880 } 881 } 882 } catch (InterruptedException ie) { 883 } 884 } 885 }, "Thread #3"); 886 t3.start(); 887 888 Thread t4 = new Thread(new Runnable() { 889 public void run() { 890 try { 891 try { 892 synchronized (start) { 893 start.meet(); 894 start.reset(); 895 } 896 manager.readLock(owner4, res1); 897 manager.readLock(owner4, res2); 898 manager.upgradeLock(owner4, res3); 899 manager.writeLock(owner4, res3); 900 } catch (LockException ie) { 901 } finally { 902 manager.releaseAll(owner4); 903 synchronized (restart) { 904 restart.meet(); 905 restart.reset(); 906 } 907 } 908 } catch (InterruptedException ie) { 909 } 910 } 911 }, "Thread #4"); 912 t4.start(); 913 914 try { 915 try { 916 synchronized (start) { 917 start.meet(); 918 start.reset(); 919 } 920 manager.readLock("reader", res1); 921 manager.readLock("reader", res2); 922 manager.readLock("reader", res3); 923 924 } catch (LockException ie) { 925 } finally { 926 manager.releaseAll("reader"); 927 try { 928 synchronized (restart) { 929 restart.meet(); 930 restart.reset(); 931 } 932 } catch (InterruptedException ie) { 933 } 934 } 935 } catch (InterruptedException ie) { 936 } 937 } 938 939 } 940 941 public void testChaos() throws Throwable { 942 943 sLogger.logInfo("\n\nChaos testing locks for internal deadlocks resp. concurrent mods\n\n"); 944 945 final String owner1 = "owner1"; 946 final String owner2 = "owner2"; 947 final String owner3 = "owner3"; 948 final String owner4 = "owner4"; 949 final String owner5 = "owner5"; 950 final String owner6 = "owner6"; 951 final String owner7 = "owner7"; 952 final String owner8 = "owner8"; 953 final String owner9 = "owner9"; 954 final String owner10 = "owner10"; 955 956 final String res1 = "res1"; 957 final String res2 = "res2"; 958 final String res3 = "res3"; 959 960 // choose low timeout so sometimes an owner times out 961 final ReadWriteUpgradeLockManager manager = new ReadWriteUpgradeLockManager(sLogger, 100); 962 963 int concurrentThreads = 7; 964 int threads = CONCURRENT_TESTS * concurrentThreads; 965 966 final RendezvousBarrier end = new RendezvousBarrier("end", threads + 1, TIMEOUT, sLogger); 967 968 sLogger.logInfo("\n\nStarting "+threads+" threads\n\n"); 969 970 for (int i = 0; i < CONCURRENT_TESTS; i++) { 971 972 final int cnt = i; 973 974 System.out.print("."); 975 976 Thread t1 = new Thread(new Runnable() { 977 public void run() { 978 try { 979 manager.readLock(owner1, res1); 980 manager.readLock(owner1, res2); 981 manager.upgradeLock(owner1, res3); 982 manager.writeLock(owner1, res3); 983 } catch (LockException ie) { 984 System.out.print("-"); 985 } finally { 986 manager.releaseAll(owner1); 987 end.call(); 988 } 989 } 990 }, "Thread #1"); 991 992 Thread t2 = new Thread(new Runnable() { 993 public void run() { 994 try { 995 manager.readLock(owner2, res1); 996 manager.readLock(owner2, res2); 997 manager.upgradeLock(owner2, res3); 998 manager.writeLock(owner2, res3); 999 } catch (LockException ie) { 1000 System.out.print("-"); 1001 } finally { 1002 manager.releaseAll(owner2); 1003 end.call(); 1004 } 1005 } 1006 }, "Thread #2"); 1007 1008 Thread t3 = new Thread(new Runnable() { 1009 public void run() { 1010 try { 1011 manager.startGlobalTimeout(owner3, 10 + cnt); 1012 manager.readLock(owner3, res1); 1013 manager.readLock(owner3, res2); 1014 manager.upgradeLock(owner3, res3); 1015 manager.writeLock(owner3, res3); 1016 } catch (LockException le) { 1017 if (le.getCode() == LockException.CODE_TIMED_OUT) { 1018 System.out.print("*"); 1019 } else { 1020 System.out.print("-"); 1021 } 1022 } finally { 1023 manager.releaseAll(owner3); 1024 end.call(); 1025 } 1026 } 1027 }, "Thread #3"); 1028 1029 Thread t4 = new Thread(new Runnable() { 1030 public void run() { 1031 try { 1032 manager.readLock(owner4, res1); 1033 manager.readLock(owner4, res2); 1034 manager.upgradeLock(owner4, res3); 1035 manager.writeLock(owner4, res3); 1036 } catch (LockException le) { 1037 System.out.print("-"); 1038 } finally { 1039 manager.releaseAll(owner4); 1040 end.call(); 1041 } 1042 } 1043 }, "Thread #4"); 1044 1045 Thread deadlock1 = new Thread(new Runnable() { 1046 public void run() { 1047 try { 1048 manager.writeLock(owner5, res2); 1049 manager.writeLock(owner5, res1); 1050 } catch (LockException le) { 1051 assertEquals(le.getCode(), LockException.CODE_DEADLOCK_VICTIM); 1052 System.out.print("-"); 1053 } finally { 1054 manager.releaseAll(owner5); 1055 end.call(); 1056 } 1057 } 1058 }, "Deadlock1 Thread"); 1059 1060 Thread deadlock2 = new Thread(new Runnable() { 1061 public void run() { 1062 try { 1063 manager.readLock(owner6, res1); 1064 manager.readLock(owner6, res2); 1065 } catch (LockException le) { 1066 assertEquals(le.getCode(), LockException.CODE_DEADLOCK_VICTIM); 1067 System.out.print("-"); 1068 } finally { 1069 manager.releaseAll(owner6); 1070 end.call(); 1071 } 1072 } 1073 }, "Deadlock1 Thread"); 1074 1075 Thread reader = new Thread(new Runnable() { 1076 public void run() { 1077 try { 1078 manager.readLock("reader", res1); 1079 manager.readLock("reader", res2); 1080 manager.readLock("reader", res3); 1081 } catch (LockException ie) { 1082 System.out.print("-"); 1083 } finally { 1084 manager.releaseAll("reader"); 1085 end.call(); 1086 } 1087 } 1088 }, "Reader Thread"); 1089 1090 1091 t4.start(); 1092 t3.start(); 1093 reader.start(); 1094 t1.start(); 1095 deadlock2.start(); 1096 t2.start(); 1097 deadlock1.start(); 1098 } 1099 // wait until all threads have really terminated 1100 end.meet(); 1101 1102 } 1103}