[jboss-cvs] JBossAS SVN: r104888 - projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Mon May 17 20:25:06 EDT 2010


Author: bstansberry at jboss.com
Date: 2010-05-17 20:25:06 -0400 (Mon, 17 May 2010)
New Revision: 104888

Modified:
   projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/SharedLocalYieldingClusterLockManager.java
Log:
[JBCLUSTER-268] Add docs, improve method names

Modified: projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/SharedLocalYieldingClusterLockManager.java
===================================================================
--- projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/SharedLocalYieldingClusterLockManager.java	2010-05-17 22:29:11 UTC (rev 104887)
+++ projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/SharedLocalYieldingClusterLockManager.java	2010-05-18 00:25:06 UTC (rev 104888)
@@ -86,17 +86,27 @@
       private LockState lockForLocalNode()
       {
          LockState lockedState = null;
-         boolean updated = false;
-         while (!updated)
+         for (;;)
          {
             LockState current = lockState.get();
-            lockedState = current.incrementAndTake(SharedLocalYieldingClusterLockManager.this.localNode);
-            updated = lockState.compareAndSet(current, lockedState);
+            lockedState = current.takeLocal(SharedLocalYieldingClusterLockManager.this.localNode);
+            if (lockState.compareAndSet(current, lockedState))
+            {
+               break;
+            }
          }
          return lockedState;
       }
       
