--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.services.locks.LockSet\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.services.locks;\r
+\r
+import org.apache.derby.iapi.services.locks.CompatibilitySpace;\r
+import org.apache.derby.iapi.services.locks.Latch;\r
+import org.apache.derby.iapi.services.locks.Lockable;\r
+import org.apache.derby.iapi.services.locks.C_LockFactory;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.services.diag.DiagnosticUtil;\r
+\r
+import org.apache.derby.iapi.reference.Property;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import java.util.HashMap;\r
+import java.util.Enumeration;\r
+import java.util.Iterator;\r
+import java.util.Map;\r
+\r
+\r
+/**\r
+ A LockSet is a complete lock table. A lock table is a hash table\r
+ keyed by a Lockable and with a LockControl as the data element.\r
+\r
+ <P>\r
+ A LockControl contains information about the locks held on a Lockable.\r
+\r
+ <BR>\r
+ MT - Mutable - Container Object : All non-private methods of this class are\r
+ thread safe unless otherwise stated by their javadoc comments.\r
+\r
+ <BR>\r
+ All searching of\r
+ the hashtable is performed using java synchroization(this).\r
+ <BR>\r
+ The class creates ActiveLock and LockControl objects.\r
+ \r
+ LockControl objects are never passed out of this class, All the methods of \r
+ LockControl are called while being synchronized on this, thus providing the\r
+ single threading that LockControl required.\r
+\r
+ Methods of Lockables are only called by this class or LockControl, and \r
+ always while being synchronized on this, thus providing the single \r
+ threading that Lockable requires.\r
+ \r
+ @see LockControl\r
+*/\r
+\r
+final class LockSet implements LockTable {\r
+ /*\r
+ ** Fields\r
+ */\r
+ private final SinglePool factory;\r
+\r
+ /** Hash table which maps <code>Lockable</code> objects to\r
+ * <code>Lock</code>s. */\r
+ private final HashMap locks;\r
+\r
+ /**\r
+ Timeout for deadlocks, in ms.\r
+ <BR>\r
+ MT - immutable\r
+ */\r
+ private int deadlockTimeout = Property.DEADLOCK_TIMEOUT_DEFAULT * 1000;\r
+ private int waitTimeout = Property.WAIT_TIMEOUT_DEFAULT * 1000;\r
+\r
+//EXCLUDE-START-lockdiag- \r
+\r
+ // this varible is set and get without synchronization. \r
+ // Only one thread should be setting it at one time.\r
+ private boolean deadlockTrace;\r
+\r
+//EXCLUDE-END-lockdiag- \r
+\r
+ // The number of waiters for locks\r
+ private int blockCount;\r
+\r
+ /*\r
+ ** Constructor\r
+ */\r
+\r
+ protected LockSet(SinglePool factory) {\r
+ this.factory = factory;\r
+ locks = new HashMap();\r
+ }\r
+\r
+\r
+ /*\r
+ ** Public Methods\r
+ */\r
+\r
+ /**\r
+ * Lock an object within a specific compatibility space.\r
+ *\r
+ * @param compatibilitySpace Compatibility space.\r
+ * @param ref Lockable reference.\r
+ * @param qualifier Qualifier.\r
+ * @param timeout Timeout in milli-seconds\r
+ *\r
+ * @return Object that represents the lock.\r
+ *\r
+ * @exception StandardException Standard Derby policy.\r
+\r
+ */\r
+ public Lock lockObject(CompatibilitySpace compatibilitySpace, Lockable ref,\r
+ Object qualifier, int timeout)\r
+ throws StandardException\r
+ { \r
+ if (SanityManager.DEBUG) {\r
+\r
+ if (SanityManager.DEBUG_ON("memoryLeakTrace")) {\r
+\r
+ if (locks.size() > 1000)\r
+ System.out.println("memoryLeakTrace:LockSet: " +\r
+ locks.size());\r
+ }\r
+ }\r
+\r
+ Control gc;\r
+ LockControl control;\r
+ Lock lockItem;\r
+ String lockDebug = null;\r
+\r
+ synchronized (this) {\r
+\r
+ gc = getControl(ref);\r
+\r
+ if (gc == null) {\r
+\r
+ // object is not locked, can be granted\r
+ Lock gl = new Lock(compatibilitySpace, ref, qualifier);\r
+\r
+ gl.grant();\r
+\r
+ locks.put(ref, gl);\r
+\r
+ return gl;\r
+ }\r
+\r
+ control = gc.getLockControl();\r
+ if (control != gc) {\r
+ locks.put(ref, control);\r
+ }\r
+ \r
+\r
+ if (SanityManager.DEBUG) {\r
+ SanityManager.ASSERT(ref.equals(control.getLockable()));\r
+\r
+ // ASSERT item is in the list\r
+ if (getControl(control.getLockable()) != control)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "lockObject mismatched lock items " + \r
+ getControl(control.getLockable()) + " " + control);\r
+ }\r
+ }\r
+\r
+ lockItem = control.addLock(this, compatibilitySpace, qualifier);\r
+\r
+ if (lockItem.getCount() != 0) {\r
+ return lockItem;\r
+ }\r
+\r
+ if (timeout == C_LockFactory.NO_WAIT) {\r
+\r
+ // remove all trace of lock\r
+ control.giveUpWait(lockItem, this);\r
+\r
+ if (SanityManager.DEBUG) \r
+ {\r
+ if (SanityManager.DEBUG_ON("DeadlockTrace"))\r
+ {\r
+\r
+ SanityManager.showTrace(new Throwable());\r
+\r
+ // The following dumps the lock table as it \r
+ // exists at the time a timeout is about to \r
+ // cause a deadlock exception to be thrown.\r
+\r
+ lockDebug = \r
+ DiagnosticUtil.toDiagString(lockItem) +\r
+ "\nCould not grant lock with zero timeout, here's the table" +\r
+ this.toDebugString();\r
+ }\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ } // synchronized block\r
+\r
+ boolean deadlockWait = false;\r
+ int actualTimeout;\r
+\r
+ if (timeout == C_LockFactory.WAIT_FOREVER)\r
+ {\r
+ // always check for deadlocks as there should not be any\r
+ deadlockWait = true;\r
+ if ((actualTimeout = deadlockTimeout) == C_LockFactory.WAIT_FOREVER)\r
+ actualTimeout = Property.DEADLOCK_TIMEOUT_DEFAULT * 1000;\r
+ }\r
+ else\r
+ {\r
+\r
+ if (timeout == C_LockFactory.TIMED_WAIT)\r
+ timeout = actualTimeout = waitTimeout;\r
+ else\r
+ actualTimeout = timeout;\r
+\r
+\r
+ // five posible cases\r
+ // i) timeout -1, deadlock -1 -> \r
+ // just wait forever, no deadlock check\r
+ // ii) timeout >= 0, deadlock -1 -> \r
+ // just wait for timeout, no deadlock check\r
+ // iii) timeout -1, deadlock >= 0 -> \r
+ // wait for deadlock, then deadlock check, \r
+ // then infinite timeout\r
+ // iv) timeout >=0, deadlock < timeout -> \r
+ // wait for deadlock, then deadlock check, \r
+ // then wait for (timeout - deadlock)\r
+ // v) timeout >=0, deadlock >= timeout -> \r
+ // just wait for timeout, no deadlock check\r
+\r
+\r
+ if (deadlockTimeout >= 0) {\r
+\r
+ if (actualTimeout < 0) {\r
+ // infinite wait but perform a deadlock check first\r
+ deadlockWait = true;\r
+ actualTimeout = deadlockTimeout;\r
+ } else if (deadlockTimeout < actualTimeout) {\r
+\r
+ // deadlock wait followed by a timeout wait\r
+\r
+ deadlockWait = true;\r
+ actualTimeout = deadlockTimeout;\r
+\r
+ // leave timeout as the remaining time\r
+ timeout -= deadlockTimeout;\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ ActiveLock waitingLock = (ActiveLock) lockItem;\r
+ lockItem = null;\r
+\r
+ int earlyWakeupCount = 0;\r
+ long startWaitTime = 0;\r
+\r
+forever: for (;;) {\r
+\r
+ byte wakeupReason = waitingLock.waitForGrant(actualTimeout);\r
+ \r
+ ActiveLock nextWaitingLock = null;\r
+ Object[] deadlockData = null;\r
+\r
+ try {\r
+ boolean willQuitWait;\r
+ Enumeration timeoutLockTable = null;\r
+ long currentTime = 0;\r
+ \r
+ synchronized (this) {\r
+\r
+ if (control.isGrantable(\r
+ control.firstWaiter() == waitingLock,\r
+ compatibilitySpace,\r
+ qualifier)) {\r
+\r
+ // Yes, we are granted, put us on the granted queue.\r
+ control.grant(waitingLock);\r
+\r
+ // Remove from the waiting queue & get next waiter\r
+ nextWaitingLock = \r
+ control.getNextWaiter(waitingLock, true, this);\r
+\r
+ return waitingLock;\r
+ }\r
+\r
+ // try again later\r
+ waitingLock.clearPotentiallyGranted(); \r
+\r
+ willQuitWait = \r
+ (wakeupReason != Constants.WAITING_LOCK_GRANT);\r
+\r
+ if (((wakeupReason == Constants.WAITING_LOCK_IN_WAIT) &&\r
+ deadlockWait) ||\r
+ (wakeupReason == Constants.WAITING_LOCK_DEADLOCK))\r
+ {\r
+\r
+ // check for a deadlock, even if we were woken up \r
+ // because we were selected as a victim we still \r
+ // check because the situation may have changed.\r
+ deadlockData = \r
+ Deadlock.look(\r
+ factory, this, control, waitingLock, \r
+ wakeupReason);\r
+\r
+ if (deadlockData == null) {\r
+ // we don't have a deadlock\r
+ deadlockWait = false;\r
+\r
+ actualTimeout = timeout;\r
+ startWaitTime = 0;\r
+ willQuitWait = false;\r
+ } else {\r
+ willQuitWait = true;\r
+ }\r
+ }\r
+\r
+ nextWaitingLock = \r
+ control.getNextWaiter(\r
+ waitingLock, willQuitWait, this);\r
+\r
+\r
+ // If we were not woken by another then we have\r
+ // timed out. Either deadlock out or timeout\r
+ if (willQuitWait) {\r
+\r
+ if (SanityManager.DEBUG) \r
+ {\r
+ if (SanityManager.DEBUG_ON("DeadlockTrace"))\r
+ {\r
+\r
+ SanityManager.showTrace(new Throwable());\r
+\r
+ // The following dumps the lock table as it \r
+ // exists at the time a timeout is about to \r
+ // cause a deadlock exception to be thrown.\r
+\r
+ lockDebug = \r
+ DiagnosticUtil.toDiagString(waitingLock) +\r
+ "\nGot deadlock/timeout, here's the table" +\r
+ this.toDebugString();\r
+ }\r
+ }\r
+ \r
+ if (deadlockTrace && (deadlockData == null))\r
+ {\r
+ // if ending lock request due to lock timeout\r
+ // want a copy of the LockTable and the time,\r
+ // in case of deadlock deadlockData has the\r
+ // info we need.\r
+ currentTime = System.currentTimeMillis(); \r
+ timeoutLockTable = \r
+ factory.makeVirtualLockTable();\r
+ }\r
+ }\r
+\r
+ } // synchronized block\r
+\r
+ // need to do this outside of the synchronized block as the\r
+ // message text building (timeouts and deadlocks) may \r
+ // involve getting locks to look up table names from \r
+ // identifiers.\r
+\r
+ if (willQuitWait)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (lockDebug != null)\r
+ {\r
+ String type = \r
+ ((deadlockData != null) ? \r
+ "deadlock:" : "timeout:"); \r
+\r
+ SanityManager.DEBUG_PRINT(\r
+ type,\r
+ "wait on lockitem caused " + type + \r
+ lockDebug);\r
+ }\r
+\r
+ }\r
+\r
+ if (deadlockData == null)\r
+ {\r
+ // ending wait because of lock timeout.\r
+\r
+ if (deadlockTrace)\r
+ { \r
+ // Turn ON derby.locks.deadlockTrace to build \r
+ // the lockTable.\r
+ \r
+ \r
+ throw Timeout.buildException(\r
+ waitingLock, timeoutLockTable, currentTime);\r
+ }\r
+ else\r
+ {\r
+ StandardException se = \r
+ StandardException.newException(\r
+ SQLState.LOCK_TIMEOUT);\r
+\r
+ throw se;\r
+ }\r
+ }\r
+ else \r
+ {\r
+ // ending wait because of lock deadlock.\r
+\r
+ throw Deadlock.buildException(\r
+ factory, deadlockData);\r
+ }\r
+ }\r
+ } finally {\r
+ if (nextWaitingLock != null) {\r
+ nextWaitingLock.wakeUp(Constants.WAITING_LOCK_GRANT);\r
+ nextWaitingLock = null;\r
+ }\r
+ }\r
+\r
+ if (actualTimeout != C_LockFactory.WAIT_FOREVER) {\r
+\r
+ if (wakeupReason != Constants.WAITING_LOCK_IN_WAIT)\r
+ earlyWakeupCount++;\r
+\r
+ if (earlyWakeupCount > 5) {\r
+\r
+ long now = System.currentTimeMillis();\r
+\r
+ if (startWaitTime != 0) {\r
+\r
+ long sleepTime = now - startWaitTime;\r
+\r
+ actualTimeout -= sleepTime;\r
+ }\r
+\r
+ startWaitTime = now;\r
+ }\r
+ }\r
+\r
+\r
+ } // for(;;)\r
+ }\r
+\r
+ /**\r
+ Unlock an object, previously locked by lockObject(). \r
+\r
+ If unlockCOunt is not zero then the lock will be unlocked\r
+ that many times, otherwise the unlock count is taken from\r
+ item.\r
+\r
+ */\r
+ public void unlock(Latch item, int unlockCount) {\r
+\r
+ if (SanityManager.DEBUG) {\r
+ if (SanityManager.DEBUG_ON(Constants.LOCK_TRACE)) {\r
+ /*\r
+ ** I don't like checking the trace flag twice, but SanityManager\r
+ ** doesn't provide a way to get to the debug trace stream\r
+ ** directly.\r
+ */\r
+ SanityManager.DEBUG(\r
+ Constants.LOCK_TRACE, \r
+ "Release lock: " + DiagnosticUtil.toDiagString(item));\r
+ }\r
+ }\r
+\r
+ boolean tryGrant = false;\r
+ ActiveLock nextGrant = null;\r
+\r
+ synchronized (this) {\r
+\r
+ Control control = getControl(item.getLockable());\r
+ \r
+ if (SanityManager.DEBUG) {\r
+\r
+ // only valid Lock's expected\r
+ if (item.getLockable() == null)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "item.getLockable() = null." +\r
+ "unlockCount " + unlockCount + \r
+ "item = " + DiagnosticUtil.toDiagString(item));\r
+ }\r
+\r
+ // only valid Lock's expected\r
+ if (control == null)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "control = null." +\r
+ "unlockCount " + unlockCount + \r
+ "item = " + DiagnosticUtil.toDiagString(item));\r
+ }\r
+\r
+ if (getControl(control.getLockable()) != control)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "unlock mismatched lock items " + \r
+ getControl(control.getLockable()) + " " + control);\r
+ }\r
+\r
+ if ((unlockCount != 0) && (unlockCount > item.getCount()))\r
+ SanityManager.THROWASSERT("unlockCount " + unlockCount +\r
+ " larger than actual lock count " + item.getCount() + " item " + item);\r
+ }\r
+\r
+ tryGrant = control.unlock(item, unlockCount);\r
+ item = null;\r
+\r
+ boolean mayBeEmpty = true;\r
+ if (tryGrant) {\r
+ nextGrant = control.firstWaiter();\r
+ if (nextGrant != null) {\r
+ mayBeEmpty = false;\r
+ if (!nextGrant.setPotentiallyGranted())\r
+ nextGrant = null;\r
+ }\r
+ }\r
+\r
+ if (mayBeEmpty) {\r
+ if (control.isEmpty()) {\r
+ // no-one granted, no-one waiting, remove lock control\r
+ locks.remove(control.getLockable());\r
+ }\r
+ return;\r
+ }\r
+ } // synchronized (this)\r
+\r
+ if (tryGrant && (nextGrant != null)) {\r
+ nextGrant.wakeUp(Constants.WAITING_LOCK_GRANT);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Unlock an object once if it is present in the specified group. Also\r
+ * remove the object from the group.\r
+ *\r
+ * @param space the compatibility space\r
+ * @param ref a reference to the locked object\r
+ * @param qualifier qualifier of the lock\r
+ * @param group a map representing the locks in a group\r
+ * @return the corresponding lock in the group map, or <code>null</code> if\r
+ * the object was not unlocked\r
+ */\r
+ public synchronized Lock unlockReference(CompatibilitySpace space,\r
+ Lockable ref, Object qualifier,\r
+ Map group) {\r
+\r
+ Control control = getControl(ref);\r
+ if (control == null) {\r
+ return null;\r
+ }\r
+\r
+ Lock setLock = control.getLock(space, qualifier);\r
+ if (setLock == null) {\r
+ return null;\r
+ }\r
+\r
+ Lock lockInGroup = (Lock) group.remove(setLock);\r
+ if (lockInGroup != null) {\r
+ unlock(lockInGroup, 1);\r
+ }\r
+\r
+ return lockInGroup;\r
+ }\r
+\r
+ /**\r
+ * Lock an object and release the lock immediately. Equivalent to\r
+ * <pre>\r
+ * Lock lock = lockTable.lockObject(space, ref, qualifier, timeout);\r
+ * lockTable.unlock(lock, 1);\r
+ * </pre>\r
+ * except that the implementation is more efficient.\r
+ *\r
+ * @param space the compatibility space\r
+ * @param ref a reference to the locked object\r
+ * @param qualifier qualifier of the lock\r
+ * @param timeout maximum time to wait in milliseconds\r
+ * (<code>LockFactory.NO_WAIT</code> means don't wait)\r
+ * @return <code>true</code> if the object was locked, or\r
+ * <code>false</code>if the timeout was <code>NO_WAIT</code> and the lock\r
+ * couldn't be obtained immediately\r
+ * @exception StandardException if the lock could not be obtained\r
+ */\r
+ public boolean zeroDurationLockObject(\r
+ CompatibilitySpace space, Lockable ref, Object qualifier, int timeout)\r
+ throws StandardException {\r
+\r
+ if (SanityManager.DEBUG) {\r
+ if (SanityManager.DEBUG_ON(Constants.LOCK_TRACE)) {\r
+ D_LockControl.debugLock(\r
+ "Zero Duration Lock Request before Grant: ",\r
+ space, null, ref, qualifier, timeout);\r
+ if (SanityManager.DEBUG_ON(Constants.LOCK_STACK_TRACE)) {\r
+ // The following will print the stack trace of the lock\r
+ // request to the log.\r
+ Throwable t = new Throwable();\r
+ java.io.PrintWriter istream =\r
+ SanityManager.GET_DEBUG_STREAM();\r
+ istream.println("Stack trace of lock request:");\r
+ t.printStackTrace(istream);\r
+ }\r
+ }\r
+ }\r
+\r
+ // Very fast zeroDurationLockObject() for unlocked objects.\r
+ // If no entry exists in the lock manager for this reference\r
+ // then it must be unlocked.\r
+ // If the object is locked then we perform a grantable\r
+ // check, skipping over any waiters.\r
+ // If the caller wants to wait and the lock cannot\r
+ // be granted then we do the slow join the queue and\r
+ // release the lock method.\r
+\r
+ synchronized (this) {\r
+ Control control = getControl(ref);\r
+ if (control == null) {\r
+ return true;\r
+ }\r
+\r
+ // If we are grantable, ignoring waiting locks then\r
+ // we can also grant this request now, as skipping\r
+ // over the waiters won't block them as we release\r
+ // the lock rightway.\r
+ if (control.isGrantable(true, space, qualifier)) {\r
+ return true;\r
+ }\r
+\r
+ // can't be granted and are not willing to wait.\r
+ if (timeout == C_LockFactory.NO_WAIT) {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ Lock lock = lockObject(space, ref, qualifier, timeout);\r
+\r
+ if (SanityManager.DEBUG) {\r
+ if (SanityManager.DEBUG_ON(Constants.LOCK_TRACE)) {\r
+ D_LockControl.debugLock(\r
+ "Zero Lock Request Granted: ",\r
+ space, null, ref, qualifier, timeout);\r
+ }\r
+ }\r
+\r
+ // and simply unlock it once\r
+ unlock(lock, 1);\r
+\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Set the deadlock timeout.\r
+ *\r
+ * @param timeout deadlock timeout in milliseconds\r
+ */\r
+ public void setDeadlockTimeout(int timeout) {\r
+ deadlockTimeout = timeout;\r
+ }\r
+\r
+ /**\r
+ * Set the wait timeout.\r
+ *\r
+ * @param timeout wait timeout in milliseconds\r
+ */\r
+ public void setWaitTimeout(int timeout) {\r
+ waitTimeout = timeout;\r
+ }\r
+ \r
+ /*\r
+ ** Non public methods\r
+ */\r
+//EXCLUDE-START-lockdiag- \r
+\r
+ public void setDeadlockTrace(boolean val)\r
+ {\r
+ // set this without synchronization\r
+ deadlockTrace = val;\r
+ } \r
+//EXCLUDE-END-lockdiag- \r
+\r
+ public String toDebugString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ String str = new String();\r
+\r
+ int i = 0;\r
+ for (Iterator it = locks.values().iterator(); it.hasNext(); )\r
+ {\r
+ str += "\n lock[" + i + "]: " + \r
+ DiagnosticUtil.toDiagString(it.next());\r
+ }\r
+\r
+ return(str);\r
+ }\r
+ else\r
+ {\r
+ return(null);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Add all waiters in this lock table to a <code>Map</code> object.\r
+ * <br>\r
+ * MT - must be synchronized on this <code>LockSet</code> object.\r
+ */\r
+ public void addWaiters(Map waiters) {\r
+ for (Iterator it = locks.values().iterator(); it.hasNext(); ) {\r
+ Control control = (Control) it.next();\r
+ control.addWaiters(waiters);\r
+ }\r
+ }\r
+\r
+//EXCLUDE-START-lockdiag- \r
+ /**\r
+ * make a shallow clone of myself and my lock controls\r
+ */\r
+ public synchronized Map shallowClone()\r
+ {\r
+ HashMap clone = new HashMap();\r
+\r
+ for (Iterator it = locks.keySet().iterator(); it.hasNext(); )\r
+ {\r
+ Lockable lockable = (Lockable) it.next();\r
+ Control control = getControl(lockable);\r
+\r
+ clone.put(lockable, control.shallowClone());\r
+ }\r
+\r
+ return clone;\r
+ }\r
+//EXCLUDE-END-lockdiag- \r
+\r
+ /*\r
+ ** Support for anyoneBlocked(). These methods assume that caller\r
+ ** is synchronized on this LockSet object.\r
+ */\r
+\r
+ /**\r
+ * Increase blockCount by one.\r
+ * <BR>\r
+ * MT - must be synchronized on this <code>LockSet</code> object.\r
+ */\r
+ public void oneMoreWaiter() {\r
+ blockCount++;\r
+ }\r
+\r
+ /**\r
+ * Decrease blockCount by one.\r
+ * <BR>\r
+ * MT - must be synchronized on this <code>LockSet</code> object.\r
+ */\r
+ public void oneLessWaiter() {\r
+ blockCount--;\r
+ }\r
+\r
+ public synchronized boolean anyoneBlocked() {\r
+ if (SanityManager.DEBUG) {\r
+ SanityManager.ASSERT(\r
+ blockCount >= 0, "blockCount should not be negative");\r
+ }\r
+\r
+ return blockCount != 0;\r
+ }\r
+\r
+ /**\r
+ * Get the <code>Control</code> for an object in the lock table.\r
+ * <br>\r
+ * MT - must be synchronized on this <code>LockSet</code> object.\r
+ */\r
+ private final Control getControl(Lockable ref) {\r
+ return (Control) locks.get(ref);\r
+ }\r
+}\r