001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: LockManager.java,v 1.118.2.8 2008/02/06 12:52:30 cwl Exp $
007: */
008:
009: package com.sleepycat.je.txn;
010:
011: import java.util.Collection;
012: import java.util.HashMap;
013: import java.util.HashSet;
014: import java.util.Iterator;
015: import java.util.List;
016: import java.util.Map;
017: import java.util.Set;
018:
019: import com.sleepycat.je.DatabaseException;
020: import com.sleepycat.je.DeadlockException;
021: import com.sleepycat.je.LockStats;
022: import com.sleepycat.je.RunRecoveryException;
023: import com.sleepycat.je.StatsConfig;
024: import com.sleepycat.je.config.EnvironmentParams;
025: import com.sleepycat.je.dbi.DatabaseImpl;
026: import com.sleepycat.je.dbi.DbConfigManager;
027: import com.sleepycat.je.dbi.EnvConfigObserver;
028: import com.sleepycat.je.dbi.EnvironmentImpl;
029: import com.sleepycat.je.dbi.MemoryBudget;
030: import com.sleepycat.je.dbi.RangeRestartException;
031: import com.sleepycat.je.latch.Latch;
032: import com.sleepycat.je.latch.LatchStats;
033: import com.sleepycat.je.latch.LatchSupport;
034:
035: /**
036: * LockManager manages locks.
037: *
038: * Note that locks are counted as taking up part of the JE cache;
039: */
040: public abstract class LockManager implements EnvConfigObserver {
041:
042: /*
043: * The total memory cost for a lock is the Lock object, plus its entry and
044: * key in the lock hash table.
045: *
046: * The addition and removal of Lock objects, and the corresponding cost of
047: * their hashmap entry and key are tracked through the LockManager.
048: */
049: static final long TOTAL_LOCK_OVERHEAD = MemoryBudget.LOCK_OVERHEAD
050: + MemoryBudget.HASHMAP_ENTRY_OVERHEAD
051: + MemoryBudget.LONG_OVERHEAD;
052:
053: private static final long REMOVE_TOTAL_LOCK_OVERHEAD = 0 - TOTAL_LOCK_OVERHEAD;
054:
055: protected int nLockTables = 1;
056: protected Latch[] lockTableLatches;
057: private Map[] lockTables; // keyed by nodeId
058: private EnvironmentImpl envImpl;
059: private MemoryBudget memoryBudget;
060: //private Level traceLevel;
061:
062: private long nRequests; // stats: number of time a request was made
063: private long nWaits; // stats: number of time a request blocked
064:
065: private static RangeRestartException rangeRestartException = new RangeRestartException();
066: private static boolean lockTableDump = false;
067:
068: public LockManager(EnvironmentImpl envImpl)
069: throws DatabaseException {
070:
071: DbConfigManager configMgr = envImpl.getConfigManager();
072: nLockTables = configMgr.getInt(EnvironmentParams.N_LOCK_TABLES);
073: lockTables = new Map[nLockTables];
074: lockTableLatches = new Latch[nLockTables];
075: for (int i = 0; i < nLockTables; i++) {
076: lockTables[i] = new HashMap();
077: lockTableLatches[i] = LatchSupport.makeLatch("Lock Table "
078: + i, envImpl);
079: }
080: this .envImpl = envImpl;
081: memoryBudget = envImpl.getMemoryBudget();
082: nRequests = 0;
083: nWaits = 0;
084: /*
085: traceLevel = Tracer.parseLevel
086: (env, EnvironmentParams.JE_LOGGING_LEVEL_LOCKMGR);
087: */
088:
089: /* Initialize mutable properties and register for notifications. */
090: envConfigUpdate(configMgr);
091: envImpl.addConfigObserver(this );
092: }
093:
094: /**
095: * Process notifications of mutable property changes.
096: */
097: public void envConfigUpdate(DbConfigManager configMgr)
098: throws DatabaseException {
099:
100: LockInfo
101: .setDeadlockStackTrace(configMgr
102: .getBoolean(EnvironmentParams.TXN_DEADLOCK_STACK_TRACE));
103: setLockTableDump(configMgr
104: .getBoolean(EnvironmentParams.TXN_DUMPLOCKS));
105: }
106:
107: /**
108: * Called when the je.txn.dumpLocks property is changed.
109: */
110: static void setLockTableDump(boolean enable) {
111: lockTableDump = enable;
112: }
113:
114: protected int getLockTableIndex(Long nodeId) {
115: return (((int) nodeId.longValue()) & 0x7fffffff) % nLockTables;
116: }
117:
118: protected int getLockTableIndex(long nodeId) {
119: return (((int) nodeId) & 0x7fffffff) % nLockTables;
120: }
121:
122: /**
123: * Attempt to acquire a lock of <i>type</i> on <i>nodeId</i>. If the lock
124: * acquisition would result in a deadlock, throw an exception.<br> If the
125: * requested lock is not currently available, block until it is or until
126: * timeout milliseconds have elapsed.<br> If a lock of <i>type</i> is
127: * already held, return EXISTING.<br> If a WRITE lock is held and a READ
128: * lock is requested, return PROMOTION.<br>
129: *
130: * If a lock request is for a lock that is not currently held, return
131: * either NEW or DENIED depending on whether the lock is granted or
132: * not.<br>
133: *
134: * @param nodeId The NodeId to lock.
135: *
136: * @param locker The Locker to lock this on behalf of.
137: *
138: * @param type The lock type requested.
139: *
140: * @param timeout milliseconds to time out after if lock couldn't be
141: * obtained. 0 means block indefinitely. Not used if nonBlockingRequest
142: * is true.
143: *
144: * @param nonBlockingRequest if true, means don't block if lock can't be
145: * acquired, and ignore the timeout parameter.
146: *
147: * @return a LockGrantType indicating whether the request was fulfilled
148: * or not. LockGrantType.NEW means the lock grant was fulfilled and
149: * the caller did not previously hold the lock. PROMOTION means the
150: * lock was granted and it was a promotion from READ to WRITE. EXISTING
151: * means the lock was already granted (not a promotion). DENIED means
152: * the lock was not granted either because the timeout passed without
153: * acquiring the lock or timeout was -1 and the lock was not immediately
154: * available.
155: *
156: * @throws DeadlockException if acquiring the lock would result in
157: * a deadlock.
158: */
159: public LockGrantType lock(long nodeId, Locker locker,
160: LockType type, long timeout, boolean nonBlockingRequest,
161: DatabaseImpl database) throws DeadlockException,
162: DatabaseException {
163:
164: assert timeout >= 0;
165:
166: /*
167: * Lock on locker before latching the lockTable to avoid having another
168: * notifier perform the notify before the waiter is actually waiting.
169: */
170: synchronized (locker) {
171: Long nid = new Long(nodeId);
172: LockAttemptResult result = attemptLock(nid, locker, type,
173: nonBlockingRequest);
174: /* Got the lock, return. */
175: if (result.success
176: || result.lockGrant == LockGrantType.DENIED) {
177: return result.lockGrant;
178: }
179:
180: assert checkNoLatchesHeld(nonBlockingRequest) : LatchSupport
181: .countLatchesHeld()
182: + " latches held while trying to lock, lock table ="
183: + LatchSupport.latchesHeldToString();
184:
185: /*
186: * We must have gotten WAIT_* from the lock request. We know that
187: * this is a blocking request, because if it wasn't, Lock.lock
188: * would have returned DENIED. Go wait!
189: */
190: assert !nonBlockingRequest;
191: try {
192: boolean doWait = true;
193:
194: /*
195: * Before blocking, check locker timeout. We need to check here
196: * or lock timeouts will always take precedence and we'll never
197: * actually get any txn timeouts.
198: */
199: if (locker.isTimedOut()) {
200: if (validateOwnership(nid, locker, type, true,
201: memoryBudget)) {
202: doWait = false;
203: } else {
204: DeadlockException DE = makeTimeoutMsg(
205: "Transaction", locker, nodeId, type,
206: result.lockGrant, result.useLock,
207: locker.getTxnTimeOut(), locker
208: .getTxnStartMillis(), System
209: .currentTimeMillis(), database);
210: throw DE;
211: }
212: }
213:
214: boolean keepTime = (timeout > 0);
215: long startTime = (keepTime ? System.currentTimeMillis()
216: : 0);
217: while (doWait) {
218: locker.setWaitingFor(result.useLock);
219: try {
220: locker.wait(timeout);
221: } catch (InterruptedException IE) {
222: throw new RunRecoveryException(envImpl, IE);
223: }
224:
225: boolean lockerTimedOut = locker.isTimedOut();
226: long now = System.currentTimeMillis();
227: boolean this LockTimedOut = (keepTime && (now
228: - startTime > timeout));
229: boolean isRestart = (result.lockGrant == LockGrantType.WAIT_RESTART);
230:
231: /*
232: * Re-check for ownership of the lock following wait. If
233: * we timed out and we don't have ownership then flush this
234: * lock from both the waiters and owners while under the
235: * lock table latch. See SR 10103.
236: */
237: if (validateOwnership(nid, locker, type,
238: lockerTimedOut || this LockTimedOut
239: || isRestart, memoryBudget)) {
240: break;
241: } else {
242:
243: /*
244: * After a restart conflict the lock will not be held.
245: */
246: if (isRestart) {
247: throw rangeRestartException;
248: }
249:
250: if (this LockTimedOut) {
251: locker.setOnlyAbortable();
252: DeadlockException DE = makeTimeoutMsg(
253: "Lock", locker, nodeId, type,
254: result.lockGrant, result.useLock,
255: timeout, startTime, now, database);
256: throw DE;
257: }
258:
259: if (lockerTimedOut) {
260: locker.setOnlyAbortable();
261: DeadlockException DE = makeTimeoutMsg(
262: "Transaction", locker, nodeId,
263: type, result.lockGrant,
264: result.useLock, locker
265: .getTxnTimeOut(), locker
266: .getTxnStartMillis(), now,
267: database);
268: throw DE;
269: }
270: }
271: }
272: } finally {
273: locker.setWaitingFor(null);
274: assert EnvironmentImpl.maybeForceYield();
275: }
276:
277: locker.addLock(nid, type, result.lockGrant);
278:
279: return result.lockGrant;
280: }
281: }
282:
283: abstract protected Lock lookupLock(Long nodeId)
284: throws DatabaseException;
285:
286: protected Lock lookupLockInternal(Long nodeId, int lockTableIndex)
287: throws DatabaseException {
288:
289: /* Get the target lock. */
290: Map lockTable = lockTables[lockTableIndex];
291: Lock useLock = (Lock) lockTable.get(nodeId);
292: return useLock;
293: }
294:
295: abstract protected LockAttemptResult attemptLock(Long nodeId,
296: Locker locker, LockType type, boolean nonBlockingRequest)
297: throws DatabaseException;
298:
299: protected LockAttemptResult attemptLockInternal(Long nodeId,
300: Locker locker, LockType type, boolean nonBlockingRequest,
301: int lockTableIndex) throws DatabaseException {
302:
303: nRequests++;
304:
305: /* Get the target lock. */
306: Map lockTable = lockTables[lockTableIndex];
307: Lock useLock = (Lock) lockTable.get(nodeId);
308: if (useLock == null) {
309: useLock = new Lock();
310: lockTable.put(nodeId, useLock);
311: memoryBudget.updateLockMemoryUsage(TOTAL_LOCK_OVERHEAD,
312: lockTableIndex);
313: }
314:
315: /*
316: * Attempt to lock. Possible return values are NEW, PROMOTION, DENIED,
317: * EXISTING, WAIT_NEW, WAIT_PROMOTION, WAIT_RESTART.
318: */
319: LockGrantType lockGrant = useLock.lock(type, locker,
320: nonBlockingRequest, memoryBudget, lockTableIndex);
321: boolean success = false;
322:
323: /* Was the attempt successful? */
324: if ((lockGrant == LockGrantType.NEW)
325: || (lockGrant == LockGrantType.PROMOTION)) {
326: locker.addLock(nodeId, type, lockGrant);
327: success = true;
328: } else if (lockGrant == LockGrantType.EXISTING) {
329: success = true;
330: } else if (lockGrant == LockGrantType.DENIED) {
331: /* Locker.lock will throw LockNotGrantedException. */
332: } else {
333: nWaits++;
334: }
335: return new LockAttemptResult(useLock, lockGrant, success);
336: }
337:
338: /**
339: * Create a informative lock or txn timeout message.
340: */
341: protected abstract DeadlockException makeTimeoutMsg(
342: String lockOrTxn, Locker locker, long nodeId,
343: LockType type, LockGrantType grantType, Lock useLock,
344: long timeout, long start, long now, DatabaseImpl database)
345: throws DatabaseException;
346:
347: /**
348: * Do the real work of creating an lock or txn timeout message.
349: */
350: protected DeadlockException makeTimeoutMsgInternal(
351: String lockOrTxn, Locker locker, long nodeId,
352: LockType type, LockGrantType grantType, Lock useLock,
353: long timeout, long start, long now, DatabaseImpl database) {
354:
355: /*
356: * Because we're accessing parts of the lock, need to have protected
357: * access to the lock table because things can be changing out from
358: * underneath us. This is a big hammer to grab for so long while we
359: * traverse the graph, but it's only when we have a deadlock and we're
360: * creating a debugging message.
361: *
362: * The alternative would be to handle ConcurrentModificationExceptions
363: * and retry until none of them happen.
364: */
365: if (lockTableDump) {
366: System.out
367: .println("++++++++++ begin lock table dump ++++++++++");
368: for (int i = 0; i < nLockTables; i++) {
369: StringBuffer sb = new StringBuffer();
370: dumpToStringNoLatch(sb, i);
371: System.out.println(sb.toString());
372: }
373: System.out
374: .println("++++++++++ end lock table dump ++++++++++");
375: }
376:
377: StringBuffer sb = new StringBuffer();
378: sb.append(lockOrTxn);
379: sb.append(" expired. Locker ").append(locker);
380: sb.append(": waited for lock");
381:
382: if (database != null) {
383: sb.append(" on database=").append(database.getDebugName());
384: }
385: sb.append(" node=").append(nodeId);
386: sb.append(" type=").append(type);
387: sb.append(" grant=").append(grantType);
388: sb.append(" timeoutMillis=").append(timeout);
389: sb.append(" startTime=").append(start);
390: sb.append(" endTime=").append(now);
391: Set owners = useLock.getOwnersClone();
392: List waiters = useLock.getWaitersListClone();
393: sb.append("\nOwners: ").append(owners);
394: sb.append("\nWaiters: ").append(waiters).append("\n");
395: StringBuffer deadlockInfo = findDeadlock(useLock, locker);
396: if (deadlockInfo != null) {
397: sb.append(deadlockInfo);
398: }
399: DeadlockException ret = new DeadlockException(sb.toString());
400: ret.setOwnerTxnIds(getTxnIds(owners));
401: ret.setWaiterTxnIds(getTxnIds(waiters));
402: return ret;
403: }
404:
405: private long[] getTxnIds(Collection c) {
406: long[] ret = new long[c.size()];
407: Iterator iter = c.iterator();
408: int i = 0;
409: while (iter.hasNext()) {
410: LockInfo info = (LockInfo) iter.next();
411: ret[i++] = info.getLocker().getId();
412: }
413:
414: return ret;
415: }
416:
417: /**
418: * Release a lock and possibly notify any waiters that they have been
419: * granted the lock.
420: *
421: * @param nodeId The node ID of the lock to release.
422: *
423: * @return true if the lock is released successfully, false if
424: * the lock is not currently being held.
425: */
426: boolean release(long nodeId, Locker locker)
427: throws DatabaseException {
428:
429: synchronized (locker) {
430: Set newOwners = releaseAndFindNotifyTargets(nodeId, locker);
431:
432: if (newOwners == null) {
433: return false;
434: }
435:
436: if (newOwners.size() > 0) {
437:
438: /*
439: * There is a new set of owners and/or there are restart
440: * waiters that should be notified.
441: */
442: Iterator iter = newOwners.iterator();
443:
444: while (iter.hasNext()) {
445: Locker lockerToNotify = (Locker) iter.next();
446:
447: /* Use notifyAll to support multiple threads per txn. */
448: synchronized (lockerToNotify) {
449: lockerToNotify.notifyAll();
450: }
451:
452: assert EnvironmentImpl.maybeForceYield();
453: }
454: }
455:
456: return true;
457: }
458: }
459:
460: /**
461: * Release the lock, and return the set of new owners to notify, if any.
462: *
463: * @return
464: * null if the lock does not exist or the given locker was not the owner,
465: * a non-empty set if owners should be notified after releasing,
466: * an empty set if no notification is required.
467: */
468: protected abstract Set releaseAndFindNotifyTargets(long nodeId,
469: Locker locker) throws DatabaseException;
470:
471: /**
472: * Do the real work of releaseAndFindNotifyTargets
473: */
474: protected Set releaseAndFindNotifyTargetsInternal(long nodeId,
475: Locker locker, int lockTableIndex) throws DatabaseException {
476:
477: Map lockTable = lockTables[lockTableIndex];
478: Lock useLock = (Lock) lockTable.get(new Long(nodeId));
479:
480: if (useLock == null) {
481: /* Lock doesn't exist. */
482: return null;
483: }
484:
485: Set lockersToNotify = useLock.release(locker, memoryBudget,
486: lockTableIndex);
487: if (lockersToNotify == null) {
488: /* Not owner. */
489: return null;
490: }
491:
492: /* If it's not in use at all, remove it from the lock table. */
493: if ((useLock.nWaiters() == 0) && (useLock.nOwners() == 0)) {
494: lockTables[lockTableIndex].remove(new Long(nodeId));
495: memoryBudget.updateLockMemoryUsage(
496: REMOVE_TOTAL_LOCK_OVERHEAD, lockTableIndex);
497: }
498:
499: return lockersToNotify;
500: }
501:
502: /**
503: * Transfer ownership a lock from one locker to another locker. We're not
504: * sending any notification to the waiters on the lock table, and the past
505: * and present owner should be ready for the transfer.
506: */
507: abstract void transfer(long nodeId, Locker owningLocker,
508: Locker destLocker, boolean demoteToRead)
509: throws DatabaseException;
510:
511: /**
512: * Do the real work of transfer
513: */
514: protected void transferInternal(long nodeId, Locker owningLocker,
515: Locker destLocker, boolean demoteToRead, int lockTableIndex)
516: throws DatabaseException {
517:
518: Map lockTable = lockTables[lockTableIndex];
519: Lock useLock = (Lock) lockTable.get(new Long(nodeId));
520:
521: assert useLock != null : "Transfer, lock " + nodeId
522: + " was null";
523: if (demoteToRead) {
524: useLock.demote(owningLocker);
525: }
526: useLock.transfer(new Long(nodeId), owningLocker, destLocker,
527: memoryBudget, lockTableIndex);
528: owningLocker.removeLock(nodeId);
529: }
530:
531: /**
532: * Transfer ownership a lock from one locker to a set of other txns,
533: * cloning the lock as necessary. This will always be demoted to read, as
534: * we can't have multiple locker owners any other way. We're not sending
535: * any notification to the waiters on the lock table, and the past and
536: * present owners should be ready for the transfer.
537: */
538: abstract void transferMultiple(long nodeId, Locker owningLocker,
539: Locker[] destLockers) throws DatabaseException;
540:
541: /**
542: * Do the real work of transferMultiple
543: */
544: protected void transferMultipleInternal(long nodeId,
545: Locker owningLocker, Locker[] destLockers,
546: int lockTableIndex) throws DatabaseException {
547:
548: Map lockTable = lockTables[lockTableIndex];
549: Lock useLock = (Lock) lockTable.get(new Long(nodeId));
550:
551: assert useLock != null : "Transfer, lock " + nodeId
552: + " was null";
553: useLock.demote(owningLocker);
554: useLock.transferMultiple(new Long(nodeId), owningLocker,
555: destLockers, memoryBudget, lockTableIndex);
556: owningLocker.removeLock(nodeId);
557: }
558:
559: /**
560: * Demote a lock from write to read. Call back to the owning locker to
561: * move this to its read collection.
562: * @param lock The lock to release. If null, use nodeId to find lock
563: * @param locker
564: */
565: abstract void demote(long nodeId, Locker locker)
566: throws DatabaseException;
567:
568: /**
569: * Do the real work of demote.
570: */
571: protected void demoteInternal(long nodeId, Locker locker,
572: int lockTableIndex) throws DatabaseException {
573:
574: Map lockTable = lockTables[lockTableIndex];
575: Lock useLock = (Lock) lockTable.get(new Long(nodeId));
576: useLock.demote(locker);
577: locker.moveWriteToReadLock(nodeId, useLock);
578: }
579:
580: /**
581: * Test the status of the lock on nodeId. If any transaction holds any
582: * lock on it, true is returned. If no transaction holds a lock on it,
583: * false is returned.
584: *
585: * This method is only used by unit tests.
586: *
587: * @param nodeId The NodeId to check.
588: * @return true if any transaction holds any lock on the nodeid. false
589: * if no lock is held by any transaction.
590: */
591: abstract boolean isLocked(Long nodeId) throws DatabaseException;
592:
593: /**
594: * Do the real work of isLocked.
595: */
596: protected boolean isLockedInternal(Long nodeId, int lockTableIndex) {
597:
598: Map lockTable = lockTables[lockTableIndex];
599: Lock entry = (Lock) lockTable.get(nodeId);
600: if (entry == null) {
601: return false;
602: }
603:
604: return entry.nOwners() != 0;
605: }
606:
607: /**
608: * Return true if this locker owns this a lock of this type on given node.
609: *
610: * This method is only used by unit tests.
611: */
612: abstract boolean isOwner(Long nodeId, Locker locker, LockType type)
613: throws DatabaseException;
614:
615: /**
616: * Do the real work of isOwner.
617: */
618: protected boolean isOwnerInternal(Long nodeId, Locker locker,
619: LockType type, int lockTableIndex) {
620:
621: Map lockTable = lockTables[lockTableIndex];
622: Lock entry = (Lock) lockTable.get(nodeId);
623: if (entry == null) {
624: return false;
625: }
626:
627: return entry.isOwner(locker, type);
628: }
629:
630: /**
631: * Return true if this locker is waiting on this lock.
632: *
633: * This method is only used by unit tests.
634: */
635: abstract boolean isWaiter(Long nodeId, Locker locker)
636: throws DatabaseException;
637:
638: /**
639: * Do the real work of isWaiter.
640: */
641: protected boolean isWaiterInternal(Long nodeId, Locker locker,
642: int lockTableIndex) {
643:
644: Map lockTable = lockTables[lockTableIndex];
645: Lock entry = (Lock) lockTable.get(nodeId);
646: if (entry == null) {
647: return false;
648: }
649:
650: return entry.isWaiter(locker);
651: }
652:
653: /**
654: * Return the number of waiters for this lock.
655: */
656: abstract int nWaiters(Long nodeId) throws DatabaseException;
657:
658: /**
659: * Do the real work of nWaiters.
660: */
661: protected int nWaitersInternal(Long nodeId, int lockTableIndex) {
662:
663: Map lockTable = lockTables[lockTableIndex];
664: Lock entry = (Lock) lockTable.get(nodeId);
665: if (entry == null) {
666: return -1;
667: }
668:
669: return entry.nWaiters();
670: }
671:
672: /**
673: * Return the number of owners of this lock.
674: */
675: abstract int nOwners(Long nodeId) throws DatabaseException;
676:
677: /**
678: * Do the real work of nWaiters.
679: */
680: protected int nOwnersInternal(Long nodeId, int lockTableIndex) {
681:
682: Map lockTable = lockTables[lockTableIndex];
683: Lock entry = (Lock) lockTable.get(nodeId);
684: if (entry == null) {
685: return -1;
686: }
687:
688: return entry.nOwners();
689: }
690:
691: /**
692: * @return the transaction that owns the write lock for this
693: */
694: abstract Locker getWriteOwnerLocker(Long nodeId)
695: throws DatabaseException;
696:
697: /**
698: * Do the real work of getWriteOwnerLocker.
699: */
700: protected Locker getWriteOwnerLockerInternal(Long nodeId,
701: int lockTableIndex) throws DatabaseException {
702:
703: Map lockTable = lockTables[lockTableIndex];
704: Lock lock = (Lock) lockTable.get(nodeId);
705: if (lock == null) {
706: return null;
707: } else if (lock.nOwners() > 1) {
708: /* not a write lock */
709: return null;
710: } else {
711: return lock.getWriteOwnerLocker();
712: }
713: }
714:
715: /*
716: * Check if we got ownership while we were waiting. If we didn't get
717: * ownership, and we timed out, remove this locker from the set of
718: * waiters. Do this in a critical section to prevent any orphaning of the
719: * lock -- we must be in a critical section between the time that we check
720: * ownership and when we flush any waiters (SR #10103)
721: * @return true if you are the owner.
722: */
723: abstract protected boolean validateOwnership(Long nodeId,
724: Locker locker, LockType type, boolean flushFromWaiters,
725: MemoryBudget mb) throws DatabaseException;
726:
727: /*
728: * Do the real work of validateOwnershipInternal.
729: */
730: protected boolean validateOwnershipInternal(Long nodeId,
731: Locker locker, LockType type, boolean flushFromWaiters,
732: MemoryBudget mb, int lockTableIndex)
733: throws DatabaseException {
734:
735: if (isOwnerInternal(nodeId, locker, type, lockTableIndex)) {
736: return true;
737: }
738:
739: if (flushFromWaiters) {
740: Lock entry = (Lock) lockTables[lockTableIndex].get(nodeId);
741: if (entry != null) {
742: entry.flushWaiter(locker, mb, lockTableIndex);
743: }
744: }
745: return false;
746: }
747:
748: /**
749: * Statistics
750: */
751: public LockStats lockStat(StatsConfig config)
752: throws DatabaseException {
753:
754: LockStats stats = new LockStats();
755: stats.setNRequests(nRequests);
756: stats.setNWaits(nWaits);
757: if (config.getClear()) {
758: nWaits = 0;
759: nRequests = 0;
760: }
761:
762: for (int i = 0; i < nLockTables; i++) {
763: LatchStats latchStats = (LatchStats) lockTableLatches[i]
764: .getLatchStats();
765: stats.accumulateLockTableLatchStats(latchStats);
766: }
767:
768: /* Dump info about the lock table. */
769: if (!config.getFast()) {
770: dumpLockTable(stats);
771: }
772: return stats;
773: }
774:
775: /**
776: * Dump the lock table to the lock stats.
777: */
778: abstract protected void dumpLockTable(LockStats stats)
779: throws DatabaseException;
780:
781: /**
782: * Do the real work of dumpLockTableInternal.
783: */
784: protected void dumpLockTableInternal(LockStats stats, int i) {
785: Map lockTable = lockTables[i];
786: stats.accumulateNTotalLocks(lockTable.size());
787: Iterator iter = lockTable.values().iterator();
788: while (iter.hasNext()) {
789: Lock lock = (Lock) iter.next();
790: stats.setNWaiters(stats.getNWaiters() + lock.nWaiters());
791: stats.setNOwners(stats.getNOwners() + lock.nOwners());
792:
793: /* Go through all the owners for a lock. */
794: Iterator ownerIter = lock.getOwnersClone().iterator();
795: while (ownerIter.hasNext()) {
796: LockInfo info = (LockInfo) ownerIter.next();
797: if (info.getLockType().isWriteLock()) {
798: stats.setNWriteLocks(stats.getNWriteLocks() + 1);
799: } else {
800: stats.setNReadLocks(stats.getNReadLocks() + 1);
801: }
802: }
803: }
804: }
805:
806: /**
807: * Debugging
808: */
809: public void dump() throws DatabaseException {
810:
811: System.out.println(dumpToString());
812: }
813:
814: public String dumpToString() throws DatabaseException {
815:
816: StringBuffer sb = new StringBuffer();
817: for (int i = 0; i < nLockTables; i++) {
818: lockTableLatches[i].acquire();
819: try {
820: dumpToStringNoLatch(sb, i);
821: } finally {
822: lockTableLatches[i].release();
823: }
824: }
825: return sb.toString();
826: }
827:
828: private void dumpToStringNoLatch(StringBuffer sb, int whichTable) {
829: Map lockTable = lockTables[whichTable];
830: Iterator entries = lockTable.entrySet().iterator();
831:
832: while (entries.hasNext()) {
833: Map.Entry entry = (Map.Entry) entries.next();
834: Long nid = (Long) entry.getKey();
835: Lock lock = (Lock) entry.getValue();
836: sb.append("---- Node Id: ").append(nid).append("----\n");
837: sb.append(lock);
838: sb.append('\n');
839: }
840: }
841:
842: private boolean checkNoLatchesHeld(boolean nonBlockingRequest) {
843: if (nonBlockingRequest) {
844: return true; // don't check if it's a non blocking request.
845: } else {
846: return (LatchSupport.countLatchesHeld() == 0);
847: }
848: }
849:
850: private StringBuffer findDeadlock(Lock lock, Locker rootLocker) {
851:
852: Set ownerSet = new HashSet();
853: ownerSet.add(rootLocker);
854: StringBuffer ret = findDeadlock1(ownerSet, lock, rootLocker);
855: if (ret != null) {
856: return ret;
857: } else {
858: return null;
859: }
860: }
861:
862: private StringBuffer findDeadlock1(Set ownerSet, Lock lock,
863: Locker rootLocker) {
864:
865: Iterator ownerIter = lock.getOwnersClone().iterator();
866: while (ownerIter.hasNext()) {
867: LockInfo info = (LockInfo) ownerIter.next();
868: Locker locker = info.getLocker();
869: Lock waitsFor = locker.getWaitingFor();
870: if (ownerSet.contains(locker) || locker == rootLocker) {
871: /* Found a cycle. */
872: StringBuffer ret = new StringBuffer();
873: ret.append("Transaction ").append(locker.toString());
874: ret.append(" owns ").append(
875: System.identityHashCode(lock));
876: ret.append(" ").append(info).append("\n");
877: ret.append("Transaction ").append(locker.toString());
878: ret.append(" waits for ");
879: if (waitsFor == null) {
880: ret.append(" nothing");
881: } else {
882: ret.append(" node ");
883: ret.append(System.identityHashCode(waitsFor));
884: }
885: ret.append("\n");
886: return ret;
887: }
888: if (waitsFor != null) {
889: ownerSet.add(locker);
890: StringBuffer sb = findDeadlock1(ownerSet, waitsFor,
891: rootLocker);
892: String overflowTag = "<Overflow>\n";
893: if (sb != null) {
894: String waitInfo = "Transaction " + locker
895: + " waits for " + waitsFor + "\n";
896: /* Limit length of message. */
897: if (sb.length() < 100000) {
898: sb.insert(0, waitInfo);
899: } else {
900: if (!sb.substring(0, overflowTag.length())
901: .equals(overflowTag)) {
902: sb.insert(0, overflowTag);
903: }
904: }
905: return sb;
906: }
907: ownerSet.remove(locker); // is this necessary?
908: }
909: }
910:
911: return null;
912: }
913:
914: /**
915: * This is just a struct to hold a multi-value return.
916: */
917: static class LockAttemptResult {
918: boolean success;
919: Lock useLock;
920: LockGrantType lockGrant;
921:
922: LockAttemptResult(Lock useLock, LockGrantType lockGrant,
923: boolean success) {
924:
925: this.useLock = useLock;
926: this.lockGrant = lockGrant;
927: this.success = success;
928: }
929: }
930: }
|