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