001: /*
002:
003: Derby - Class org.apache.derby.impl.services.locks.SinglePool
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.monitor.Monitor;
025:
026: import org.apache.derby.iapi.services.locks.LockFactory;
027: import org.apache.derby.iapi.services.locks.C_LockFactory;
028: import org.apache.derby.iapi.services.locks.Lockable;
029: import org.apache.derby.iapi.services.locks.Latch;
030: import org.apache.derby.iapi.services.locks.Limit;
031:
032: import org.apache.derby.iapi.error.StandardException;
033:
034: import org.apache.derby.iapi.services.property.PropertyUtil;
035: import org.apache.derby.iapi.services.daemon.Serviceable;
036:
037: import org.apache.derby.iapi.services.sanity.SanityManager;
038: import org.apache.derby.iapi.util.Matchable;
039: import org.apache.derby.iapi.reference.Property;
040:
041: import java.util.Hashtable;
042: import java.util.Properties;
043: import java.io.Serializable;
044: import java.util.Dictionary;
045: import java.util.Enumeration;
046:
047: // debugging
048: import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
049:
050: /**
051: An implementation of LockFactory that uses a single pool
052: for the locks, i.e. all lock requests go through a single
053: point of synchronisation.
054: <p>
055: The default concrete class "SinglePool.java", prints nothing and thus
056: incurs no overhead associated with the code to dump lock information. An
057: alternate concrete class "LockDebug/TracingSinglePool.java", attempts to
058: output only lock information that "makes sense" to a user - for instance it
059: doesn't print latch locks.
060:
061: <BR>
062: MT - Mutable - Container Object : Thread Aware
063: */
064:
065: public class SinglePool extends Hashtable implements LockFactory {
066: /**
067: The complete set of locks in the system
068:
069: <BR>
070: MT - immutable - content dynamic : LockSet is ThreadSafe
071: */
072: protected final LockSet lockTable;
073:
074: /**
075: This is now in this object, it now extends Hashtable.
076: A hash table of all compatability spaces. Key is the object
077: representing the compatability space, value is a LockSpace object.
078: Addition and removal from the Hashtable is performed under the
079: Hashtable's monitor. This requires holding this monitor while making
080: calls to the thread safe methods of LockSpace. This is to ensure
081: that it is guaranteed that a LockSpace is only removed when it is
082: empty and no-one is in the process of adding to it. No deadlocks are
083: possible because the spaces reference is not visible outside this
084: class and the LockSpace class does not call back into this class.
085:
086: <BR>
087: MT - immutable - content dynamic : Java synchronized(spaces)
088:
089: This class creates a LockSet and LockSpaces, both classes are thread
090: safe.
091:
092: */
093:
094: /**
095: True if all deadlocks errors should be logged.
096: */
097: int deadlockMonitor;
098:
099: public SinglePool() {
100: lockTable = new LockSet(this );
101: }
102:
103: /*
104: ** Methods of LockFactory
105: */
106:
107: /**
108: Latch a specific object with a timeout.
109:
110: <BR>
111: MT - thread safe
112:
113: @exception StandardException Standard Cloudscape error policy
114:
115: @see LockFactory#latchObject
116: */
117: public boolean latchObject(Object compatabilitySpace, Lockable ref,
118: Object qualifier, int timeout) throws StandardException {
119:
120: Lock latch = lockTable.lockObject(compatabilitySpace, ref,
121: qualifier, timeout, (Latch) null);
122:
123: if (SanityManager.DEBUG) {
124: if (latch == null)
125: SanityManager.ASSERT(timeout == C_LockFactory.NO_WAIT,
126: "timeout not NO_WAIT");
127: }
128: return latch != null;
129: }
130:
131: /**
132: Unlatch an object.
133:
134: <BR>
135: MT - thread safe
136:
137: @see LockFactory#unlatch
138: */
139: public void unlatch(Latch heldLatch) {
140: lockTable.unlock(heldLatch, 1);
141: }
142:
143: /**
144: Lock a specific object with a timeout.
145:
146: <BR>
147: MT - thread safe
148:
149: @exception StandardException Standard Cloudscape error policy
150:
151: @see LockFactory#lockObject
152: */
153: protected Lock lockAnObject(Object compatabilitySpace,
154: Object group, Lockable ref, Object qualifier, int timeout,
155: Latch heldLatch) throws StandardException {
156: if (SanityManager.DEBUG) {
157: if (SanityManager.DEBUG_ON(Constants.LOCK_TRACE)) {
158:
159: D_LockControl.debugLock("Lock Request before Grant: ",
160: compatabilitySpace, group, ref, qualifier,
161: timeout);
162:
163: if (SanityManager.DEBUG_ON(Constants.LOCK_STACK_TRACE)) {
164: // The following will print the stack trace of the lock
165: // request to the log.
166: Throwable t = new Throwable();
167: java.io.PrintWriter istream = SanityManager
168: .GET_DEBUG_STREAM();
169:
170: istream.println("Stack trace of lock request:");
171: t.printStackTrace(istream);
172: }
173: }
174: }
175:
176: Lock lock = lockTable.lockObject(compatabilitySpace, ref,
177: qualifier, timeout, heldLatch);
178:
179: // See if NO_WAIT was passed in and the lock could not be granted.
180: if (lock == null) {
181: if (SanityManager.DEBUG) {
182: SanityManager.ASSERT(timeout == C_LockFactory.NO_WAIT,
183: "timeout not NO_WAIT");
184: }
185: return null;
186: }
187:
188: if (SanityManager.DEBUG) {
189: if (SanityManager.DEBUG_ON(Constants.LOCK_TRACE)) {
190: D_LockControl.debugLock("Lock Request Granted: ",
191: compatabilitySpace, group, ref, qualifier,
192: timeout);
193: }
194: }
195:
196: // find the space and atomically add lock to required group
197: synchronized (this ) {
198:
199: LockSpace ls = (LockSpace) get(compatabilitySpace);
200: if (ls == null) {
201: ls = new LockSpace(this , compatabilitySpace);
202: put(compatabilitySpace, ls);
203: }
204:
205: // we hold the spaces monitor while adding the lock to close
206: // the window between finding the LockSpace and adding a lock
207: // to it, thus ensuring the LockSpace is not removed from the
208: // spaces Hashtable underneath us.
209:
210: ls.addLock(group, lock);
211: }
212:
213: return lock;
214: }
215:
216: /**
217: Lock a specific object
218:
219: <BR>
220: MT - thread safe
221:
222: @exception StandardException Standard Cloudscape error policy
223:
224: @see LockFactory#lockObject
225: */
226: public boolean lockObject(Object compatabilitySpace, Object group,
227: Lockable ref, Object qualifier, int timeout)
228: throws StandardException {
229:
230: return lockAnObject(compatabilitySpace, group, ref, qualifier,
231: timeout, (Latch) null) != null;
232: }
233:
234: /**
235: Lock a specific object while holding a latch
236:
237: <BR>
238: MT - thread safe
239:
240: @exception StandardException Standard Cloudscape error policy
241:
242: @see LockFactory#lockObject
243: */
244: public boolean lockObject(Object group, Lockable ref,
245: Object qualifier, int timeout, Latch latch)
246: throws StandardException {
247:
248: if (SanityManager.DEBUG) {
249: if (timeout == C_LockFactory.NO_WAIT)
250: SanityManager
251: .THROWASSERT("no wait lock requested in lockObject() with latch");
252: }
253:
254: Lock lock = lockAnObject(latch.getCompatabilitySpace(), group,
255: ref, qualifier, timeout, latch);
256: return lock instanceof ActiveLock;
257: }
258:
259: /**
260: Unlock a specific object
261:
262: <BR>
263: MT - thread safe
264:
265: @see LockFactory#unlock
266: */
267:
268: public int unlock(Object compatabilitySpace, Object group,
269: Lockable ref, Object qualifier) {
270: if (SanityManager.DEBUG) {
271: if (SanityManager.DEBUG_ON(Constants.LOCK_TRACE)) {
272: D_LockControl.debugLock("Lock Unlock: ",
273: compatabilitySpace, group, ref, qualifier, -1);
274: }
275: }
276:
277: LockSpace ls = (LockSpace) get(compatabilitySpace);
278: if (ls == null)
279: return 0;
280:
281: int count = ls
282: .unlockReference(lockTable, ref, qualifier, group);
283:
284: if (SanityManager.DEBUG) {
285: SanityManager.ASSERT((count == 0) || (count == 1),
286: "count = " + count);
287: }
288:
289: return count;
290: }
291:
292: /**
293: Unlock a group of objects.
294:
295: <BR>
296: MT - thread safe
297:
298: @param group handle of group that objects were locked with.
299: If group is null then this call is equivilent to unlockAll().
300:
301: @see LockFactory#unlockGroup
302: */
303: public void unlockGroup(Object compatabilitySpace, Object group) {
304:
305: if (SanityManager.DEBUG) {
306: if (SanityManager.DEBUG_ON(Constants.LOCK_TRACE)) {
307: D_LockControl.debugLock("Lock Unlock Group: ",
308: compatabilitySpace, group);
309: }
310: }
311:
312: LockSpace ls = (LockSpace) get(compatabilitySpace);
313: if (ls == null)
314: return;
315:
316: ls.unlockGroup(lockTable, group);
317: }
318:
319: public void unlockGroup(Object compatabilitySpace, Object group,
320: Matchable key) {
321:
322: if (SanityManager.DEBUG) {
323: if (SanityManager.DEBUG_ON(Constants.LOCK_TRACE)) {
324: D_LockControl.debugLock("Lock Unlock Group: ",
325: compatabilitySpace, group);
326: }
327: }
328:
329: LockSpace ls = (LockSpace) get(compatabilitySpace);
330: if (ls == null)
331: return;
332:
333: ls.unlockGroup(lockTable, group, key);
334:
335: }
336:
337: /**
338: Transfer a set of locks from one group to another.
339:
340: <BR>
341: MT - thread safe
342:
343: @see LockFactory#transfer
344: */
345: public void transfer(Object compatabilitySpace, Object oldGroup,
346: Object newGroup) {
347:
348: if (SanityManager.DEBUG) {
349: if (SanityManager.DEBUG_ON(Constants.LOCK_TRACE)) {
350: StringBuffer sb = new StringBuffer("Lock Transfer:");
351:
352: D_LockControl.debugAppendObject(sb,
353: " CompatabilitySpace=", compatabilitySpace);
354: D_LockControl.debugAppendObject(sb, " Old Group=",
355: oldGroup);
356: D_LockControl.debugAppendObject(sb, " New Group=",
357: newGroup);
358:
359: D_LockControl.debugAddThreadInfo(sb);
360:
361: SanityManager
362: .DEBUG(Constants.LOCK_TRACE, sb.toString());
363: }
364: }
365:
366: LockSpace ls = (LockSpace) get(compatabilitySpace);
367: if (ls == null)
368: return;
369:
370: // there is a window where someone could remove the LockSpace from the
371: // spaces Hashtable, since we do not hold the spaces' monitor. This is
372: // Ok as the LockSpace will have no locks and this method
373: // will correctly do nothing.
374:
375: ls.transfer(oldGroup, newGroup);
376: }
377:
378: /**
379: Returns true if locks by anyone are blocking anyone else
380: */
381: public boolean anyoneBlocked() {
382: return lockTable.anyoneBlocked();
383: }
384:
385: /**
386: Return true if locks are held in this group and this space.
387:
388: <BR>
389: MT - thread safe
390:
391: @param group handle of group that objects were locked with.
392:
393: @see LockFactory#areLocksHeld
394: */
395: public boolean areLocksHeld(Object compatabilitySpace, Object group) {
396:
397: LockSpace ls = (LockSpace) get(compatabilitySpace);
398: if (ls == null)
399: return false;
400:
401: // there is a window where someone could remove the LockSpace from the
402: // spaces Hashtable, since we do not hold the spaces' monitor. This is
403: // Ok as the LockSpace will have no locks and this method will
404: // correctly return false.
405:
406: return ls.areLocksHeld(group);
407: }
408:
409: /**
410: Return true if locks are held in this space
411:
412: <BR>
413: MT - thread safe
414:
415: @see LockFactory#areLocksHeld
416: */
417: public boolean areLocksHeld(Object compatabilitySpace) {
418: LockSpace ls = (LockSpace) get(compatabilitySpace);
419: if (ls == null)
420: return false;
421: return !ls.isEmpty();
422: }
423:
424: public boolean zeroDurationlockObject(Object compatabilitySpace,
425: Lockable ref, Object qualifier, int timeout)
426: throws StandardException {
427:
428: if (SanityManager.DEBUG) {
429: if (SanityManager.DEBUG_ON(Constants.LOCK_TRACE)) {
430:
431: D_LockControl.debugLock(
432: "Zero Duration Lock Request before Grant: ",
433: compatabilitySpace, (Object) null, ref,
434: qualifier, timeout);
435:
436: if (SanityManager.DEBUG_ON(Constants.LOCK_STACK_TRACE)) {
437: // The following will print the stack trace of the lock
438: // request to the log.
439: Throwable t = new Throwable();
440: java.io.PrintWriter istream = SanityManager
441: .GET_DEBUG_STREAM();
442:
443: istream.println("Stack trace of lock request:");
444: t.printStackTrace(istream);
445: }
446: }
447: }
448:
449: // Very fast zeroDurationlockObject() for unlocked objects.
450: // If no entry exists in the lock manager for this reference
451: // then it must be unlocked.
452: // If the object is locked then we perform a grantable
453: // check, skipping over any waiters.
454: // If the caller wants to wait and the lock cannot
455: // be granted then we do the slow join the queue and
456: // release the lock method.
457: synchronized (lockTable) {
458:
459: Control control = (Control) lockTable.get(ref);
460: if (control == null) {
461: return true;
462: }
463:
464: // If we are grantable, ignoring waiting locks then
465: // we can also grant this request now, as skipping
466: // over the waiters won't block them as we release
467: // the lock rightway.
468: if (control
469: .isGrantable(true, compatabilitySpace, qualifier))
470: return true;
471:
472: // can't be granted and are not willing to wait.
473: if (timeout == C_LockFactory.NO_WAIT)
474: return false;
475: }
476:
477: Lock lock = lockTable.lockObject(compatabilitySpace, ref,
478: qualifier, timeout, (Latch) null);
479:
480: if (SanityManager.DEBUG) {
481: if (SanityManager.DEBUG_ON(Constants.LOCK_TRACE)) {
482: D_LockControl.debugLock("Zero Lock Request Granted: ",
483: compatabilitySpace, (Object) null, ref,
484: qualifier, timeout);
485: }
486: }
487:
488: // and simply unlock it once
489: lockTable.unlock(lock, 1);
490:
491: return true;
492: }
493:
494: public boolean isLockHeld(Object compatabilitySpace, Object group,
495: Lockable ref, Object qualifier) {
496:
497: LockSpace ls = (LockSpace) get(compatabilitySpace);
498: if (ls == null)
499: return false;
500:
501: return ls.isLockHeld(group, ref, qualifier);
502: }
503:
504: public synchronized void setLimit(Object compatabilitySpace,
505: Object group, int limit, Limit callback) {
506:
507: LockSpace ls = (LockSpace) get(compatabilitySpace);
508: if (ls == null) {
509: ls = new LockSpace(this , compatabilitySpace);
510: put(compatabilitySpace, ls);
511: }
512:
513: ls.setLimit(group, limit, callback);
514:
515: }
516:
517: /**
518: Clear a limit set by setLimit.
519: */
520: public void clearLimit(Object compatabilitySpace, Object group) {
521: LockSpace ls = (LockSpace) get(compatabilitySpace);
522: if (ls == null)
523: return;
524:
525: ls.clearLimit(group);
526: }
527:
528: //EXCLUDE-START-lockdiag-
529:
530: /**
531: Routines to support lock diagnostics VTIs for the benefit of VirtualLockTable
532: */
533: /* package */
534: public Enumeration makeVirtualLockTable() {
535: // make a shallow copy of the locktable.
536: LockTableVTI myclone = new LockTableVTI(lockTable
537: .shallowClone());
538:
539: return myclone;
540: }
541:
542: //EXCLUDE-END-lockdiag-
543:
544: /*
545: ** Non-public methods
546: */
547:
548: //EXCLUDE-START-debug-
549: public String toDebugString() {
550: return (lockTable.toDebugString());
551: }
552:
553: //EXCLUDE-END-debug-
554:
555: /*
556: ** Methods of PropertySetCallback
557: */
558:
559: public void init(boolean dbOnly, Dictionary p) {
560:
561: getAndApply(dbOnly, p, Property.DEADLOCK_TIMEOUT);
562: getAndApply(dbOnly, p, Property.LOCKWAIT_TIMEOUT);
563: getAndApply(dbOnly, p, Property.DEADLOCK_MONITOR);
564: //EXCLUDE-START-lockdiag-
565: getAndApply(dbOnly, p, Property.DEADLOCK_TRACE);
566: //EXCLUDE-END-lockdiag-
567: }
568:
569: private void getAndApply(boolean dbOnly, Dictionary p, String key) {
570:
571: try {
572:
573: Serializable value = (String) PropertyUtil
574: .getPropertyFromSet(dbOnly, p, key);
575: if (value != null) {
576: validate(key, value, p);
577: apply(key, value, p);
578: }
579: } catch (StandardException se) {
580: // just ignore value at bootup.
581: }
582: }
583:
584: public boolean validate(String key, Serializable value, Dictionary p)
585: throws StandardException {
586:
587: if (!key.startsWith(Property.LOCKS_INTRO))
588: return false;
589:
590: if (value != null) {
591:
592: if (key.equals(Property.DEADLOCK_TIMEOUT))
593: getWaitValue((String) value,
594: Property.DEADLOCK_TIMEOUT_DEFAULT);
595: else if (key.equals(Property.LOCKWAIT_TIMEOUT))
596: getWaitValue((String) value,
597: Property.WAIT_TIMEOUT_DEFAULT);
598: else if (key.equals(Property.DEADLOCK_MONITOR))
599: PropertyUtil.booleanProperty(Property.DEADLOCK_MONITOR,
600: value, false);
601: else if (key.equals(Property.DEADLOCK_TRACE))
602: PropertyUtil.booleanProperty(Property.DEADLOCK_TRACE,
603: value, false);
604: }
605:
606: return true;
607: }
608:
609: public Serviceable apply(String key, Serializable value,
610: Dictionary p) throws StandardException {
611:
612: if (value == null) {
613: // a delete, fill in the new value
614: value = PropertyUtil.getPropertyFromSet(p, key);
615: }
616:
617: String svalue = (String) value;
618:
619: if (key.equals(Property.DEADLOCK_TIMEOUT))
620: lockTable.deadlockTimeout = getWaitValue(svalue,
621: Property.DEADLOCK_TIMEOUT_DEFAULT);
622: else if (key.equals(Property.LOCKWAIT_TIMEOUT))
623: lockTable.waitTimeout = getWaitValue(svalue,
624: Property.WAIT_TIMEOUT_DEFAULT);
625: else if (key.equals(Property.DEADLOCK_MONITOR)) {
626: deadlockMonitor = PropertyUtil.booleanProperty(
627: Property.DEADLOCK_MONITOR, svalue, false) ? StandardException.REPORT_ALWAYS
628: : StandardException.REPORT_DEFAULT;
629: }
630: //EXCLUDE-START-lockdiag-
631: else if (key.equals(Property.DEADLOCK_TRACE))
632: lockTable.setDeadlockTrace(PropertyUtil.booleanProperty(
633: Property.DEADLOCK_TRACE, svalue, false));
634: //EXCLUDE-END-lockdiag-
635:
636: return null;
637: }
638:
639: public Serializable map(String key, Serializable value, Dictionary p) {
640: return null;
641: }
642:
643: /*
644: ** Property related methods
645: */
646:
647: private static int getWaitValue(String value, int defaultValue) {
648:
649: // properties are defined in seconds
650: int wait = PropertyUtil.handleInt(value, Integer.MIN_VALUE,
651: Integer.MAX_VALUE / 1000, defaultValue);
652:
653: if (wait < 0)
654: wait = C_LockFactory.WAIT_FOREVER;
655: else
656: // convert to milliseconds
657: wait *= 1000;
658:
659: return wait;
660: }
661: }
|