001: /*
002:
003: Derby - Class org.apache.derby.impl.services.locks.LockSpace
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.services.locks;
023:
024: import org.apache.derby.iapi.services.locks.Lockable;
025: import org.apache.derby.iapi.services.locks.Limit;
026:
027: import org.apache.derby.iapi.util.Matchable;
028: import org.apache.derby.iapi.services.sanity.SanityManager;
029: import org.apache.derby.iapi.error.StandardException;
030:
031: import java.util.Hashtable;
032: import java.util.Enumeration;
033: import java.util.Dictionary;
034: import java.util.HashMap;
035: import java.util.Iterator;
036:
037: /**
038:
039: A LockSpace represents the complete set of locks held within
040: a single compatability space, broken into groups of locks.
041:
042: A LockSpace is a hashtable keyed by the group reference,
043: the data for each key is a Hashtable of Lock's.
044:
045: */
046: class LockSpace extends Hashtable {
047:
048: private final Object compatSpace;
049: // the object I live in
050: private final Dictionary holder;
051:
052: private HashMap spareGroups[] = new HashMap[3];
053:
054: // the Limit info.
055: private Object callbackGroup;
056: private int limit;
057: private int nextLimitCall;
058: private Limit callback;
059:
060: LockSpace(Dictionary holder, Object compatSpace) {
061: super ();
062: this .compatSpace = compatSpace;
063: this .holder = holder;
064: }
065:
066: /**
067: Add a lock to a group.
068: */
069: protected synchronized void addLock(Object group, Lock lock)
070: throws StandardException {
071:
072: Lock lockInGroup = null;
073:
074: HashMap dl = (HashMap) get(group);
075: if (dl == null) {
076: dl = getGroupMap(group);
077: } else if (lock.getCount() != 1) {
078: lockInGroup = (Lock) dl.get(lock);
079: }
080:
081: if (lockInGroup == null) {
082: lockInGroup = lock.copy();
083: dl.put(lockInGroup, lockInGroup);
084: }
085: lockInGroup.count++;
086:
087: if (inLimit)
088: return;
089:
090: if (!group.equals(callbackGroup))
091: return;
092:
093: int groupSize = dl.size();
094:
095: if (groupSize > nextLimitCall) {
096:
097: inLimit = true;
098: callback.reached(compatSpace, group, limit, new LockList(
099: java.util.Collections.enumeration(dl.keySet())),
100: groupSize);
101: inLimit = false;
102:
103: // see when the next callback should occur, if the callback
104: // failed to release a sufficent amount of locks then
105: // delay until another "limit" locks are obtained.
106: int newGroupSize = dl.size();
107: if (newGroupSize < (limit / 2))
108: nextLimitCall = limit;
109: else if (newGroupSize < (nextLimitCall / 2))
110: nextLimitCall -= limit;
111: else
112: nextLimitCall += limit;
113:
114: }
115: }
116:
117: private boolean inLimit;
118:
119: /**
120: Unlock all the locks in a group and then remove the group.
121: */
122:
123: synchronized void unlockGroup(LockSet lset, Object group) {
124: HashMap dl = (HashMap) remove(group);
125: if (dl == null)
126: return;
127:
128: for (Iterator list = dl.keySet().iterator(); list.hasNext();) {
129: lset.unlock((Lock) list.next(), 0);
130: }
131:
132: if ((callbackGroup == null) && isEmpty())
133: holder.remove(compatSpace);
134: else if (group.equals(callbackGroup))
135: nextLimitCall = limit;
136:
137: saveGroup(dl);
138: }
139:
140: private HashMap getGroupMap(Object group) {
141: HashMap[] sg = spareGroups;
142: HashMap dl = null;
143: for (int i = 0; i < 3; i++) {
144: dl = sg[i];
145: if (dl != null) {
146: sg[i] = null;
147: break;
148: }
149: }
150:
151: if (dl == null)
152: dl = new HashMap(5, 0.8f);
153:
154: put(group, dl);
155: return dl;
156: }
157:
158: private void saveGroup(HashMap dl) {
159: HashMap[] sg = spareGroups;
160: for (int i = 0; i < 3; i++) {
161: if (sg[i] == null) {
162: sg[i] = dl;
163: dl.clear();
164: break;
165: }
166: }
167: }
168:
169: /**
170: Unlock all locks in the group that match the key
171: */
172: synchronized void unlockGroup(LockSet lset, Object group,
173: Matchable key) {
174: HashMap dl = (HashMap) get(group);
175: if (dl == null)
176: return; // no group at all
177:
178: boolean allUnlocked = true;
179: for (Iterator e = dl.keySet().iterator(); e.hasNext();) {
180:
181: Lock lock = (Lock) e.next();
182: if (!key.match(lock.getLockable())) {
183: allUnlocked = false;
184: continue;
185: }
186: lset.unlock(lock, 0);
187: e.remove();
188: }
189:
190: if (allUnlocked) {
191: remove(group);
192: saveGroup(dl);
193: if ((callbackGroup == null) && isEmpty())
194: holder.remove(compatSpace);
195: else if (group.equals(callbackGroup))
196: nextLimitCall = limit;
197:
198: }
199: }
200:
201: synchronized void transfer(Object oldGroup, Object newGroup) {
202: HashMap from = (HashMap) get(oldGroup);
203: if (from == null)
204: return;
205:
206: HashMap to = (HashMap) get(newGroup);
207: if (to == null) {
208: // simple case
209: put(newGroup, from);
210: clearLimit(oldGroup);
211: remove(oldGroup);
212: return;
213: }
214:
215: if (to.size() < from.size()) {
216:
217: // place the contents of to into from
218: mergeGroups(to, from);
219:
220: Object oldTo = put(newGroup, from);
221: if (SanityManager.DEBUG) {
222: SanityManager.ASSERT(oldTo == to,
223: "inconsistent state in LockSpace");
224: }
225:
226: } else {
227: mergeGroups(from, to);
228: }
229:
230: clearLimit(oldGroup);
231: remove(oldGroup);
232: }
233:
234: private void mergeGroups(HashMap from, HashMap into) {
235:
236: for (Iterator e = from.keySet().iterator(); e.hasNext();) {
237:
238: Object lock = e.next();
239:
240: Object lockI = into.get(lock);
241:
242: if (lockI == null) {
243: // lock is only in from list
244: into.put(lock, lock);
245: } else {
246: // merge the locks
247: Lock fromL = (Lock) lock;
248: Lock intoL = (Lock) lockI;
249:
250: intoL.count += fromL.getCount();
251: }
252: }
253:
254: }
255:
256: synchronized int unlockReference(LockSet lset, Lockable ref,
257: Object qualifier, Object group) {
258:
259: // look for locks matching our reference and qualifier.
260: HashMap dl = (HashMap) get(group);
261: if (dl == null)
262: return 0;
263:
264: Lock lockInGroup;
265: synchronized (lset) {
266: Control control = lset.getControl(ref);
267: if (control == null)
268: return 0;
269:
270: Lock setLock = control.getLock(compatSpace, qualifier);
271: if (setLock == null)
272: return 0;
273:
274: lockInGroup = (Lock) dl.remove(setLock);
275: if (lockInGroup == null)
276: return 0;
277: setLock = null;
278:
279: lset.unlock(lockInGroup, 1);
280: }
281:
282: if (lockInGroup.getCount() == 1) {
283:
284: if (dl.isEmpty()) {
285: remove(group);
286: saveGroup(dl);
287: if ((callbackGroup == null) && isEmpty())
288: holder.remove(compatSpace);
289: else if (group.equals(callbackGroup))
290: nextLimitCall = limit;
291:
292: }
293:
294: return 1;
295: }
296:
297: // the lock item will be left in the group
298: lockInGroup.count--;
299: dl.put(lockInGroup, lockInGroup);
300: return 1;
301: }
302:
303: /**
304: Return true if locks are held in a group
305: */
306: synchronized boolean areLocksHeld(Object group) {
307: return (get(group) != null);
308: }
309:
310: synchronized boolean isLockHeld(Object group, Lockable ref,
311: Object qualifier) {
312:
313: // look for locks matching our reference and qualifier.
314: HashMap dl = (HashMap) get(group);
315: if (dl == null)
316: return false;
317:
318: Object heldLock = dl.get(new Lock(compatSpace, ref, qualifier));
319: return (heldLock != null);
320: }
321:
322: synchronized void setLimit(Object group, int limit, Limit callback) {
323: callbackGroup = group;
324: this .nextLimitCall = this .limit = limit;
325: this .callback = callback;
326: }
327:
328: /**
329: Clear a limit set by setLimit.
330: */
331: synchronized void clearLimit(Object group) {
332:
333: if (group.equals(callbackGroup)) {
334: callbackGroup = null;
335: nextLimitCall = limit = Integer.MAX_VALUE;
336: callback = null;
337:
338: if (isEmpty())
339: holder.remove(compatSpace);
340: }
341: }
342:
343: /**
344: Return a count of the number of locks
345: held by this space. The argument bail
346: indicates at which point the counting
347: should bail out and return the current
348: count. This routine will bail if the
349: count is greater than bail. Thus this
350: routine is intended to for deadlock
351: code to find the space with the
352: fewest number of locks.
353: */
354: synchronized int deadlockCount(int bail) {
355:
356: int count = 0;
357:
358: for (Enumeration groups = elements(); groups.hasMoreElements();) {
359: HashMap group = (HashMap) groups.nextElement();
360: for (Iterator locks = group.keySet().iterator(); locks
361: .hasNext();) {
362: Lock lock = (Lock) locks.next();
363: count += lock.getCount();
364: if (count > bail)
365: return count;
366: }
367: }
368: return count;
369:
370: }
371: }
372:
373: /**
374: An Enumeration that returns the the Lockables
375: in a group.
376: */
377:
378: class LockList implements Enumeration {
379:
380: private Enumeration lockGroup;
381:
382: LockList(Enumeration lockGroup) {
383: this .lockGroup = lockGroup;
384: }
385:
386: public boolean hasMoreElements() {
387: return lockGroup.hasMoreElements();
388: }
389:
390: public Object nextElement() {
391: return ((Lock) lockGroup.nextElement()).getLockable();
392: }
393: }
|