Mega Code Archive

 
Categories / Java / Threads
 

ReentrantLock

/* Java Threads, 3rd Edition By Scott Oaks, Henry Wong 3rd Edition September 2004  ISBN: 0-596-00782-5 */ import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; // This is a very slow implementation of a ReentrantLock class and is not for //   everyday usage. The purpose of this class is to test for deadlocks. The // lock() //   method now throws a DeadlockDetectedException, if a deadlock occurs. This //   Alternate version has some production properties, including faster //   deadlock check, full implementation of all lock methods, and configurable //   options. public class AlternateDeadlockDetectingLock extends ReentrantLock {   // List of deadlock detecting locks.   // This array is not thread safe, and must be externally synchronized   //    by the class lock. Hence, it should only be called by static   //    methods.   private static List deadlockLocksRegistry = new ArrayList();   private static synchronized void registerLock(       AlternateDeadlockDetectingLock ddl) {     if (!deadlockLocksRegistry.contains(ddl))       deadlockLocksRegistry.add(ddl);   }   private static synchronized void unregisterLock(       AlternateDeadlockDetectingLock ddl) {     if (deadlockLocksRegistry.contains(ddl))       deadlockLocksRegistry.remove(ddl);   }   // List of threads hard waiting for this lock.   // This array is not thread safe, and must be externally synchronized   //    by the class lock. Hence, it should only be called by static   //    methods.   private List hardwaitingThreads = new ArrayList();   private static synchronized void markAsHardwait(List l, Thread t) {     if (!l.contains(t))       l.add(t);   }   private static synchronized void freeIfHardwait(List l, Thread t) {     if (l.contains(t))       l.remove(t);   }   //   // Deadlock checking methods   //   // Given a thread, return all locks that are already owned   // Must own class lock prior to calling this method   private static Iterator getAllLocksOwned(Thread t) {     AlternateDeadlockDetectingLock current;     ArrayList results = new ArrayList();     Iterator itr = deadlockLocksRegistry.iterator();     while (itr.hasNext()) {       current = (AlternateDeadlockDetectingLock) itr.next();       if (current.getOwner() == t)         results.add(current);     }     return results.iterator();   }   // Given a lock, return all threads that are hard waiting for the lock   // Must own class lock prior to calling this method   private static Iterator getAllThreadsHardwaiting(       AlternateDeadlockDetectingLock l) {     return l.hardwaitingThreads.iterator();   }   // Check to see if a thread can perform a hard wait on a lock   // Must call synchronized version only...   private static boolean canThreadWaitOnLock0(Thread t,       AlternateDeadlockDetectingLock l) {     Iterator locksOwned = getAllLocksOwned(t);     while (locksOwned.hasNext()) {       AlternateDeadlockDetectingLock current = (AlternateDeadlockDetectingLock) locksOwned           .next();       // Thread can't wait if lock is already owned. This is the end       // condition       //      for the recursive algorithm -- as the initial condition should be       //      already tested for.       if (current == l)         return false;       Iterator waitingThreads = getAllThreadsHardwaiting(current);       while (waitingThreads.hasNext()) {         Thread otherthread = (Thread) waitingThreads.next();         // In order for the thread to safely wait on the lock, it can't         //   own any locks that have waiting threads that already owns         //   lock. etc. etc. etc. recursively etc.         if (!canThreadWaitOnLock0(otherthread, l)) {           return false;         }       }     }     return true;   }   private static synchronized boolean canThreadWaitOnLock(Thread t,       AlternateDeadlockDetectingLock l) {     // Skip check if there is no owner     // There is a race condition is the owner is null. However, it doesn't     // matter.     //     Testing for no owner ensures none of the threads in the thread wait     //     tree will grab it later -- as all locks in the tree are owned.     if (l.getOwner() == null) {       return true;     }     return canThreadWaitOnLock0(t, l);   }   // Options: variable to control behavior   //    FastFail: if set true, deadlock exception will thrown for every call   // after   //              first exception is detected   //     CleanUp: if set true, lock will cleanup deadlock condition -- allowing   //              for continued operation after failure. FastFail must be off.   //    HWSWTime: # of seconds before a Softwait is to be considered as a   // hardwait.   //              Default is 60 seconds.   private static boolean DDLFastFail = false;   private static boolean DDLCleanUp = false;   private static int DDLHWSWTime = 60;   // Core Constructors   //   public AlternateDeadlockDetectingLock() {     this(false, false);   }   public AlternateDeadlockDetectingLock(boolean fair) {     this(fair, false);   }   private boolean debugging;   public AlternateDeadlockDetectingLock(boolean fair, boolean debug) {     super(fair);     debugging = debug;     registerLock(this);   }   private static boolean DDLdeadlockDETECTED = false;   //   // Core Methods   //   public void lock() {     if (DDLFastFail && DDLdeadlockDETECTED) {       throw new DeadlockDetectedException("EARILER DEADLOCK DETECTED");     }     // Note: Owner can't change if current thread is owner. It is     //       not guaranteed otherwise. Other owners can change due to     //       condition variables.     if (isHeldByCurrentThread()) {       if (debugging)         System.out.println("Already Own Lock");       super.lock();       freeIfHardwait(hardwaitingThreads, Thread.currentThread());       return;     }     // Note: The wait list must be marked before it is tested because     //       there is a race condition between lock() method calls.     markAsHardwait(hardwaitingThreads, Thread.currentThread());     if (canThreadWaitOnLock(Thread.currentThread(), this)) {       if (debugging)         System.out.println("Waiting For Lock");       super.lock();       freeIfHardwait(hardwaitingThreads, Thread.currentThread());       if (debugging)         System.out.println("Got New Lock");     } else {       DDLdeadlockDETECTED = true;       if (DDLCleanUp)         freeIfHardwait(hardwaitingThreads, Thread.currentThread());       throw new DeadlockDetectedException("DEADLOCK DETECTED");     }   }   //   // Note: It is debatable whether this is a hard or soft wait. Even if   //       interruption is common, we don't know if the interrupting thread   //       is also involved in the deadlock. In this alternate version, it   //       will be treated as a hard wait.   public void lockInterruptibly() throws InterruptedException {     if (DDLFastFail && DDLdeadlockDETECTED) {       throw new DeadlockDetectedException("EARILER DEADLOCK DETECTED");     }     // Note: Owner can't change if current thread is owner. It is     //       not guaranteed otherwise. Other owners can change due to     //       condition variables.     if (isHeldByCurrentThread()) {       if (debugging)         System.out.println("Already Own Lock");       try {         super.lockInterruptibly();       } finally {         freeIfHardwait(hardwaitingThreads, Thread.currentThread());       }       return;     }     // Note: The wait list must be marked before it is tested because     //       there is a race condition between lock() method calls.     markAsHardwait(hardwaitingThreads, Thread.currentThread());     if (canThreadWaitOnLock(Thread.currentThread(), this)) {       if (debugging)         System.out.println("Waiting For Lock");       try {         super.lockInterruptibly();       } finally {         freeIfHardwait(hardwaitingThreads, Thread.currentThread());       }       if (debugging)         System.out.println("Got New Lock");     } else {       DDLdeadlockDETECTED = true;       if (DDLCleanUp)         freeIfHardwait(hardwaitingThreads, Thread.currentThread());       throw new DeadlockDetectedException("DEADLOCK DETECTED");     }   }   //   // Note: It is debatable where is the point between a hard wait and a   //      soft wait. Is it still a soft wait, if the timeout is large? As   //      compromise, it is to be considered a hardwait if the timeout   //      is larger than a specified time. Developers should modify this method   //      as needed.   public boolean tryLock(long time, TimeUnit unit)       throws InterruptedException {     if (DDLFastFail && DDLdeadlockDETECTED) {       throw new DeadlockDetectedException("EARILER DEADLOCK DETECTED");     }     // Perform operation as a soft wait     if (unit.toSeconds(time) < DDLHWSWTime) {       return super.tryLock(time, unit);     }     // Note: Owner can't change if current thread is owner. It is     //       not guaranteed otherwise. Other owners can change due to     //       condition variables.     if (isHeldByCurrentThread()) {       if (debugging)         System.out.println("Already Own Lock");       try {         return super.tryLock(time, unit);       } finally {         freeIfHardwait(hardwaitingThreads, Thread.currentThread());       }     }     // Note: The wait list must be marked before it is tested because     //       there is a race condition between lock() method calls.     markAsHardwait(hardwaitingThreads, Thread.currentThread());     if (canThreadWaitOnLock(Thread.currentThread(), this)) {       if (debugging)         System.out.println("Waiting For Lock");       try {         return super.tryLock(time, unit);       } finally {         freeIfHardwait(hardwaitingThreads, Thread.currentThread());         if (debugging)           System.out.println("Got New Lock");       }     } else {       DDLdeadlockDETECTED = true;       if (DDLCleanUp)         freeIfHardwait(hardwaitingThreads, Thread.currentThread());       throw new DeadlockDetectedException("DEADLOCK DETECTED");     }   }   // Note 1: Deadlocks are possible with any hard wait -- this includes   //      the reacquitition of the lock upon return from an await() method.   //      As such, condition variables will mark for the future hard   //      wait, prior to releasing the lock.   // Note 2: There is no need to check for deadlock on this end because   //      a deadlock can be created whether the condition variable owns the   //      lock or is reacquiring it. Since we are marking *before* giving   //      up ownership, the deadlock will be detected on the lock() side   //      first. It is not possible to create a new deadlock just by releasing   //      locks.   public class DeadlockDetectingCondition implements Condition {     Condition embedded;     protected DeadlockDetectingCondition(ReentrantLock lock,         Condition embedded) {       this.embedded = embedded;     }     // Note: The algorithm can detect a deadlock condition if the thead is     //    either waiting for or already owns the lock, or both. This is why     //    we have to mark for waiting *before* giving up the lock.     public void await() throws InterruptedException {       try {         markAsHardwait(hardwaitingThreads, Thread.currentThread());         embedded.await();       } finally {         freeIfHardwait(hardwaitingThreads, Thread.currentThread());       }     }     public void awaitUninterruptibly() {       markAsHardwait(hardwaitingThreads, Thread.currentThread());       embedded.awaitUninterruptibly();       freeIfHardwait(hardwaitingThreads, Thread.currentThread());     }     public long awaitNanos(long nanosTimeout) throws InterruptedException {       try {         markAsHardwait(hardwaitingThreads, Thread.currentThread());         return embedded.awaitNanos(nanosTimeout);       } finally {         freeIfHardwait(hardwaitingThreads, Thread.currentThread());       }     }     public boolean await(long time, TimeUnit unit)         throws InterruptedException {       try {         markAsHardwait(hardwaitingThreads, Thread.currentThread());         return embedded.await(time, unit);       } finally {         freeIfHardwait(hardwaitingThreads, Thread.currentThread());       }     }     public boolean awaitUntil(Date deadline) throws InterruptedException {       try {         markAsHardwait(hardwaitingThreads, Thread.currentThread());         return embedded.awaitUntil(deadline);       } finally {         freeIfHardwait(hardwaitingThreads, Thread.currentThread());       }     }     public void signal() {       embedded.signal();     }     public void signalAll() {       embedded.signalAll();     }   }   // Return a condition variable that support detection of deadlocks   public Condition newCondition() {     return new DeadlockDetectingCondition(this, super.newCondition());   }   //   // Testing routines here   //   // These are very simple tests -- more tests will have to be written   private static Lock a = new AlternateDeadlockDetectingLock(false, true);   private static Lock b = new AlternateDeadlockDetectingLock(false, true);   private static Lock c = new AlternateDeadlockDetectingLock(false, true);   private static Condition wa = a.newCondition();   private static Condition wb = b.newCondition();   private static Condition wc = c.newCondition();   private static void delaySeconds(int seconds) {     try {       Thread.sleep(seconds * 1000);     } catch (InterruptedException ex) {     }   }   private static void awaitSeconds(Condition c, int seconds) {     try {       c.await(seconds, TimeUnit.SECONDS);     } catch (InterruptedException ex) {     }   }   private static void testOne() {     new Thread(new Runnable() {       public void run() {         System.out.println("thread one grab a");         a.lock();         delaySeconds(2);         System.out.println("thread one grab b");         b.lock();         delaySeconds(2);         a.unlock();         b.unlock();       }     }).start();     new Thread(new Runnable() {       public void run() {         System.out.println("thread two grab b");         b.lock();         delaySeconds(2);         System.out.println("thread two grab a");         a.lock();         delaySeconds(2);         a.unlock();         b.unlock();       }     }).start();   }   private static void testTwo() {     new Thread(new Runnable() {       public void run() {         System.out.println("thread one grab a");         a.lock();         delaySeconds(2);         System.out.println("thread one grab b");         b.lock();         delaySeconds(10);         a.unlock();         b.unlock();       }     }).start();     new Thread(new Runnable() {       public void run() {         System.out.println("thread two grab b");         b.lock();         delaySeconds(2);         System.out.println("thread two grab c");         c.lock();         delaySeconds(10);         b.unlock();         c.unlock();       }     }).start();     new Thread(new Runnable() {       public void run() {         System.out.println("thread three grab c");         c.lock();         delaySeconds(4);         System.out.println("thread three grab a");         a.lock();         delaySeconds(10);         c.unlock();         a.unlock();       }     }).start();   }   private static void testThree() {     new Thread(new Runnable() {       public void run() {         System.out.println("thread one grab b");         b.lock();         System.out.println("thread one grab a");         a.lock();         delaySeconds(2);         System.out.println("thread one waits on b");         awaitSeconds(wb, 10);         a.unlock();         b.unlock();       }     }).start();     new Thread(new Runnable() {       public void run() {         delaySeconds(1);         System.out.println("thread two grab b");         b.lock();         System.out.println("thread two grab a");         a.lock();         delaySeconds(10);         b.unlock();         c.unlock();       }     }).start();   }   public static void main(String args[]) {     int test = 1;     if (args.length > 0)       test = Integer.parseInt(args[0]);     switch (test) {     case 1:       testOne(); // 2 threads deadlocking on grabbing 2 locks       break;     case 2:       testTwo(); // 3 threads deadlocking on grabbing 2 out of 3 locks       break;     case 3:       testThree(); // 2 threads deadlocking on 2 locks with CV wait       break;     default:       System.err.println("usage: java DeadlockDetectingLock [ test# ]");     }     delaySeconds(60);     System.out.println("--- End Program ---");     System.exit(0);   } }class DeadlockDetectedException extends RuntimeException {     public DeadlockDetectedException(String s) {         super(s);     } }