Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / MyDerby-10.3 / java / engine / org / apache / derby / impl / services / locks / ConcurrentLockSet.java
diff --git a/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/services/locks/ConcurrentLockSet.java b/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/services/locks/ConcurrentLockSet.java
new file mode 100644 (file)
index 0000000..70671d5
--- /dev/null
@@ -0,0 +1,1011 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.services.locks.ConcurrentLockSet\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.concurrent.atomic.AtomicInteger;\r
+import java.util.concurrent.locks.Condition;\r
+import java.util.concurrent.locks.ReentrantLock;\r
+import java.util.concurrent.ConcurrentHashMap;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.Enumeration;\r
+import java.util.Map;\r
+\r
+\r
+/**\r
+    A ConcurrentLockSet is a complete lock table which maps\r
+    <code>Lockable</code>s to <code>LockControl</code> objects.\r
+\r
+       <P>\r
+       A LockControl contains information about the locks held on a Lockable.\r
+\r
+       <BR>\r
+    MT - Mutable : All public methods of this class, except addWaiters, are\r
+    thread safe. addWaiters can only be called from the thread which performs\r
+    deadlock detection. Only one thread can perform deadlock detection at a\r
+    time.\r
+\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 holding a ReentrantLock associated with the\r
+    Lockable controlled by the LockControl, 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 holding the corresponding ReentrantLock, thus providing the\r
+    single threading that Lockable requires.\r
+       \r
+       @see LockControl\r
+*/\r
+\r
+final class ConcurrentLockSet implements LockTable {\r
+       /*\r
+       ** Fields\r
+       */\r
+       private final AbstractPool factory;\r
+\r
+    /** Hash table which maps <code>Lockable</code> objects to\r
+     * <code>Lock</code>s. */\r
+    private final ConcurrentHashMap<Lockable, Entry> locks;\r
+\r
+    /**\r
+     * List containing all entries seen by the last call to\r
+     * <code>addWaiters()</code>. Makes it possible for the deadlock detection\r
+     * thread to lock all the entries it has visited until it has\r
+     * finished. This prevents false deadlocks from being reported (because all\r
+     * observed waiters must still be waiting when the deadlock detection has\r
+     * completed).\r
+     */\r
+    private ArrayList<Entry> seenByDeadlockDetection;\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 final AtomicInteger blockCount;\r
+\r
+       /*\r
+       ** Constructor\r
+       */\r
+\r
+       ConcurrentLockSet(AbstractPool factory) {\r
+               this.factory = factory;\r
+        blockCount = new AtomicInteger();\r
+               locks = new ConcurrentHashMap<Lockable, Entry>();\r
+       }\r
+\r
+    /**\r
+     * Class representing an entry in the lock table.\r
+     */\r
+    private static final class Entry {\r
+        /** The lock control. */\r
+        Control control;\r
+        /**\r
+         * Mutex used to ensure single-threaded access to the LockControls. To\r
+         * avoid Java deadlocks, no thread should ever hold the mutex of more\r
+         * than one entry. Excepted from this requirement is a thread which\r
+         * performs deadlock detection. During deadlock detection, a thread\r
+         * might hold several mutexes, but it is not allowed to hold any mutex\r
+         * when entering the deadlock detection. Only one thread is allowed to\r
+         * perform deadlock detection at a time.\r
+         */\r
+        private final ReentrantLock mutex = new ReentrantLock();\r
+        /**\r
+         * Condition variable which prevents calls to <code>lock()</code> from\r
+         * locking the entry. If it is not <code>null</code>, only the thread\r
+         * performing deadlock detection may lock the entry (by calling\r
+         * <code>lockForDeadlockDetection()</code>).\r
+         */\r
+        private Condition deadlockDetection;\r
+\r
+        /**\r
+         * Lock the entry, ensuring exclusive access to the contained\r
+         * <code>Control</code> object. The call will block until the entry can\r
+         * be locked. If the entry is unlocked and\r
+         * <code>deadlockDetection</code> is not <code>null</code>, the entry\r
+         * belongs to a thread which waits for deadlock detection to be\r
+         * initiated, and the call will block until that thread has finished\r
+         * its deadlock detection.\r
+         */\r
+        void lock() {\r
+            if (SanityManager.DEBUG) {\r
+                SanityManager.ASSERT(!mutex.isHeldByCurrentThread());\r
+            }\r
+            mutex.lock();\r
+            while (deadlockDetection != null) {\r
+                deadlockDetection.awaitUninterruptibly();\r
+            }\r
+        }\r
+\r
+        /**\r
+         * Unlock the entry, allowing other threads to lock and access the\r
+         * contained <code>Control</code> object.\r
+         */\r
+        void unlock() {\r
+            mutex.unlock();\r
+        }\r
+\r
+        /**\r
+         * Lock the entry while performing deadlock detection. This method will\r
+         * lock the entry even when <code>deadlockDetection</code> is not\r
+         * <code>null</code>. If <code>deadlockDetection</code> is not\r
+         * <code>null</code>, we know the entry and its <code>Control</code>\r
+         * will not be accessed by others until we have finished the deadlock\r
+         * detection, so it's OK for us to access it.\r
+         *\r
+         */\r
+        void lockForDeadlockDetection() {\r
+            if (SanityManager.DEBUG) {\r
+                SanityManager.ASSERT(!mutex.isHeldByCurrentThread());\r
+            }\r
+            mutex.lock();\r
+        }\r
+\r
+        /**\r
+         * Notify that the lock request that is currently accessing the entry\r
+         * will be entering deadlock detection. Unlock the entry to allow the\r
+         * current thread or other threads to lock the entry for deadlock\r
+         * detection, but set the condition variable to prevent regular locking\r
+         * of the entry.\r
+         */\r
+        void enterDeadlockDetection() {\r
+            deadlockDetection = mutex.newCondition();\r
+            mutex.unlock();\r
+        }\r
+\r
+        /**\r
+         * Notify that the deadlock detection triggered by the current thread\r
+         * has finished. Re-lock the entry and notify any waiters that the\r
+         * deadlock detection has completed.\r
+         */\r
+        void exitDeadlockDetection() {\r
+            if (SanityManager.DEBUG) {\r
+                SanityManager.ASSERT(!mutex.isHeldByCurrentThread());\r
+            }\r
+            mutex.lock();\r
+            deadlockDetection.signalAll();\r
+            deadlockDetection = null;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Get an entry from the lock table. If no entry exists for the\r
+     * <code>Lockable</code>, insert an entry. The returned entry will be\r
+     * locked and is guaranteed to still be present in the table.\r
+     *\r
+     * @param ref the <code>Lockable</code> whose entry to return\r
+     * @return the entry for the <code>Lockable</code>, locked for exclusive\r
+     * access\r
+     */\r
+    private Entry getEntry(Lockable ref) {\r
+        Entry e = locks.get(ref);\r
+        while (true) {\r
+            if (e != null) {\r
+                e.lock();\r
+                if (e.control != null) {\r
+                    // entry is found and in use, return it\r
+                    return e;\r
+                }\r
+                // entry is empty, hence it was removed from the table after we\r
+                // retrieved it. Try to reuse it later.\r
+            } else {\r
+                // no entry found, create a new one\r
+                e = new Entry();\r
+                e.lock();\r
+            }\r
+            // reinsert empty entry, or insert the new entry\r
+            Entry current = locks.putIfAbsent(ref, e);\r
+            if (current == null) {\r
+                // successfully (re-)inserted entry, return it\r
+                return e;\r
+            }\r
+            // someone beat us, unlock the old entry and retry with the entry\r
+            // they inserted\r
+            e.unlock();\r
+            e = current;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Check whether there is a deadlock. Make sure that only one thread enters\r
+     * deadlock detection at a time.\r
+     *\r
+     * @param entry the entry in the lock table for the lock request that\r
+     * triggered deadlock detection\r
+     * @param waitingLock the waiting lock\r
+     * @param wakeupReason the reason for waking up the waiter\r
+     * @return an object describing the deadlock\r
+     */\r
+    private Object[] checkDeadlock(Entry entry, ActiveLock waitingLock,\r
+                                   byte wakeupReason) {\r
+        LockControl control = (LockControl) entry.control;\r
+        // make sure that the entry is not blocking other threads performing\r
+        // deadlock detection since we have to wait for them to finish\r
+        entry.enterDeadlockDetection();\r
+        synchronized (Deadlock.class) {\r
+            try {\r
+                return Deadlock.look(factory, this, control, waitingLock,\r
+                                     wakeupReason);\r
+            } finally {\r
+                // unlock all entries we visited\r
+                for (Entry e : seenByDeadlockDetection) {\r
+                    e.unlock();\r
+                }\r
+                seenByDeadlockDetection = null;\r
+                // re-lock the entry\r
+                entry.exitDeadlockDetection();\r
+            }\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
+               LockControl control;\r
+               Lock lockItem;\r
+        String  lockDebug = null;\r
+\r
+        Entry entry = getEntry(ref);\r
+        try {\r
+\r
+            Control gc = entry.control;\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
+                               entry.control = gl;\r
+\r
+                               return gl;\r
+                       }\r
+\r
+                       control = gc.getLockControl();\r
+                       if (control != gc) {\r
+                               entry.control = control;\r
+                       }\r
+\r
+                       if (SanityManager.DEBUG) {\r
+                               SanityManager.ASSERT(ref.equals(control.getLockable()));\r
+                               // ASSERT item is in the list\r
+                SanityManager.ASSERT(\r
+                    locks.get(control.getLockable()).control == control);\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, " +\r
+                            "here's the table";\r
+\r
+                        // We cannot hold a lock on an entry while calling\r
+                        // toDebugString() since it will lock other entries in\r
+                        // the lock table. Holding the lock could cause a\r
+                        // deadlock.\r
+                        entry.unlock();\r
+                        try {\r
+                            lockDebug += toDebugString();\r
+                        } finally {\r
+                            // Re-lock the entry so that the outer finally\r
+                            // clause doesn't fail.\r
+                            entry.lock();\r
+                        }\r
+                    }\r
+                }\r
+\r
+                               return null;\r
+                       }\r
+\r
+        } finally {\r
+            entry.unlock();\r
+        }\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
+                    entry.lock();\r
+                    try {\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
+                                checkDeadlock(entry, waitingLock, 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 (SanityManager.DEBUG &&\r
+                                SanityManager.DEBUG_ON("DeadlockTrace") &&\r
+                                willQuitWait) {\r
+                            // Generate the first part of the debug message\r
+                            // while holding the lock on entry, so that we have\r
+                            // exclusive access to waitingLock. Wait until the\r
+                            // entry has been unlocked before appending the\r
+                            // contents of the lock table (to avoid deadlocks).\r
+                            lockDebug =\r
+                                DiagnosticUtil.toDiagString(waitingLock) +\r
+                                "\nGot deadlock/timeout, here's the table";\r
+                        }\r
+\r
+                    } finally {\r
+                        entry.unlock();\r
+                    }\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 (deadlockTrace && (deadlockData == null)) {\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
+                        if (SanityManager.DEBUG)\r
+                        {\r
+                            if (SanityManager.DEBUG_ON("DeadlockTrace")) {\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 += toDebugString();\r
+                            }\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
+        // assume LockEntry is there\r
+        Entry entry = locks.get(item.getLockable());\r
+        entry.lock();\r
+        try {\r
+            unlock(entry, item, unlockCount);\r
+        } finally {\r
+            entry.unlock();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Unlock an object, previously locked by lockObject().\r
+     *\r
+     * @param entry the entry in which the lock is contained (the current\r
+     * thread must have locked the entry)\r
+     * @param item the item to unlock\r
+     * @param unlockCount the number of times to unlock the item (if zero, take\r
+     * the unlock count from item)\r
+     */\r
+    private void unlock(Entry entry, Latch item, int unlockCount) {\r
+               if (SanityManager.DEBUG) {\r
+            SanityManager.ASSERT(entry.mutex.isHeldByCurrentThread());\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
+        Control control = entry.control;\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
+                SanityManager.ASSERT(\r
+                    locks.get(control.getLockable()).control == control);\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
+                    entry.control = null;\r
+                               }\r
+                               return;\r
+                       }\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 Lock unlockReference(CompatibilitySpace space, Lockable ref,\r
+                                Object qualifier, Map group) {\r
+\r
+        Entry entry = locks.get(ref);\r
+        if (entry == null) {\r
+            return null;\r
+        }\r
+\r
+        entry.lock();\r
+        try {\r
+            Control control = entry.control;\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(entry, lockInGroup, 1);\r
+            }\r
+\r
+            return lockInGroup;\r
+\r
+        } finally {\r
+            entry.unlock();\r
+        }\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
+        Entry entry = locks.get(ref);\r
+        if (entry == null) {\r
+            return true;\r
+        }\r
+\r
+        entry.lock();\r
+        try {\r
+            Control control = entry.control;\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
+        } finally {\r
+            entry.unlock();\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
+    private String toDebugString()\r
+    {\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            String str = new String();\r
+\r
+            int i = 0;\r
+            for (Entry entry : locks.values())\r
+            {\r
+                entry.lock();\r
+                try {\r
+                    str += "\n  lock[" + i + "]: " +\r
+                        DiagnosticUtil.toDiagString(entry.control);\r
+                } finally {\r
+                    entry.unlock();\r
+                }\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
+     * This method can only be called by the thread that is currently\r
+     * performing deadlock detection. All entries that are visited in the lock\r
+     * table will be locked when this method returns. The entries that have\r
+     * been seen and locked will be unlocked after the deadlock detection has\r
+     * finished.\r
+     */\r
+    public void addWaiters(Map waiters) {\r
+        seenByDeadlockDetection = new ArrayList<Entry>(locks.size());\r
+        for (Entry entry : locks.values()) {\r
+            seenByDeadlockDetection.add(entry);\r
+            entry.lockForDeadlockDetection();\r
+            if (entry.control != null) {\r
+                entry.control.addWaiters(waiters);\r
+            }\r
+        }\r
+    }\r
+\r
+//EXCLUDE-START-lockdiag- \r
+       /**\r
+        * make a shallow clone of myself and my lock controls\r
+        */\r
+    public Map<Lockable, Control> shallowClone() {\r
+        HashMap<Lockable, Control> clone = new HashMap<Lockable, Control>();\r
+\r
+        for (Entry entry : locks.values()) {\r
+            entry.lock();\r
+            try {\r
+                Control control = entry.control;\r
+                if (control != null) {\r
+                    clone.put(control.getLockable(), control.shallowClone());\r
+                }\r
+            } finally {\r
+                entry.unlock();\r
+            }\r
+               }\r
+\r
+               return clone;\r
+       }\r
+//EXCLUDE-END-lockdiag- \r
+\r
+       /**\r
+        * Increase blockCount by one.\r
+        */\r
+       public void oneMoreWaiter() {\r
+        blockCount.incrementAndGet();\r
+       }\r
+\r
+       /**\r
+        * Decrease blockCount by one.\r
+        */\r
+       public void oneLessWaiter() {\r
+               blockCount.decrementAndGet();\r
+       }\r
+\r
+    /**\r
+     * Check whether anyone is blocked.\r
+     * @return <code>true</code> if someone is blocked, <code>false</code>\r
+     * otherwise\r
+     */\r
+       public boolean anyoneBlocked() {\r
+        int blocked = blockCount.get();\r
+               if (SanityManager.DEBUG) {\r
+                       SanityManager.ASSERT(\r
+                               blocked >= 0, "blockCount should not be negative");\r
+               }\r
+               return blocked != 0;\r
+       }\r
+}\r