001: package com.quadcap.sql.lock;
002:
003: /* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
004: *
005: * This software is distributed under the Quadcap Free Software License.
006: * This software may be used or modified for any purpose, personal or
007: * commercial. Open Source redistributions are permitted. Commercial
008: * redistribution of larger works derived from, or works which bundle
009: * this software requires a "Commercial Redistribution License"; see
010: * http://www.quadcap.com/purchase.
011: *
012: * Redistributions qualify as "Open Source" under one of the following terms:
013: *
014: * Redistributions are made at no charge beyond the reasonable cost of
015: * materials and delivery.
016: *
017: * Redistributions are accompanied by a copy of the Source Code or by an
018: * irrevocable offer to provide a copy of the Source Code for up to three
019: * years at the cost of materials and delivery. Such redistributions
020: * must allow further use, modification, and redistribution of the Source
021: * Code under substantially the same terms as this license.
022: *
023: * Redistributions of source code must retain the copyright notices as they
024: * appear in each source code file, these license terms, and the
025: * disclaimer/limitation of liability set forth as paragraph 6 below.
026: *
027: * Redistributions in binary form must reproduce this Copyright Notice,
028: * these license terms, and the disclaimer/limitation of liability set
029: * forth as paragraph 6 below, in the documentation and/or other materials
030: * provided with the distribution.
031: *
032: * The Software is provided on an "AS IS" basis. No warranty is
033: * provided that the Software is free of defects, or fit for a
034: * particular purpose.
035: *
036: * Limitation of Liability. Quadcap Software shall not be liable
037: * for any damages suffered by the Licensee or any third party resulting
038: * from use of the Software.
039: */
040:
041: import java.util.ArrayList;
042: import java.util.HashMap;
043: import java.util.Iterator;
044:
045: import java.util.Comparator;
046:
047: import java.sql.SQLException;
048:
049: import javax.concurrent.Sync;
050:
051: import com.quadcap.util.Debug;
052: import com.quadcap.util.Util;
053:
054: import com.quadcap.util.ConfigNumber;
055:
056: /**
057: * Manage a hierarchical set of read/write/intention locks.
058: *
059: * @author Stan Bailes
060: */
061: public class LockManager {
062: //#ifdef TRACE
063: /*{com.quadcap.qed.Trace-vars.xml-1064}
064: * <config-var>
065: * <config-name>qed.trace.locks</config-name>
066: * <config-dflt>0</config-dflt>
067: * <config-desc>
068: * <pre>
069: * bit 0: verbose lock tracing
070: * bit 1: lock tracing
071: * bit 2: really verbose lock tracing
072: * bit 3: transactions
073: * bit 4: dump locktable every 30 seconds
074: * </pre>
075: * </config-desc>
076: * </config-var>
077: */
078: protected static final ConfigNumber trace = ConfigNumber.find(
079: "qed.trace.locks", "0");
080: //#endif
081:
082: protected static final ConfigNumber lockTimeout = ConfigNumber
083: .find("qed.lock.Timeout", "60");
084:
085: private long transCount = 1;
086:
087: final Object locksLock = new Object();
088:
089: /** Pool of lock objects */
090: LockPool locks = new LockPool();
091:
092: /** Pool of 'held' locks */
093: HeldLockPool held = new HeldLockPool();
094:
095: /** Temp held lock object used for searching */
096: HeldLock tmpHeld = null;
097:
098: /** Pool of transaction objects. */
099: TransactionPool transactions = new TransactionPool();
100:
101: /** held locks sorted by transaction */
102: SortedArray byTrans;
103: SortedArrayIterator byTransIter;
104:
105: /**
106: * Constructor
107: */
108: public LockManager() {
109: Comparator compare = new Comparator() {
110: public int compare(Object a, Object b) {
111: return HeldLock.compare((HeldLock) a, (HeldLock) b);
112: }
113: };
114: byTrans = new SortedArray(compare);
115: byTransIter = new SortedArrayIterator(byTrans);
116: tmpHeld = held.get(null, null, LockMode.IX);
117: final LockManager lockMgr = this ;
118:
119: //#ifdef DEBUG
120: if (trace.bit(4)) {
121: Thread t = new Thread() {
122: public void run() {
123: setName("LockManager Debug");
124: Debug.println("Lock dump thread running");
125: while (lockMgr != null) {
126: try {
127: Thread.sleep(10000);
128: synchronized (lockMgr.locksLock) {
129: if (lockMgr.byTrans.size() > 0) {
130: Debug.println("DUMP: "
131: + lockMgr.toString());
132: }
133: }
134: } catch (Throwable e) {
135: Debug.print(e);
136: }
137: }
138: }
139: };
140: t.setDaemon(true);
141: t.start();
142: }
143: //#endif
144: }
145:
146: /**
147: * Allocate a new Transaction. All lock operations are performed
148: * on behalf of a Transaction.
149: */
150: public final Transaction getTransaction(long transId) {
151: synchronized (locksLock) {
152: Transaction t = (Transaction) transactions.get(transId);
153: //#ifdef DEBUG
154: if (trace.bit(3)) {
155: Debug.println("getTransaction() = " + t);
156: if (trace.bit(2)) {
157: Debug.println("this = " + this );
158: }
159: }
160: //#endif
161: return t;
162: }
163: }
164:
165: /**
166: * Find an existing transaction. Return null if the specified
167: * transaction doesn't exist.
168: */
169: public Transaction findTransaction(long transId) {
170: return transactions.find(transId);
171: }
172:
173: /**
174: * End a transaction, releasing all of its locks
175: */
176: public void releaseTransaction(Transaction t) {
177: //#ifdef DEBUG
178: if (trace.bit(3)) {
179: Debug.println("releaseTransaction(" + t + ")");
180: if (trace.bit(2)) {
181: Debug.println("this = " + this );
182: }
183: }
184: //#endif
185: synchronized (locksLock) {
186: try {
187: // for each lock held by the transaction
188: Iterator tlocks = locksForTransaction(t);
189: //#ifdef TRACE
190: if (trace.bit(1)) {
191: Debug.println("releaseTransaction(" + t + ") [1]");
192: }
193: //#endif
194: //Debug.println("releaseTransaction(" + t + "), held locks = " + byTrans);
195: while (tlocks.hasNext()) {
196: HeldLock h = (HeldLock) tlocks.next();
197: boolean saveMe = false;
198: if (h.trans.getTransactionId() < t
199: .getTransactionId())
200: continue;
201: if (h.trans.getTransactionId() > t
202: .getTransactionId())
203: break;
204: //Debug.println(" Releasing: " + h);
205: Lock lock = h.lock;
206: int mode = h.mode;
207: tlocks.remove();
208: if (h.mode == LockMode.NL)
209: continue;
210: //#ifdef TRACE
211: if (trace.bit(1)) {
212: Debug.println("[release] lock(" + t + ", "
213: + lock + " in "
214: + LockMode.toString(mode));
215: }
216: //#endif
217: lock.decrHeldCount(mode);
218: Transaction w = lock.headWaitQueue();
219: while (w != null && w.getWaitMode() == LockMode.NL) {
220: lock.popWaitQueue();
221: w = lock.headWaitQueue();
222: }
223: if (w != null && !w.equals(t)) {
224: if (lock.couldLock(w.getWaitMode())) {
225: lock.popWaitQueue();
226: HeldLock wl = w.getWait();
227: //#ifdef TRACE
228: if (trace.bit(1)) {
229: Debug.println("" + t + " releasing "
230: + lock + ", waking " + wl);
231: }
232: //#endif
233:
234: wl.mode = wl.waitMode;
235: acquireLock(wl);
236: w.clearWait();
237: try {
238: Sync c = w.getSync();
239: c.release();
240: } catch (InterruptedException ex) {
241: }
242: }
243: }
244: held.release(h);
245: }
246: } finally {
247: transactions.release(t);
248: }
249: //#ifdef TRACE
250: if (trace.bit(1)) {
251: Debug.println("releaseTransaction(" + t + ") done");
252: if (trace.bit(2)) {
253: Debug.println("this = " + this );
254: }
255: }
256: //#endif
257: }
258: }
259:
260: /**
261: * Return an iterator over all active transactions
262: */
263: public Iterator transactions() {
264: return transactions.iterator();
265: }
266:
267: /**
268: * Return an Iterator over the held locks positioned at the first
269: * lock owned by transaction 't'.
270: */
271: final Iterator locksForTransaction(Transaction t) {
272: tmpHeld.trans = t;
273: tmpHeld.lock = null;
274: byTransIter.position(tmpHeld);
275: return byTransIter;
276: }
277:
278: final HeldLock getLockForTransaction(Transaction t, Lock lock) {
279: tmpHeld.trans = t;
280: tmpHeld.lock = lock;
281: tmpHeld.mode = LockMode.NL;
282: byTransIter.position(tmpHeld);
283: while (byTransIter.hasNext()) {
284: HeldLock h = (HeldLock) byTransIter.next();
285: if (t.getTransactionId() > h.trans.getTransactionId()) {
286: return null;
287: }
288: if (t.getTransactionId() == h.trans.getTransactionId()
289: && lock.hashCode() == h.lock.hashCode()) {
290: return h;
291: }
292: }
293: return null;
294: }
295:
296: /**
297: * Instantiate the specified lock on behalf of transaction 't',
298: * which may or may not already have this lock.
299: */
300: public final Lock getLock(Transaction t, Lock parent, String name,
301: int mode) throws SQLException {
302: HeldLock h = null;
303: boolean newLock = false;
304: Sync s = null;
305: //#ifdef TRACE
306: if (trace.bit(1)) {
307: Debug.println("---- getLock(" + t + ", " + parent + ", "
308: + name + ", " + LockMode.toString(mode) + ")");
309: }
310: //#endif
311: try {
312: synchronized (locksLock) {
313: Lock lock = locks.get(parent, name);
314: checkParentsLockMode(t, lock, mode);
315:
316: h = getLockForTransaction(t, lock);
317: if (h != null) {
318: //Debug.println(" holding: " + h);
319: // 't' already has 'lock'; check the mode.
320: if (LockMode.implies(h.mode, mode)) {
321: //Debug.println(" Already have: " + h);
322: return h.lock;
323: }
324: if (LockMode.implies(mode, h.mode)) {
325: if (lock.couldPromote(h.mode, mode)) {
326: promoteLock(h, mode);
327: //Debug.println(" Promoted: " + h);
328: return h.lock;
329: } else {
330: //Debug.println(" Can't promote: " + h + ": " + lock.dump());
331: }
332: } else {
333: throw new RuntimeException("Transaction " + t
334: + " has lock " + lock + " in mode "
335: + LockMode.toString(h.mode)
336: + ", trying to lock now in mode "
337: + LockMode.toString(mode));
338: }
339: } else {
340: newLock = true;
341: //Debug.println(" Don't have it yet");
342: // 't' does not have 'lock' in any way
343: h = held.get(t, lock, mode);
344: if (lock.couldLock(mode) && !lock.hasWaiters()) {
345: acquireLock(h);
346: //Debug.println(" Got lock: " + h);
347: return h.lock;
348: }
349: }
350: h.setWaitMode(mode);
351: try {
352: s = waitForLock(h);
353: } catch (InterruptedException ex) {
354: throw new SQLException(
355: "Interrupted trying to get lock: " + name);
356: }
357:
358: }
359: //#ifdef DEBUG
360: if (trace.bit(0)) {
361: Debug.println(" Waiting for: " + h);
362: if (trace.bit(2)) {
363: Debug.println(byTrans.toString());
364: }
365: }
366: //#endif
367: boolean got = false;
368: try {
369: got = s.attempt(lockTimeout.longValue() * 1000);
370: } catch (InterruptedException ex) {
371: }
372: if (!got) {
373: Debug.println("Lock timed out: " + name + " after "
374: + lockTimeout.longValue() + " seconds");
375: Debug.println("LockManager: " + this );
376: // try {
377: // got = s.attempt(lockTimeout.longValue() * 1000);
378: // } catch (InterruptedException ex) {}
379: if (!got) {
380: t.setWait(null);
381: if (newLock) {
382: held.release(h);
383: newLock = false;
384: }
385: throw new SQLException(
386: "Timed out trying to get lock: " + name
387: + " for [T:" + t + "]");
388: }
389: }
390: //#ifdef DEBUG
391: if (trace.bit(0)) {
392: Debug.println(" Wait done for: " + h);
393: }
394: //#endif
395: return h.lock;
396: } finally {
397: //#ifdef TRACE
398: if (trace.bit(1)) {
399: Debug.println("getLock(" + name + ") done");
400: }
401: //#endif
402: }
403: }
404:
405: /**
406: * We're in the critical section still
407: */
408: final Sync waitForLock(HeldLock h) throws InterruptedException {
409: final Transaction t = h.trans;
410: //#ifdef PARANOID
411: //- if (t.getWait() != null)
412: //- throw new RuntimeException("Transaction already waiting");
413: //#endif
414: h.lock.addWaitQueue(t);
415: t.setWait(h);
416: return t.getSync();
417: }
418:
419: /**
420: * Ensure that the lock's parent(s) are properly locked, to enable
421: * locking <code>lock</code> in the specified mode.
422: */
423: final void checkParentsLockMode(Transaction t, Lock lock, int mode) {
424: Lock p = lock.getParent();
425: if (p != null) {
426: HeldLock h = getLockForTransaction(t, p);
427: if (h == null) {
428: //#ifdef DEBUG
429: Debug.println("Held: " + byTrans + ", lock = " + lock);
430: Debug.println("LockManager: " + this );
431: //#endif
432: throw new RuntimeException("no lock on parent " + p);
433: }
434: if (!couldLockWithParentMode(h.mode, mode)) {
435: //#ifdef DEBUG
436: Debug.println("lock = " + lock + ", p = " + p
437: + ", h = " + h);
438: Debug.println("LockManager: " + this );
439: //#endif
440: throw new RuntimeException(
441: "parent not properly locked: " + h);
442: }
443: checkParentsLockMode(t, p, mode);
444: }
445: }
446:
447: final boolean couldLockWithParentMode(int pmode, int mode) {
448: switch (mode) {
449: case LockMode.S:
450: case LockMode.IS:
451: return pmode == LockMode.X || pmode == LockMode.S
452: || pmode == LockMode.IX || pmode == LockMode.IS;
453: case LockMode.X:
454: case LockMode.IX:
455: case LockMode.SIX:
456: return pmode == LockMode.X || pmode == LockMode.IX
457: || pmode == LockMode.SIX;
458: default:
459: throw new RuntimeException("bad lock mode: "
460: + LockMode.toString(mode));
461: }
462: }
463:
464: void promoteLock(HeldLock h, int mode) {
465: h.lock.decrHeldCount(h.mode);
466: h.lock.incrHeldCount(mode);
467: h.mode = mode;
468: }
469:
470: void acquireLock(HeldLock h) {
471: h.lock.incrHeldCount(h.mode);
472: byTrans.add(h);
473: }
474:
475: //#ifdef DEBUG
476: public String toString() {
477: synchronized (locksLock) {
478: StringBuffer sb = new StringBuffer();
479: sb.append("\nTransactions:\n------------\n");
480: sb.append(transactions);
481: sb.append("\nHeld:\n----\n");
482: sb.append(byTrans);
483: sb.append("\nLocks:\n-----\n");
484: sb.append(locks);
485: sb.append("\n");
486: return sb.toString();
487: }
488: }
489: //#endif
490: }
|