Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / derby-10.3.2.1 / java / engine / org / apache / derby / impl / services / locks / LockSpace.java
diff --git a/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/services/locks/LockSpace.java b/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/services/locks/LockSpace.java
new file mode 100644 (file)
index 0000000..89dc485
--- /dev/null
@@ -0,0 +1,391 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.services.locks.LockSpace\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.Lockable;\r
+import org.apache.derby.iapi.services.locks.Limit;\r
+\r
+import org.apache.derby.iapi.util.Matchable;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import java.util.Enumeration;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+\r
+/**\r
+\r
+       A LockSpace represents the complete set of locks held within\r
+       a single compatibility space, broken into groups of locks.\r
+\r
+    A LockSpace contains a HashMap keyed by the group reference,\r
+    the data for each key is a HashMap of Lock's.\r
+\r
+    <p> A <code>LockSpace</code> can have an owner (for instance a\r
+    transaction). Currently, the owner is used by the virtual lock table to\r
+    find out which transaction a lock belongs to. Some parts of the code also\r
+    use the owner as a group object which guarantees that the lock is released\r
+    on a commit or an abort. The owner has no special meaning to the lock\r
+    manager and can be any object, including <code>null</code>. </p>\r
+*/\r
+final class LockSpace implements CompatibilitySpace {\r
+\r
+       /** Map from group references to groups of locks. */\r
+       private final HashMap groups;\r
+       /** Reference to the owner of this compatibility space. */\r
+       private final Object owner;\r
+\r
+       private HashMap spareGroups[] = new HashMap[3];\r
+\r
+       // the Limit info.\r
+       private Object callbackGroup;\r
+       private int    limit;\r
+       private int    nextLimitCall;\r
+       private Limit  callback;\r
+\r
+       /**\r
+        * Creates a new <code>LockSpace</code> instance.\r
+        *\r
+        * @param owner an object representing the owner of the compatibility space\r
+        */\r
+       LockSpace(Object owner) {\r
+               groups = new HashMap();\r
+               this.owner = owner;\r
+       }\r
+\r
+       /**\r
+        * Get the object representing the owner of the compatibility space.\r
+        *\r
+        * @return the owner of the compatibility space\r
+        */\r
+       public Object getOwner() {\r
+               return owner;\r
+       }\r
+\r
+       /**\r
+               Add a lock to a group.\r
+       */\r
+       protected synchronized void addLock(Object group, Lock lock)\r
+               throws StandardException {\r
+\r
+               Lock lockInGroup = null;\r
+\r
+               HashMap dl = (HashMap) groups.get(group);\r
+               if (dl == null) {\r
+                       dl = getGroupMap(group);\r
+               } else if (lock.getCount() != 1) {\r
+                       lockInGroup = (Lock) dl.get(lock);\r
+               }\r
+\r
+               if (lockInGroup == null) {\r
+                       lockInGroup = lock.copy();\r
+                       dl.put(lockInGroup, lockInGroup);\r
+               }\r
+               lockInGroup.count++;\r
+\r
+               if (inLimit)\r
+                       return;\r
+\r
+               if (!group.equals(callbackGroup))\r
+                       return;\r
+\r
+               int groupSize = dl.size();\r
+               \r
+               if (groupSize > nextLimitCall) {\r
+\r
+                       inLimit = true;\r
+                       callback.reached(this, group, limit,\r
+                               new LockList(java.util.Collections.enumeration(dl.keySet())), groupSize);\r
+                       inLimit = false;\r
+\r
+                       // see when the next callback should occur, if the callback\r
+                       // failed to release a sufficent amount of locks then\r
+                       // delay until another "limit" locks are obtained.\r
+                       int newGroupSize = dl.size();\r
+                       if (newGroupSize < (limit / 2))\r
+                               nextLimitCall = limit;\r
+                       else if (newGroupSize < (nextLimitCall / 2))\r
+                               nextLimitCall -= limit;\r
+                       else\r
+                               nextLimitCall += limit;\r
+\r
+               }\r
+       }\r
+       \r
+       private boolean inLimit;\r
+       /**\r
+               Unlock all the locks in a group and then remove the group.\r
+       */\r
+\r
+       synchronized void unlockGroup(LockTable lset, Object group) {\r
+               HashMap dl = (HashMap) groups.remove(group);\r
+               if (dl == null)\r
+                       return;\r
+\r
+               for (Iterator list = dl.keySet().iterator(); list.hasNext(); ) {\r
+                       lset.unlock((Lock) list.next(), 0);\r
+               }\r
+\r
+               if ((callbackGroup != null) && group.equals(callbackGroup)) {\r
+                       nextLimitCall = limit;\r
+               }\r
+\r
+               saveGroup(dl);\r
+       }\r
+\r
+       private HashMap getGroupMap(Object group) {\r
+               HashMap[] sg = spareGroups;\r
+               HashMap dl = null;\r
+               for (int i = 0; i < 3; i++) {\r
+                       dl = sg[i];\r
+                       if (dl != null) {\r
+                               sg[i] = null;\r
+                               break;\r
+                       }\r
+               }\r
+\r
+               if (dl == null)\r
+                       dl = new HashMap(5, 0.8f);\r
+\r
+               groups.put(group, dl);\r
+               return dl;\r
+       }\r
+       private void saveGroup(HashMap dl) {\r
+               HashMap[] sg = spareGroups;\r
+               for (int i = 0; i < 3; i++) {\r
+                       if (sg[i] == null) {\r
+                               sg[i] = dl;\r
+                               dl.clear();\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+               Unlock all locks in the group that match the key\r
+       */\r
+       synchronized void unlockGroup(LockTable lset, Object group, Matchable key) {\r
+               HashMap dl = (HashMap) groups.get(group);\r
+               if (dl == null)\r
+                       return; //  no group at all\r
+\r
+               boolean allUnlocked = true;\r
+               for (Iterator e = dl.keySet().iterator(); e.hasNext(); ) {\r
+\r
+                       Lock lock = (Lock) e.next();\r
+                       if (!key.match(lock.getLockable())) {\r
+                               allUnlocked = false;\r
+                               continue;\r
+                       }\r
+                       lset.unlock(lock, 0);\r
+                       e.remove();\r
+               }\r
+\r
+               if (allUnlocked) {\r
+                       groups.remove(group);\r
+                       saveGroup(dl);\r
+                       if ((callbackGroup != null) && group.equals(callbackGroup)) {\r
+                               nextLimitCall = limit;\r
+                       }\r
+               }\r
+       }\r
+\r
+       synchronized void transfer(Object oldGroup, Object newGroup) {\r
+               HashMap from = (HashMap) groups.get(oldGroup);\r
+               if (from == null)\r
+                       return;\r
+\r
+               HashMap to = (HashMap) groups.get(newGroup);\r
+               if (to == null) {\r
+                       // simple case \r
+                       groups.put(newGroup, from);\r
+                       clearLimit(oldGroup);\r
+                       groups.remove(oldGroup);\r
+                       return;\r
+               }\r
+\r
+               if (to.size() < from.size()) {\r
+\r
+                       // place the contents of to into from\r
+                       mergeGroups(to, from);\r
+\r
+                       Object oldTo = groups.put(newGroup, from);\r
+                       if (SanityManager.DEBUG) {\r
+                               SanityManager.ASSERT(oldTo == to, "inconsistent state in LockSpace");\r
+                       }\r
+\r
+               } else {\r
+                       mergeGroups(from, to);\r
+               }\r
+               \r
+               clearLimit(oldGroup);\r
+               groups.remove(oldGroup);\r
+       }\r
+\r
+       private void mergeGroups(HashMap from, HashMap into) {\r
+\r
+               for (Iterator e = from.keySet().iterator(); e.hasNext(); ) {\r
+\r
+                       Object lock = e.next();\r
+\r
+                       Object lockI = into.get(lock);\r
+\r
+                       if (lockI == null) {\r
+                               // lock is only in from list\r
+                               into.put(lock, lock);\r
+                       } else {\r
+                               // merge the locks\r
+                               Lock fromL = (Lock) lock;\r
+                               Lock intoL = (Lock) lockI;\r
+\r
+                               intoL.count += fromL.getCount();\r
+                       }\r
+               }\r
+\r
+       }\r
+\r
+       synchronized int unlockReference(LockTable lset, Lockable ref,\r
+                                                                        Object qualifier, Object group) {\r
+\r
+               // look for locks matching our reference and qualifier.\r
+               HashMap dl = (HashMap) groups.get(group);\r
+               if (dl == null)\r
+                       return 0;\r
+\r
+               Lock lockInGroup = lset.unlockReference(this, ref, qualifier, dl);\r
+               if (lockInGroup == null) {\r
+                       return 0;\r
+               }\r
+\r
+               if (lockInGroup.getCount() == 1) {\r
+\r
+                       if (dl.isEmpty()) {\r
+                               groups.remove(group);\r
+                               saveGroup(dl);\r
+                               if ((callbackGroup != null) && group.equals(callbackGroup)) {\r
+                                       nextLimitCall = limit;\r
+                               }\r
+                       }\r
+\r
+                       return 1;\r
+               }\r
+                       \r
+               // the lock item will be left in the group\r
+               lockInGroup.count--;\r
+               dl.put(lockInGroup, lockInGroup);\r
+               return 1;\r
+       }\r
+\r
+       /**\r
+               Return true if locks are held in a group\r
+       */\r
+       synchronized boolean areLocksHeld(Object group) {\r
+               return groups.containsKey(group);\r
+       }\r
+\r
+       /**\r
+        * Return true if locks are held in this compatibility space.\r
+        * @return true if locks are held, false otherwise\r
+        */\r
+       synchronized boolean areLocksHeld() {\r
+               return !groups.isEmpty();\r
+       }\r
+       \r
+       synchronized boolean isLockHeld(Object group, Lockable ref, Object qualifier) {\r
+\r
+               // look for locks matching our reference and qualifier.\r
+               HashMap dl = (HashMap) groups.get(group);\r
+               if (dl == null)\r
+                       return false;\r
+\r
+               Object heldLock = dl.get(new Lock(this, ref, qualifier));\r
+               return (heldLock != null);\r
+       }\r
+\r
+       synchronized void setLimit(Object group, int limit, Limit callback) {\r
+               callbackGroup = group;\r
+               this.nextLimitCall = this.limit = limit;\r
+               this.callback = callback;\r
+       }\r
+\r
+       /**\r
+               Clear a limit set by setLimit.\r
+       */\r
+       synchronized void clearLimit(Object group) {\r
+               if (group.equals(callbackGroup)) {\r
+                       callbackGroup = null;\r
+                       nextLimitCall = limit = Integer.MAX_VALUE;\r
+                       callback = null;\r
+               }\r
+       }\r
+\r
+       /**\r
+               Return a count of the number of locks\r
+               held by this space. The argument bail\r
+               indicates at which point the counting\r
+               should bail out and return the current\r
+               count. This routine will bail if the\r
+               count is greater than bail. Thus this\r
+               routine is intended to for deadlock\r
+               code to find the space with the\r
+               fewest number of locks.\r
+       */\r
+       synchronized int deadlockCount(int bail) {\r
+\r
+               int count = 0;\r
+\r
+               for (Iterator it = groups.values().iterator(); it.hasNext(); ) {\r
+                       HashMap group = (HashMap) it.next();\r
+                       for (Iterator locks = group.keySet().iterator(); locks.hasNext(); ) {\r
+                                       Lock lock = (Lock) locks.next();\r
+                                       count += lock.getCount();\r
+                                       if (count > bail)\r
+                                               return count;\r
+                       }\r
+               }\r
+               return count;\r
+\r
+       }\r
+}\r
+\r
+/**\r
+       An Enumeration that returns the the Lockables\r
+       in a group.\r
+*/\r
+\r
+class LockList implements Enumeration {\r
+\r
+       private Enumeration lockGroup;\r
+\r
+       LockList(Enumeration lockGroup) {\r
+               this.lockGroup = lockGroup;\r
+       }\r
+\r
+       public boolean hasMoreElements() {\r
+               return lockGroup.hasMoreElements();\r
+       }\r
+\r
+       public Object nextElement() {\r
+               return ((Lock) lockGroup.nextElement()).getLockable();\r
+       }\r
+}\r