[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