-      private LockState lockForRemotNode(ClusterNode caller, long timeout) throws TimeoutException
+      /**
+       * Attempts to take the lock, blocking if it has an owner
+       * 
+       * @param caller node that wants the lock
+       * @param timeout max ms to wait to acquire lock
+       * @return  the LockState after the lock is taken
+       * @throws TimeoutException if the lock cannot be acquired within <code>timeout</code> ms
+       */
+      private LockState lockForRemoteNode(ClusterNode caller, long timeout) throws TimeoutException
       {
          LockState lockedState = null;
          
@@ -109,7 +119,7 @@
          {
             // Block while not first in queue or cannot acquire lock
             LockState currentState = lockState.get();
-            lockedState = currentState.take(caller);
+            lockedState = currentState.takeRemote(caller);
             while (waiters.peek() != currentThread || 
                    currentState.lockHolder == SharedLocalYieldingClusterLockManager.this.localNode ||
                    !lockState.compareAndSet(currentState, lockedState)) 
@@ -119,7 +129,7 @@
                   wasInterrupted = true;
                
                currentState = lockState.get();
-               lockedState = currentState.take(caller);
+               lockedState = currentState.takeRemote(caller);
                if (System.currentTimeMillis() >= deadline)
                {
                   // One last attempt
@@ -144,6 +154,14 @@
          return lockedState;
       }
       
+      /**
+       * Decrements the lock count and removes the local node as lock holder
+       * if the count reaches zero.
+       * 
+       * @param caller must be the local node
+       * 
+       * @throws IllegalStateException if <code>caller</code> is not the local node
+       */
       private void unlock(ClusterNode caller)
       {
          LockState current = lockState.get();
@@ -154,7 +172,7 @@
             {
                for (;;)
                {
-                  newState = current.decrementAndRelease();
+                  newState = current.releaseLock();
                   if (lockState.compareAndSet(current, newState))
                   {
                      break;
@@ -167,18 +185,21 @@
             }
             else
             {
-               for (;;)
-               {
-                  newState = current.release();
-                  if (lockState.compareAndSet(current, newState))
-                  {
-                     break;
-                  }
-                  else
-                  {
-                     current = lockState.get();
-                  }
-               }
+               throw new IllegalStateException("Should not receive unlock calls for remote node " + caller);
+               // BES -- the below was an impl, but this case is not correct so
+               // replaced with above exception
+//               for (;;)
+//               {
+//                  newState = current.release();
+//                  if (lockState.compareAndSet(current, newState))
+//                  {
+//                     break;
+//                  }
+//                  else
+//                  {
+//                     current = lockState.get();
+//                  }
+//               }
             }
             
             if (newState.lockHolder == null)
@@ -195,7 +216,7 @@
          LockState newState = null;
          for (;;)
          {
-            newState = current.increment();
+            newState = current.register();
             if (lockState.compareAndSet(current, newState))
             {
                break;
@@ -210,23 +231,29 @@
       
    }
    
+   /**
+    * Immutable data object that encapsulates the state of a LocalLock. Provides
+    * methods that return LockStates for the expected state transitions.
+    */
    private static class LockState
    {
-      private static final LockState AVAILABLE = new LockState(0, null, null);
+      /** The standard initial state */
+      private static final LockState AVAILABLE = new LockState(0, null, null, false);
       
+      /** Number of local callers that have locked or want to lock */
       private final int localLockCount;
+      /** The node that holds the lock */
       private final ClusterNode lockHolder;
+      /** 
+       * Most recent thread to call {@link LocalLock#registerForLocalLock()}.
+       * Used by a thread that asks the cluster for the lock to detect if other
+       * threads have subsequently registered interest and need to be notified
+       * of the result.
+       */
       private final Thread latestRegistrant; 
+      /** Flag indicating the lock object is no longer valid and callers should obtain a new one */
       private final boolean invalid;
       
-      private LockState(int localLockCount, ClusterNode lockHolder, Thread latestRegistrant)
-      {
-         this.localLockCount = localLockCount;
-         this.lockHolder = lockHolder;
-         this.latestRegistrant = latestRegistrant;
-         this.invalid = false;
-      }
-      
       private LockState(int localLockCount, ClusterNode lockHolder, Thread latestRegistrant, boolean invalid)
       {
          this.localLockCount = localLockCount;
@@ -235,35 +262,83 @@
          this.invalid = invalid;
       }
       
-      private LockState increment()
+      /**
+       * Record interest in obtaining the lock.
+       * 
+       * @return a LockState with a lock count one higher than this one and with
+       *         the current thread as latestRegistrant
+       */
+      private LockState register()
       {
-         return new LockState(localLockCount + 1, lockHolder, Thread.currentThread());
+         return new LockState(localLockCount + 1, lockHolder, Thread.currentThread(), invalid);
       }
       
-      private LockState decrement()
+      /**
+       * Record that interest in obtaining the lock is finished. Note that does
+       * not mean release the lock. This method should be called in a finally
+       * block following a call to {@link #register()}.
+       * 
+       * @param decrement <code>true</code> if unregistering should reverse the lock count increase
+       *                  that occurred with {@link #register()}
+       * 
+       * @return a LockState that will not have the calling thread as latestRegistrant and that,
+       *         if <code>decrement</code> is <code>true</code> will have a lock 
+       *         count one less than this one
+       */
+      private LockState unregister(boolean decrement)
       {
-         return new LockState(localLockCount - 1, lockHolder, latestRegistrant);
+         Thread registrant = (latestRegistrant == Thread.currentThread() ? null : latestRegistrant);
+         return new LockState(localLockCount - 1, lockHolder, registrant, invalid);
       }
       
-      private LockState incrementAndTake(ClusterNode owner)
+      /**
+       * Increase the local lock count and assign the lock holder to the given
+       * node. This should only be called with the local node as owner.
+       * 
+       * @param owner the local node.
+       * 
+       * @return a LockState with a lock count one higher than this one and with
+       *         <code>owner</code> as the lock holder
+       */
+      private LockState takeLocal(ClusterNode owner)
       {
-         return new LockState(localLockCount + 1, owner, latestRegistrant);
+         Thread registrant = (latestRegistrant == Thread.currentThread() ? null : latestRegistrant);
+         return new LockState(localLockCount + 1, owner, registrant, invalid);
       }
       
-      private LockState take(ClusterNode owner)
+      /**
+       * Decrease the local lock count, and, if the count is now zero set the
+       * lock holder as null.
+       * 
+       * @return a LockState with a lock count one lower than this one and potentially
+       *         with no lock holder
+       */
+      private LockState releaseLock()
       {
-         return new LockState(localLockCount, owner, latestRegistrant);
+         return localLockCount == 1 
+               ? (latestRegistrant == null && !invalid ? AVAILABLE : new LockState(0, null, latestRegistrant, invalid)) 
+               : new LockState(localLockCount - 1, lockHolder, latestRegistrant, invalid);
       }
       
-      private LockState decrementAndRelease()
+      /**
+       * Assign the given node as lock owner. This should only be called with
+       * a remote node as owner.
+       * 
+       * @param owner a remote node. Should not be <code>null</code>
+       * 
+       * @return a LockState with <code>owner</code> as lock holder
+       */
+      private LockState takeRemote(ClusterNode owner)
       {
-         return localLockCount == 1 ? new LockState(0, null, latestRegistrant) : decrement();
+         return new LockState(localLockCount, owner, latestRegistrant, invalid);
       }
       
-      private LockState release()
-      {
-         return new LockState(localLockCount, null, latestRegistrant);
-      }
+      // BES -- this would be called if it was valid for ClusterHandler.unlock
+      // to be called from a remote caller
+//      private LockState release()
+//      {
+//         return new LockState(localLockCount, null, latestRegistrant);
+//      }
       
       private LockState invalidate()
       {
@@ -272,7 +347,11 @@
       
    }
    
-   /** Handles callbacks from the cluster lock support object */
+   /** 
+    * Handles callbacks from the cluster lock support object. We use a 
+    * separate class to avoid exposing the LocalLockHandler in the top
+    * level class' API.
+    */
    private class ClusterHandler implements LocalLockHandler
    {      
       // ----------------------------------------------------- LocalLockHandler
@@ -307,7 +386,7 @@
          }
          else 
          {
-            LockState currentState = lock.lockForRemotNode(caller, timeout);
+            LockState currentState = lock.lockForRemoteNode(caller, timeout);
             
             // Any local thread who has a ref to lock will now need to request it
             // remotely from caller, which won't grant it until this method returns.
@@ -316,6 +395,8 @@
             // a new lock to handle that.
             localLocks.remove(lockName, lock);
             
+            // Make sure anyone with a reference to lock sees that it is no
+            // longer valid
             LockState invalidated = null;
             for (;;)
             {
@@ -478,7 +559,7 @@
                      synchronized (localLock)
                      {
                         lockState = localLock.lockState.get();
-                        if (lockState.lockHolder == null)
+                        if (lockState.lockHolder != this.localNode)
                         {
                            localLock.wait(remaining);
                         }
@@ -501,21 +582,22 @@
          }
          finally
          {
+            // Unregister interest in the lock
+            
             // If we called clusterSupport.lock() above, its callback into
-            // ClusterHandler.lockFromCluster() will increment localLock.localLockCount.
+            // ClusterHandler.lockFromCluster() will increment the local lock count.
             // So, decrement so we don't double count
             // (If we threw an exception above we should also decrement)
-            if (result != LockResult.ALREADY_HELD)
+            boolean decrement = (result != LockResult.ALREADY_HELD);
+            
+            // Only unregister if the current lock object for this key is
+            // the same one we registered with above
+            boolean updated = false;
+            while (localLocks.get(lockName) == localLock && !updated)
             {
-               // Only decrement if the current lock object for this key is
-               // the same one we incremented above
-               boolean updated = false;
-               while (localLocks.get(lockName) == localLock && !updated)
-               {
-                  LockState currentState = localLock.registerForLocalLock();
-                  LockState decremented = currentState.decrement();
-                  updated = localLock.lockState.compareAndSet(currentState, decremented);
-               }
+               LockState currentState = localLock.lockState.get();
+               LockState unregistered = currentState.unregister(decrement);
+               updated = localLock.lockState.compareAndSet(currentState, unregistered);
             }
          }         
       }
@@ -531,15 +613,22 @@
     *               tracking once all local locks are unlocked.
     */
    public void unlock(Serializable lockName, boolean remove)
-   {
-      LocalLock lock = getLocalLock(lockName, false);
-      if (remove && lock != null)
+   {      
+      if (remove)
       {
-         lock.removable = true;
+         // Mark the lock removable so whatever thread reduces the local
+         // lock count to zero can do the removal
+         LocalLock lock = getLocalLock(lockName, false);
+         if (lock != null)
+         {
+            lock.removable = true;
+         }
       }
       
       this.clusterSupport.unlock(lockName);
       
+      // See if it our responsibility to do a removal
+      LocalLock lock = getLocalLock(lockName, false);
       if (lock != null && lock.removable && lock.lockState.get().lockHolder == null)
       {
          localLocks.remove(lockName, lock);




More information about the jboss-cvs-commits mailing list