1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002,2008 Oracle. All rights reserved. 5 * 6 * $Id: TransactionTest.java,v 12.11 2008/02/07 17:12:31 mark Exp $ 7 */ 8 9package com.sleepycat.collections.test; 10 11import java.io.File; 12import java.io.IOException; 13import java.util.Iterator; 14import java.util.List; 15import java.util.SortedSet; 16 17import junit.framework.Test; 18import junit.framework.TestCase; 19import junit.framework.TestSuite; 20 21import com.sleepycat.collections.CurrentTransaction; 22import com.sleepycat.collections.StoredCollections; 23import com.sleepycat.collections.StoredContainer; 24import com.sleepycat.collections.StoredIterator; 25import com.sleepycat.collections.StoredList; 26import com.sleepycat.collections.StoredSortedMap; 27import com.sleepycat.collections.TransactionRunner; 28import com.sleepycat.collections.TransactionWorker; 29import com.sleepycat.compat.DbCompat; 30import com.sleepycat.db.CursorConfig; 31import com.sleepycat.db.Database; 32import com.sleepycat.db.DatabaseConfig; 33import com.sleepycat.db.DatabaseEntry; 34import com.sleepycat.db.DatabaseException; 35import com.sleepycat.db.Environment; 36import com.sleepycat.db.EnvironmentConfig; 37import com.sleepycat.db.Transaction; 38import com.sleepycat.db.TransactionConfig; 39import com.sleepycat.util.RuntimeExceptionWrapper; 40import com.sleepycat.util.test.SharedTestUtils; 41import com.sleepycat.util.test.TestEnv; 42 43/** 44 * @author Mark Hayes 45 */ 46public class TransactionTest extends TestCase { 47 48 private static final Long ONE = new Long(1); 49 private static final Long TWO = new Long(2); 50 private static final Long THREE = new Long(3); 51 52 /** 53 * Runs a command line collection test. 54 * @see #usage 55 */ 56 public static void main(String[] args) 57 throws Exception { 58 59 if (args.length == 1 && 60 (args[0].equals("-h") || args[0].equals("-help"))) { 61 usage(); 62 } else { 63 junit.framework.TestResult tr = 64 junit.textui.TestRunner.run(suite()); 65 if (tr.errorCount() > 0 || 66 tr.failureCount() > 0) { 67 System.exit(1); 68 } else { 69 System.exit(0); 70 } 71 } 72 } 73 74 private static void usage() { 75 76 System.out.println( 77 "Usage: java com.sleepycat.collections.test.TransactionTest" 78 + " [-h | -help]\n"); 79 System.exit(2); 80 } 81 82 public static Test suite() 83 throws Exception { 84 85 TestSuite suite = new TestSuite(TransactionTest.class); 86 return suite; 87 } 88 89 private Environment env; 90 private CurrentTransaction currentTxn; 91 private Database store; 92 private StoredSortedMap map; 93 private TestStore testStore = TestStore.BTREE_UNIQ; 94 95 public TransactionTest(String name) { 96 97 super(name); 98 } 99 100 public void setUp() 101 throws Exception { 102 103 SharedTestUtils.printTestName(SharedTestUtils.qualifiedTestName(this)); 104 env = TestEnv.TXN.open("TransactionTests"); 105 currentTxn = CurrentTransaction.getInstance(env); 106 store = testStore.open(env, dbName(0)); 107 map = new StoredSortedMap(store, testStore.getKeyBinding(), 108 testStore.getValueBinding(), true); 109 } 110 111 public void tearDown() { 112 113 try { 114 if (store != null) { 115 store.close(); 116 } 117 if (env != null) { 118 env.close(); 119 } 120 } catch (Exception e) { 121 System.out.println("Ignored exception during tearDown: " + e); 122 } finally { 123 /* Ensure that GC can cleanup. */ 124 store = null; 125 env = null; 126 currentTxn = null; 127 map = null; 128 testStore = null; 129 } 130 } 131 132 private String dbName(int i) { 133 134 return "txn-test-" + getName() + '-' + i; 135 } 136 137 public void testGetters() 138 throws Exception { 139 140 assertNotNull(env); 141 assertNotNull(currentTxn); 142 assertNull(currentTxn.getTransaction()); 143 144 currentTxn.beginTransaction(null); 145 assertNotNull(currentTxn.getTransaction()); 146 currentTxn.commitTransaction(); 147 assertNull(currentTxn.getTransaction()); 148 149 currentTxn.beginTransaction(null); 150 assertNotNull(currentTxn.getTransaction()); 151 currentTxn.abortTransaction(); 152 assertNull(currentTxn.getTransaction()); 153 154 // read-uncommitted property should be inherited 155 156 assertTrue(!isReadUncommitted(map)); 157 assertTrue(!isReadUncommitted(map.values())); 158 assertTrue(!isReadUncommitted(map.keySet())); 159 assertTrue(!isReadUncommitted(map.entrySet())); 160 161 StoredSortedMap other = (StoredSortedMap) 162 StoredCollections.configuredMap 163 (map, CursorConfig.READ_UNCOMMITTED); 164 assertTrue(isReadUncommitted(other)); 165 assertTrue(isReadUncommitted(other.values())); 166 assertTrue(isReadUncommitted(other.keySet())); 167 assertTrue(isReadUncommitted(other.entrySet())); 168 assertTrue(!isReadUncommitted(map)); 169 assertTrue(!isReadUncommitted(map.values())); 170 assertTrue(!isReadUncommitted(map.keySet())); 171 assertTrue(!isReadUncommitted(map.entrySet())); 172 173 // read-committed property should be inherited 174 175 assertTrue(!isReadCommitted(map)); 176 assertTrue(!isReadCommitted(map.values())); 177 assertTrue(!isReadCommitted(map.keySet())); 178 assertTrue(!isReadCommitted(map.entrySet())); 179 180 other = (StoredSortedMap) 181 StoredCollections.configuredMap 182 (map, CursorConfig.READ_COMMITTED); 183 assertTrue(isReadCommitted(other)); 184 assertTrue(isReadCommitted(other.values())); 185 assertTrue(isReadCommitted(other.keySet())); 186 assertTrue(isReadCommitted(other.entrySet())); 187 assertTrue(!isReadCommitted(map)); 188 assertTrue(!isReadCommitted(map.values())); 189 assertTrue(!isReadCommitted(map.keySet())); 190 assertTrue(!isReadCommitted(map.entrySet())); 191 } 192 193 public void testTransactional() 194 throws Exception { 195 196 // is transactional because DB_AUTO_COMMIT was passed to 197 // Database.open() 198 // 199 assertTrue(map.isTransactional()); 200 store.close(); 201 store = null; 202 203 // is not transactional 204 // 205 DatabaseConfig dbConfig = new DatabaseConfig(); 206 DbCompat.setTypeBtree(dbConfig); 207 dbConfig.setAllowCreate(true); 208 Database db = DbCompat.testOpenDatabase 209 (env, null, dbName(1), null, dbConfig); 210 map = new StoredSortedMap(db, testStore.getKeyBinding(), 211 testStore.getValueBinding(), true); 212 assertTrue(!map.isTransactional()); 213 map.put(ONE, ONE); 214 readCheck(map, ONE, ONE); 215 db.close(); 216 217 // is transactional 218 // 219 dbConfig.setTransactional(true); 220 currentTxn.beginTransaction(null); 221 db = DbCompat.testOpenDatabase 222 (env, currentTxn.getTransaction(), dbName(2), null, dbConfig); 223 currentTxn.commitTransaction(); 224 map = new StoredSortedMap(db, testStore.getKeyBinding(), 225 testStore.getValueBinding(), true); 226 assertTrue(map.isTransactional()); 227 currentTxn.beginTransaction(null); 228 map.put(ONE, ONE); 229 readCheck(map, ONE, ONE); 230 currentTxn.commitTransaction(); 231 db.close(); 232 } 233 234 public void testExceptions() 235 throws Exception { 236 237 try { 238 currentTxn.commitTransaction(); 239 fail(); 240 } catch (IllegalStateException expected) {} 241 242 try { 243 currentTxn.abortTransaction(); 244 fail(); 245 } catch (IllegalStateException expected) {} 246 } 247 248 public void testNested() 249 throws Exception { 250 251 if (!DbCompat.NESTED_TRANSACTIONS) { 252 return; 253 } 254 assertNull(currentTxn.getTransaction()); 255 256 Transaction txn1 = currentTxn.beginTransaction(null); 257 assertNotNull(txn1); 258 assertTrue(txn1 == currentTxn.getTransaction()); 259 260 assertNull(map.get(ONE)); 261 assertNull(map.put(ONE, ONE)); 262 assertEquals(ONE, map.get(ONE)); 263 264 Transaction txn2 = currentTxn.beginTransaction(null); 265 assertNotNull(txn2); 266 assertTrue(txn2 == currentTxn.getTransaction()); 267 assertTrue(txn1 != txn2); 268 269 assertNull(map.put(TWO, TWO)); 270 assertEquals(TWO, map.get(TWO)); 271 272 Transaction txn3 = currentTxn.beginTransaction(null); 273 assertNotNull(txn3); 274 assertTrue(txn3 == currentTxn.getTransaction()); 275 assertTrue(txn1 != txn2); 276 assertTrue(txn1 != txn3); 277 assertTrue(txn2 != txn3); 278 279 assertNull(map.put(THREE, THREE)); 280 assertEquals(THREE, map.get(THREE)); 281 282 Transaction txn = currentTxn.abortTransaction(); 283 assertTrue(txn == txn2); 284 assertTrue(txn == currentTxn.getTransaction()); 285 assertNull(map.get(THREE)); 286 assertEquals(TWO, map.get(TWO)); 287 288 txn3 = currentTxn.beginTransaction(null); 289 assertNotNull(txn3); 290 assertTrue(txn3 == currentTxn.getTransaction()); 291 assertTrue(txn1 != txn2); 292 assertTrue(txn1 != txn3); 293 assertTrue(txn2 != txn3); 294 295 assertNull(map.put(THREE, THREE)); 296 assertEquals(THREE, map.get(THREE)); 297 298 txn = currentTxn.commitTransaction(); 299 assertTrue(txn == txn2); 300 assertTrue(txn == currentTxn.getTransaction()); 301 assertEquals(THREE, map.get(THREE)); 302 assertEquals(TWO, map.get(TWO)); 303 304 txn = currentTxn.commitTransaction(); 305 assertTrue(txn == txn1); 306 assertTrue(txn == currentTxn.getTransaction()); 307 assertEquals(THREE, map.get(THREE)); 308 assertEquals(TWO, map.get(TWO)); 309 assertEquals(ONE, map.get(ONE)); 310 311 txn = currentTxn.commitTransaction(); 312 assertNull(txn); 313 assertNull(currentTxn.getTransaction()); 314 assertEquals(THREE, map.get(THREE)); 315 assertEquals(TWO, map.get(TWO)); 316 assertEquals(ONE, map.get(ONE)); 317 } 318 319 public void testRunnerCommit() 320 throws Exception { 321 322 commitTest(false); 323 } 324 325 public void testExplicitCommit() 326 throws Exception { 327 328 commitTest(true); 329 } 330 331 private void commitTest(final boolean explicit) 332 throws Exception { 333 334 final TransactionRunner runner = new TransactionRunner(env); 335 runner.setAllowNestedTransactions(DbCompat.NESTED_TRANSACTIONS); 336 337 assertNull(currentTxn.getTransaction()); 338 339 runner.run(new TransactionWorker() { 340 public void doWork() throws Exception { 341 final Transaction txn1 = currentTxn.getTransaction(); 342 assertNotNull(txn1); 343 assertNull(map.put(ONE, ONE)); 344 assertEquals(ONE, map.get(ONE)); 345 346 runner.run(new TransactionWorker() { 347 public void doWork() throws Exception { 348 final Transaction txn2 = currentTxn.getTransaction(); 349 assertNotNull(txn2); 350 if (DbCompat.NESTED_TRANSACTIONS) { 351 assertTrue(txn1 != txn2); 352 } else { 353 assertTrue(txn1 == txn2); 354 } 355 assertNull(map.put(TWO, TWO)); 356 assertEquals(TWO, map.get(TWO)); 357 assertEquals(ONE, map.get(ONE)); 358 if (DbCompat.NESTED_TRANSACTIONS && explicit) { 359 currentTxn.commitTransaction(); 360 } 361 } 362 }); 363 364 Transaction txn3 = currentTxn.getTransaction(); 365 assertSame(txn1, txn3); 366 367 assertEquals(TWO, map.get(TWO)); 368 assertEquals(ONE, map.get(ONE)); 369 } 370 }); 371 372 assertNull(currentTxn.getTransaction()); 373 } 374 375 public void testRunnerAbort() 376 throws Exception { 377 378 abortTest(false); 379 } 380 381 public void testExplicitAbort() 382 throws Exception { 383 384 abortTest(true); 385 } 386 387 private void abortTest(final boolean explicit) 388 throws Exception { 389 390 final TransactionRunner runner = new TransactionRunner(env); 391 runner.setAllowNestedTransactions(DbCompat.NESTED_TRANSACTIONS); 392 393 assertNull(currentTxn.getTransaction()); 394 395 runner.run(new TransactionWorker() { 396 public void doWork() throws Exception { 397 final Transaction txn1 = currentTxn.getTransaction(); 398 assertNotNull(txn1); 399 assertNull(map.put(ONE, ONE)); 400 assertEquals(ONE, map.get(ONE)); 401 402 if (DbCompat.NESTED_TRANSACTIONS) { 403 try { 404 runner.run(new TransactionWorker() { 405 public void doWork() throws Exception { 406 final Transaction txn2 = 407 currentTxn.getTransaction(); 408 assertNotNull(txn2); 409 assertTrue(txn1 != txn2); 410 assertNull(map.put(TWO, TWO)); 411 assertEquals(TWO, map.get(TWO)); 412 if (explicit) { 413 currentTxn.abortTransaction(); 414 } else { 415 throw new IllegalArgumentException( 416 "test-abort"); 417 } 418 } 419 }); 420 assertTrue(explicit); 421 } catch (IllegalArgumentException e) { 422 assertTrue(!explicit); 423 assertEquals("test-abort", e.getMessage()); 424 } 425 } 426 427 Transaction txn3 = currentTxn.getTransaction(); 428 assertSame(txn1, txn3); 429 430 assertEquals(ONE, map.get(ONE)); 431 assertNull(map.get(TWO)); 432 } 433 }); 434 435 assertNull(currentTxn.getTransaction()); 436 } 437 438 public void testReadCommittedCollection() 439 throws Exception { 440 441 StoredSortedMap degree2Map = (StoredSortedMap) 442 StoredCollections.configuredSortedMap 443 (map, CursorConfig.READ_COMMITTED); 444 445 // original map is not read-committed 446 assertTrue(!isReadCommitted(map)); 447 448 // all read-committed containers are read-uncommitted 449 assertTrue(isReadCommitted(degree2Map)); 450 assertTrue(isReadCommitted 451 (StoredCollections.configuredMap 452 (map, CursorConfig.READ_COMMITTED))); 453 assertTrue(isReadCommitted 454 (StoredCollections.configuredCollection 455 (map.values(), CursorConfig.READ_COMMITTED))); 456 assertTrue(isReadCommitted 457 (StoredCollections.configuredSet 458 (map.keySet(), CursorConfig.READ_COMMITTED))); 459 assertTrue(isReadCommitted 460 (StoredCollections.configuredSortedSet 461 ((SortedSet) map.keySet(), 462 CursorConfig.READ_COMMITTED))); 463 464 if (DbCompat.RECNO_METHOD) { 465 // create a list just so we can call configuredList() 466 Database listStore = TestStore.RECNO_RENUM.open(env, null); 467 List list = new StoredList(listStore, TestStore.VALUE_BINDING, 468 true); 469 assertTrue(isReadCommitted 470 (StoredCollections.configuredList 471 (list, CursorConfig.READ_COMMITTED))); 472 listStore.close(); 473 } 474 475 map.put(ONE, ONE); 476 doReadCommitted(degree2Map, null); 477 } 478 479 private static boolean isReadCommitted(Object container) { 480 StoredContainer storedContainer = (StoredContainer) container; 481 /* We can't use getReadCommitted until is is added to DB core. */ 482 return storedContainer.getCursorConfig() != null && 483 storedContainer.getCursorConfig().getReadCommitted(); 484 } 485 486 public void testReadCommittedTransaction() 487 throws Exception { 488 489 TransactionConfig config = new TransactionConfig(); 490 config.setReadCommitted(true); 491 doReadCommitted(map, config); 492 } 493 494 private void doReadCommitted(final StoredSortedMap degree2Map, 495 TransactionConfig txnConfig) 496 throws Exception { 497 498 map.put(ONE, ONE); 499 TransactionRunner runner = new TransactionRunner(env); 500 runner.setTransactionConfig(txnConfig); 501 assertNull(currentTxn.getTransaction()); 502 runner.run(new TransactionWorker() { 503 public void doWork() throws Exception { 504 assertNotNull(currentTxn.getTransaction()); 505 506 /* Do a read-committed get(), the lock is not retained. */ 507 assertEquals(ONE, degree2Map.get(ONE)); 508 509 /* 510 * If we were not using read-committed, the following write of 511 * key ONE with an auto-commit transaction would self-deadlock 512 * since two transactions in the same thread would be 513 * attempting to lock the same key, one for write and one for 514 * read. This test passes if we do not deadlock. 515 */ 516 DatabaseEntry key = new DatabaseEntry(); 517 DatabaseEntry value = new DatabaseEntry(); 518 testStore.getKeyBinding().objectToEntry(ONE, key); 519 testStore.getValueBinding().objectToEntry(TWO, value); 520 store.put(null, key, value); 521 } 522 }); 523 assertNull(currentTxn.getTransaction()); 524 } 525 526 public void testReadUncommittedCollection() 527 throws Exception { 528 529 StoredSortedMap dirtyMap = (StoredSortedMap) 530 StoredCollections.configuredSortedMap 531 (map, CursorConfig.READ_UNCOMMITTED); 532 533 // original map is not read-uncommitted 534 assertTrue(!isReadUncommitted(map)); 535 536 // all read-uncommitted containers are read-uncommitted 537 assertTrue(isReadUncommitted(dirtyMap)); 538 assertTrue(isReadUncommitted 539 (StoredCollections.configuredMap 540 (map, CursorConfig.READ_UNCOMMITTED))); 541 assertTrue(isReadUncommitted 542 (StoredCollections.configuredCollection 543 (map.values(), CursorConfig.READ_UNCOMMITTED))); 544 assertTrue(isReadUncommitted 545 (StoredCollections.configuredSet 546 (map.keySet(), CursorConfig.READ_UNCOMMITTED))); 547 assertTrue(isReadUncommitted 548 (StoredCollections.configuredSortedSet 549 ((SortedSet) map.keySet(), CursorConfig.READ_UNCOMMITTED))); 550 551 if (DbCompat.RECNO_METHOD) { 552 // create a list just so we can call configuredList() 553 Database listStore = TestStore.RECNO_RENUM.open(env, null); 554 List list = new StoredList(listStore, TestStore.VALUE_BINDING, 555 true); 556 assertTrue(isReadUncommitted 557 (StoredCollections.configuredList 558 (list, CursorConfig.READ_UNCOMMITTED))); 559 listStore.close(); 560 } 561 562 doReadUncommitted(dirtyMap); 563 } 564 565 private static boolean isReadUncommitted(Object container) { 566 StoredContainer storedContainer = (StoredContainer) container; 567 return storedContainer.getCursorConfig() != null && 568 storedContainer.getCursorConfig().getReadUncommitted(); 569 } 570 571 public void testReadUncommittedTransaction() 572 throws Exception { 573 574 TransactionRunner runner = new TransactionRunner(env); 575 TransactionConfig config = new TransactionConfig(); 576 config.setReadUncommitted(true); 577 runner.setTransactionConfig(config); 578 assertNull(currentTxn.getTransaction()); 579 runner.run(new TransactionWorker() { 580 public void doWork() throws Exception { 581 assertNotNull(currentTxn.getTransaction()); 582 doReadUncommitted(map); 583 } 584 }); 585 assertNull(currentTxn.getTransaction()); 586 } 587 588 /** 589 * Tests that the CurrentTransaction static WeakHashMap does indeed allow 590 * GC to reclaim tine environment when it is closed. At one point this was 591 * not working because the value object in the map has a reference to the 592 * environment. This was fixed by wrapping the Environment in a 593 * WeakReference. [#15444] 594 * 595 * This test only succeeds intermittently, probably due to its reliance 596 * on the GC call. 597 */ 598 public void testCurrentTransactionGC() 599 throws Exception { 600 601 /* 602 * This test can have indeterminate results because it depends on 603 * a finalize count, so it's not part of the default run. 604 */ 605 if (!SharedTestUtils.runLongTests()) { 606 return; 607 } 608 609 final StringBuffer finalizedFlag = new StringBuffer(); 610 611 class MyEnv extends Environment { 612 613 MyEnv(File home, EnvironmentConfig config) 614 throws IOException, DatabaseException { 615 616 super(home, config); 617 } 618 619 protected void finalize() { 620 finalizedFlag.append('.'); 621 } 622 } 623 624 MyEnv myEnv = new MyEnv(env.getHome(), env.getConfig()); 625 CurrentTransaction myCurrTxn = CurrentTransaction.getInstance(myEnv); 626 627 store.close(); 628 store = null; 629 map = null; 630 631 env.close(); 632 env = null; 633 634 myEnv.close(); 635 myEnv = null; 636 637 myCurrTxn = null; 638 currentTxn = null; 639 640 for (int i = 0; i < 10; i += 1) { 641 byte[] x = null; 642 try { 643 x = new byte[Integer.MAX_VALUE - 1]; 644 } catch (OutOfMemoryError expected) { 645 } 646 assertNull(x); 647 System.gc(); 648 } 649 650 for (int i = 0; i < 10; i += 1) { 651 System.gc(); 652 } 653 654 assertTrue(finalizedFlag.length() > 0); 655 } 656 657 private synchronized void doReadUncommitted(StoredSortedMap dirtyMap) 658 throws Exception { 659 660 // start thread one 661 ReadUncommittedThreadOne t1 = new ReadUncommittedThreadOne(env, this); 662 t1.start(); 663 wait(); 664 665 // put ONE 666 synchronized (t1) { t1.notify(); } 667 wait(); 668 readCheck(dirtyMap, ONE, ONE); 669 assertTrue(!dirtyMap.isEmpty()); 670 671 // abort ONE 672 synchronized (t1) { t1.notify(); } 673 t1.join(); 674 readCheck(dirtyMap, ONE, null); 675 assertTrue(dirtyMap.isEmpty()); 676 677 // start thread two 678 ReadUncommittedThreadTwo t2 = new ReadUncommittedThreadTwo(env, this); 679 t2.start(); 680 wait(); 681 682 // put TWO 683 synchronized (t2) { t2.notify(); } 684 wait(); 685 readCheck(dirtyMap, TWO, TWO); 686 assertTrue(!dirtyMap.isEmpty()); 687 688 // commit TWO 689 synchronized (t2) { t2.notify(); } 690 t2.join(); 691 readCheck(dirtyMap, TWO, TWO); 692 assertTrue(!dirtyMap.isEmpty()); 693 } 694 695 private static class ReadUncommittedThreadOne extends Thread { 696 697 private CurrentTransaction currentTxn; 698 private TransactionTest parent; 699 private StoredSortedMap map; 700 701 private ReadUncommittedThreadOne(Environment env, 702 TransactionTest parent) { 703 704 this.currentTxn = CurrentTransaction.getInstance(env); 705 this.parent = parent; 706 this.map = parent.map; 707 } 708 709 public synchronized void run() { 710 711 try { 712 assertNull(currentTxn.getTransaction()); 713 assertNotNull(currentTxn.beginTransaction(null)); 714 assertNotNull(currentTxn.getTransaction()); 715 readCheck(map, ONE, null); 716 synchronized (parent) { parent.notify(); } 717 wait(); 718 719 // put ONE 720 assertNull(map.put(ONE, ONE)); 721 readCheck(map, ONE, ONE); 722 synchronized (parent) { parent.notify(); } 723 wait(); 724 725 // abort ONE 726 assertNull(currentTxn.abortTransaction()); 727 assertNull(currentTxn.getTransaction()); 728 } catch (Exception e) { 729 throw new RuntimeExceptionWrapper(e); 730 } 731 } 732 } 733 734 private static class ReadUncommittedThreadTwo extends Thread { 735 736 private Environment env; 737 private CurrentTransaction currentTxn; 738 private TransactionTest parent; 739 private StoredSortedMap map; 740 741 private ReadUncommittedThreadTwo(Environment env, 742 TransactionTest parent) { 743 744 this.env = env; 745 this.currentTxn = CurrentTransaction.getInstance(env); 746 this.parent = parent; 747 this.map = parent.map; 748 } 749 750 public synchronized void run() { 751 752 try { 753 final TransactionRunner runner = new TransactionRunner(env); 754 final Object thread = this; 755 assertNull(currentTxn.getTransaction()); 756 757 runner.run(new TransactionWorker() { 758 public void doWork() throws Exception { 759 assertNotNull(currentTxn.getTransaction()); 760 readCheck(map, TWO, null); 761 synchronized (parent) { parent.notify(); } 762 thread.wait(); 763 764 // put TWO 765 assertNull(map.put(TWO, TWO)); 766 readCheck(map, TWO, TWO); 767 synchronized (parent) { parent.notify(); } 768 thread.wait(); 769 770 // commit TWO 771 } 772 }); 773 assertNull(currentTxn.getTransaction()); 774 } catch (Exception e) { 775 throw new RuntimeExceptionWrapper(e); 776 } 777 } 778 } 779 780 private static void readCheck(StoredSortedMap checkMap, Object key, 781 Object expect) { 782 if (expect == null) { 783 assertNull(checkMap.get(key)); 784 assertTrue(checkMap.tailMap(key).isEmpty()); 785 assertTrue(!checkMap.tailMap(key).containsKey(key)); 786 assertTrue(!checkMap.keySet().contains(key)); 787 assertTrue(checkMap.duplicates(key).isEmpty()); 788 Iterator i = checkMap.keySet().iterator(); 789 try { 790 while (i.hasNext()) { 791 assertTrue(!key.equals(i.next())); 792 } 793 } finally { StoredIterator.close(i); } 794 } else { 795 assertEquals(expect, checkMap.get(key)); 796 assertEquals(expect, checkMap.tailMap(key).get(key)); 797 assertTrue(!checkMap.tailMap(key).isEmpty()); 798 assertTrue(checkMap.tailMap(key).containsKey(key)); 799 assertTrue(checkMap.keySet().contains(key)); 800 assertTrue(checkMap.values().contains(expect)); 801 assertTrue(!checkMap.duplicates(key).isEmpty()); 802 assertTrue(checkMap.duplicates(key).contains(expect)); 803 Iterator i = checkMap.keySet().iterator(); 804 try { 805 boolean found = false; 806 while (i.hasNext()) { 807 if (expect.equals(i.next())) { 808 found = true; 809 } 810 } 811 assertTrue(found); 812 } 813 finally { StoredIterator.close(i); } 814 } 815 } 816} 817