Clover coverage report -
Coverage timestamp: Thu Jul 5 2007 20:02:32 EDT
file stats: LOC: 534   Methods: 20
NCLOC: 342   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
LockTest.java 54.5% 90.2% 95% 87%
coverage coverage
 1    package org.jboss.cache.lock;
 2   
 3    import junit.framework.Test;
 4    import junit.framework.TestCase;
 5    import junit.framework.TestSuite;
 6    import org.jboss.cache.misc.TestingUtil;
 7   
 8    import java.util.concurrent.CyclicBarrier;
 9    import java.util.concurrent.Semaphore;
 10    import java.util.concurrent.TimeUnit;
 11    import java.util.concurrent.locks.Lock;
 12   
 13    /**
 14    * Various tests that test isolation level semantics provided by locks
 15    *
 16    * @author Bela Ban
 17    * @version $Id: LockTest.java,v 1.10 2007/05/23 10:28:52 msurtani Exp $
 18    */
 19    public class LockTest extends TestCase
 20    {
 21    int value = 10;
 22    Throwable t1_ex, t2_ex;
 23    long start = 0;
 24    final long TIMEOUT = 5000;
 25    final long SLEEP = 500;
 26   
 27    volatile boolean committed;
 28   
 29  4 public LockTest(String name)
 30    {
 31  4 super(name);
 32    }
 33   
 34   
 35  4 public void tearDown() throws Exception
 36    {
 37  4 super.tearDown();
 38  4 t1_ex = t2_ex = null;
 39  4 committed = false;
 40    }
 41   
 42   
 43    static class MyFIFOSemaphore extends Semaphore
 44    {
 45  1 public MyFIFOSemaphore(int permits)
 46    {
 47  1 super(permits);
 48    }
 49   
 50  5 public void acquire() throws InterruptedException
 51    {
 52  5 super.acquire();
 53    }
 54   
 55  5 public void release()
 56    {
 57  5 super.release();
 58    }
 59    }
 60   
 61   
 62    /**
 63    * Thread1 reads data, thread2 changes and - before thread2 commits - t1 should see t2's changes.
 64    * Timeline:
 65    * <ol>
 66    * T1 reads data - 10
 67    * T2 writes data - 20
 68    * T1 reads data - 20 (see's T2's uncommitted modfication)
 69    * T2 commits (releases its lock)
 70    * T1 reads data - 20
 71    * </ol>
 72    */
 73  1 public void testReadUncommitted() throws Throwable
 74    {
 75  1 final LockStrategy s = new LockStrategyReadUncommitted();
 76  1 final Semaphore sem = new MyFIFOSemaphore(1);
 77  1 final CyclicBarrier barrier = new CyclicBarrier(2);
 78   
 79  1 Thread t1 = new Thread("t1")
 80    {
 81    Lock lock = null;
 82   
 83  1 public void run()
 84    {
 85  1 try
 86    {
 87  1 sem.acquire(); // we're first to the semaphore
 88   
 89    // log("waiting on barrier");
 90  1 barrier.await(); // wait until t2 joins us
 91    // log("passed barrier");
 92  1 lock = s.readLock();
 93  1 lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS);
 94  1 log("1st read: value is " + value);
 95  1 assertEquals(10, value);
 96  1 sem.release(); // give t2 time to make the modification
 97  1 TestingUtil.sleepThread(100);
 98   
 99  1 sem.acquire(); // to read the uncommitted modification by t2
 100  1 log("2nd read: value is " + value + "; we should see t2's uncommitted change (20)");
 101  1 assertEquals(20, value); // we're seeing the modification by t2 before t2 committed (a.k.a. released the lock)
 102  1 sem.release();
 103  1 TestingUtil.sleepThread(100);
 104   
 105  1 sem.acquire(); // to read the committed change by t2
 106  1 log("3rd read: value is still " + value + "; we should see t2's committed change");
 107  1 assertEquals(20, value);
 108    }
 109    catch (Throwable ex)
 110    {
 111  0 t1_ex = ex;
 112    }
 113    finally
 114    {
 115  1 if (lock != null)
 116  1 lock.unlock();
 117  1 sem.release();
 118    }
 119    }
 120    };
 121   
 122   
 123  1 Thread t2 = new Thread("t2")
 124    {
 125    Lock lock = null;
 126   
 127  1 public void run()
 128    {
 129  1 try
 130    {
 131  1 TestingUtil.sleepThread(100);
 132  1 barrier.await();
 133  1 sem.acquire();
 134  1 lock = s.writeLock();
 135  1 lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS);
 136  1 log("changing value from " + value + " to 20");
 137  1 value = 20;
 138  1 sem.release(); // now t1 can read the uncommitted modification
 139  1 TestingUtil.sleepThread(100);
 140   
 141  1 sem.acquire(); // to unlock the lock
 142  1 log("committing the TX");
 143  1 lock.unlock();
 144    }
 145    catch (Throwable ex)
 146    {
 147  0 t2_ex = ex;
 148    }
 149    finally
 150    {
 151  1 if (lock != null)
 152  1 lock.unlock();
 153  1 sem.release();
 154    }
 155    }
 156    };
 157   
 158  1 t1.start();
 159  1 t2.start();
 160  1 t1.join();
 161  1 t2.join();
 162  1 if (t1_ex != null)
 163  0 throw t1_ex;
 164  1 if (t2_ex != null)
 165  0 throw t2_ex;
 166    }
 167   
 168   
 169    /**
 170    * Thread1 reads data, thread2 changes and - before thread2 commits - t1 should *not* see t2's changes.
 171    * Timeline:
 172    * <ol>
 173    * T1 reads data - 10
 174    * T2 writes data - 20 (*not* visible to T1)
 175    * T1 reads data - 10 (should *not* see T2's uncommitted modfication)
 176    * T2 commits (releases its lock)
 177    * T1 sees T2's committed modification - 20
 178    * </ol>
 179    * <em>Commented for now, until we get the right semantics</em>
 180    * See http://www-128.ibm.com/developerworks/java/library/j-jtp0514.html for a discussion of
 181    * isolation levels
 182    */
 183   
 184    // removed in favor of o.j.c.transaction.IsolationLevelReadCommittedTest.testReadCommitted()
 185    // The lock interceptor makes one request a read lock before *each* read.
 186   
 187    /*public void testReadCommitted() throws Throwable {
 188    final IdentityLock identity_lock=new IdentityLock(IsolationLevel.READ_COMMITTED);
 189   
 190    Thread t1=new Thread("t1") {
 191   
 192    public void run() {
 193    try {
 194    identity_lock.acquireReadLock(this, TIMEOUT);
 195    log("1st read: value is " + value);
 196    assertEquals(10, value);
 197    TestingUtil.sleepThread(SLEEP);
 198   
 199    log("2nd read: value is " + value + "; we should *not* see t2's uncommitted change (20)");
 200    // we're seeing the modification by t2 before t2 committed (a.k.a. released the lock)
 201    assertEquals("This is due to incorrect impl of READ_COMMITTED", 10, value);
 202   
 203    TestingUtil.sleepThread(SLEEP);
 204   
 205    log("3rd read: value is still " + value + "; we should see t2's committed change");
 206    assertEquals(20, value);
 207    }
 208    catch(Throwable ex) {
 209    t1_ex=ex;
 210    }
 211    finally {
 212    identity_lock.unlock(this);
 213    }
 214    }
 215    };
 216   
 217   
 218    Thread t2=new Thread("t2") {
 219   
 220    public void run() {
 221    try {
 222    TestingUtil.sleepThread(100);
 223    identity_lock.acquireWriteLock(this, TIMEOUT);
 224    log("changing value from " + value + " to 20");
 225    value=20;
 226    TestingUtil.sleepThread(SLEEP * 2);
 227   
 228    log("committing the TX");
 229    identity_lock.unlock(this);
 230    }
 231    catch(Throwable ex) {
 232    t2_ex=ex;
 233    }
 234    finally {
 235    identity_lock.unlock(this);
 236    }
 237    }
 238    };
 239   
 240    t1.start();
 241    t2.start();
 242    t1.join();
 243    t2.join();
 244    if(t1_ex != null)
 245    throw t1_ex;
 246    if(t2_ex != null)
 247    throw t2_ex;
 248    }*/
 249  1 public void testWriteThanRead() throws Throwable
 250    {
 251  1 final LockStrategy s = new LockStrategyReadCommitted();
 252   
 253  1 Thread t1 = new Thread("t1")
 254    {
 255    Lock lock = null;
 256   
 257  1 public void run()
 258    {
 259  1 try
 260    {
 261  1 TestingUtil.sleepThread(100);
 262  1 lock = s.readLock();
 263  1 lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS);
 264  1 log("1st read: value is " + value);
 265  1 assertEquals(20, value);
 266  1 TestingUtil.sleepThread(SLEEP);
 267   
 268  1 log("2nd read: value is " + value + "; we should see t2's uncommitted change (20)");
 269  1 assertEquals(20, value); // we're seeing the modification by t2 before t2 committed (a.k.a. released the lock)
 270  1 TestingUtil.sleepThread(SLEEP);
 271    }
 272    catch (Throwable ex)
 273    {
 274  0 t1_ex = ex;
 275    }
 276    finally
 277    {
 278  1 lock.unlock();
 279    }
 280    }
 281    };
 282   
 283   
 284  1 Thread t2 = new Thread("t2")
 285    {
 286    Lock lock = null;
 287   
 288  1 public void run()
 289    {
 290  1 try
 291    {
 292  1 lock = s.writeLock();
 293  1 lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS);
 294  1 log("changing value from " + value + " to 20");
 295  1 value = 20;
 296  1 TestingUtil.sleepThread(SLEEP);
 297   
 298  1 log("committing the TX");
 299  1 lock.unlock();
 300    }
 301    catch (Throwable ex)
 302    {
 303  0 t2_ex = ex;
 304    }
 305    finally
 306    {
 307  1 lock.unlock();
 308    }
 309    }
 310    };
 311   
 312  1 t2.start();
 313  1 t1.start();
 314  1 t2.join();
 315  1 t1.join();
 316  1 if (t1_ex != null)
 317  0 throw t1_ex;
 318  1 if (t2_ex != null)
 319  0 throw t2_ex;
 320    }
 321   
 322   
 323    /**
 324    * Thread1 reads data, thread2 changes and - before thread2 commits - t1 should *not* see t2's changes.
 325    * In addition, Thread1 should *not* see thread2's changes even after thread2 commits, until thread1 commits.
 326    * Timeline:
 327    * <ol>
 328    * T1 reads data - 10
 329    * T2 writes data - 20 (*not* visible to T1)
 330    * T1 reads data - 10 (should *not* see T2's uncommitted modfication)
 331    * T2 commits (releases its lock)
 332    * T1 reads data, should *not* see T2's committed modification - 10
 333    * T1 commits
 334    * T1 starts a new TX - should see 20
 335    * </ol>
 336    * Note: because we use pessimistic locking, the above sequence will effectively be serialized into sequential
 337    * execution: thread1 will acquire the read lock on the data and hold on to it until TX commit, only then will
 338    * thread2 be able to access the data with a write lock.
 339    */
 340  1 public void testRepeatableRead() throws Throwable
 341    {
 342  1 final LockStrategy s = new LockStrategyRepeatableRead();
 343   
 344  1 Thread t1 = new Thread("t1")
 345    {
 346    Lock lock = null;
 347   
 348  1 public void run()
 349    {
 350  1 try
 351    {
 352  1 lock = s.readLock();
 353  1 lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS);
 354  1 log("1st read: value is " + value);
 355  1 assertEquals(10, value);
 356  1 TestingUtil.sleepThread(SLEEP);
 357   
 358  1 log("2nd read: value is " + value + "; we should *not* see t2's uncommitted change (20)");
 359  1 assertEquals(10, value);
 360  1 TestingUtil.sleepThread(SLEEP);
 361   
 362  1 log("3rd read: value is still " + value + "; we should not see t2's committed change");
 363  1 assertEquals(10, value);
 364  1 lock.unlock();
 365   
 366  1 TestingUtil.sleepThread(SLEEP);
 367  1 lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS);
 368  1 log("4th read: value is now " + value + "; we should see t2's committed change in our new TX");
 369  1 assertEquals(20, value);
 370    }
 371    catch (Throwable ex)
 372    {
 373  0 t1_ex = ex;
 374    }
 375    finally
 376    {
 377  1 lock.unlock();
 378    }
 379    }
 380    };
 381   
 382   
 383  1 Thread t2 = new Thread("t2")
 384    {
 385    Lock lock = null;
 386   
 387  1 public void run()
 388    {
 389  1 try
 390    {
 391  1 TestingUtil.sleepThread(100);
 392  1 lock = s.writeLock();
 393  1 lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS);
 394  1 log("changing value from " + value + " to 20");
 395  1 value = 20;
 396  1 TestingUtil.sleepThread(SLEEP);
 397   
 398  1 log("committing the TX");
 399  1 lock.unlock();
 400    }
 401    catch (Throwable ex)
 402    {
 403  0 t2_ex = ex;
 404    }
 405    finally
 406    {
 407  1 lock.unlock();
 408    }
 409    }
 410    };
 411   
 412  1 t1.start();
 413  1 t2.start();
 414  1 t1.join();
 415  1 t2.join();
 416  1 if (t1_ex != null)
 417  0 throw t1_ex;
 418  1 if (t2_ex != null)
 419  0 throw t2_ex;
 420    }
 421   
 422   
 423    /**
 424    * Because only 1 reader or writer can hold the lock at any given time, since thread1 is the first to get the lock,
 425    * it will hold on to it until it commits. The the writer thread (thread2) will have a chance to change the value.
 426    * Timeline:
 427    * <ol>
 428    * T1 reads data - 10
 429    * T1 commits
 430    * T2 writes data - 20
 431    * T2 commits
 432    * T1 starts a new TX and reads data - 20
 433    * T2 commits (releases its lock)
 434    * </ol>
 435    */
 436  1 public void testSerializable() throws Throwable
 437    {
 438  1 final LockStrategy s = new LockStrategySerializable();
 439   
 440  1 Thread t1 = new Thread("t1")
 441    {
 442    Lock lock = null;
 443   
 444  1 public void run()
 445    {
 446  1 try
 447    {
 448  1 lock = s.readLock();
 449  1 lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS);
 450  1 log("1st read: value is " + value);
 451  1 assertEquals(10, value);
 452  1 lock.unlock();
 453  1 TestingUtil.sleepThread(SLEEP);
 454   
 455  1 lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS);
 456  1 log("2nd read: value is " + value + "; we should see t2's committed change (20)");
 457  1 assertEquals(20, value);
 458    }
 459    catch (Throwable ex)
 460    {
 461  0 t1_ex = ex;
 462    }
 463    finally
 464    {
 465  1 lock.unlock();
 466    }
 467    }
 468    };
 469   
 470   
 471  1 Thread t2 = new Thread("t2")
 472    {
 473    Lock lock = null;
 474   
 475  1 public void run()
 476    {
 477  1 try
 478    {
 479  1 TestingUtil.sleepThread(100);
 480  1 lock = s.writeLock();
 481  1 lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS);
 482  1 log("changing value from " + value + " to 20");
 483  1 value = 20;
 484  1 log("committing the TX");
 485  1 lock.unlock();
 486    }
 487    catch (Throwable ex)
 488    {
 489  0 t2_ex = ex;
 490    }
 491    finally
 492    {
 493  1 lock.unlock();
 494    }
 495    }
 496    };
 497   
 498  1 t1.start();
 499  1 t2.start();
 500  1 t1.join();
 501  1 t2.join();
 502  1 if (t1_ex != null)
 503  0 throw t1_ex;
 504  1 if (t2_ex != null)
 505  0 throw t2_ex;
 506    }
 507   
 508  19 void log(String s)
 509    {
 510  19 long now;
 511  19 if (start == 0)
 512  4 start = System.currentTimeMillis();
 513  19 now = System.currentTimeMillis();
 514   
 515  19 System.out.println("[" + Thread.currentThread().getName() + "] [" + (now - start) + "] " + s);
 516    }
 517   
 518   
 519  0 public static void main(String[] args) throws Exception
 520    {
 521  0 junit.textui.TestRunner.run(suite());
 522    }
 523   
 524    // Needed for JUnit.
 525  1 public static Test suite()
 526    {
 527  1 TestSuite suite = new TestSuite();
 528  1 suite.addTestSuite(LockTest.class);
 529  1 return suite;
 530    }
 531   
 532   
 533    }
 534