Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*-
    * See the file LICENSE for redistribution information.
    *
    * Copyright (c) 2002, 2013 Oracle and/or its affiliates.  All rights reserved.
    *
    */
   
   package com.sleepycat.je.txn;
   
  import static com.sleepycat.je.txn.LockStatDefinition.GROUP_DESC;
  import static com.sleepycat.je.txn.LockStatDefinition.GROUP_NAME;
  import static com.sleepycat.je.txn.LockStatDefinition.LOCK_OWNERS;
  import static com.sleepycat.je.txn.LockStatDefinition.LOCK_READ_LOCKS;
  import static com.sleepycat.je.txn.LockStatDefinition.LOCK_REQUESTS;
  import static com.sleepycat.je.txn.LockStatDefinition.LOCK_TOTAL;
  import static com.sleepycat.je.txn.LockStatDefinition.LOCK_WAITERS;
  import static com.sleepycat.je.txn.LockStatDefinition.LOCK_WAITS;
  import static com.sleepycat.je.txn.LockStatDefinition.LOCK_WRITE_LOCKS;
  
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
LockManager manages locks. Note that locks are counted as taking up part of the JE cache;
  
  public abstract class LockManager implements EnvConfigObserver {
  
      /*
       * The total memory cost for a lock is the Lock object, plus its entry and
       * key in the lock hash table.
       *
       * The addition and removal of Lock objects, and the corresponding cost of
       * their hashmap entry and key are tracked through the LockManager.
       */
      static final long TOTAL_LOCKIMPL_OVERHEAD =
          . +
          . +
          .;
  
      static final long TOTAL_THINLOCKIMPL_OVERHEAD =
          . +
          . +
          .;
  
      private static final long REMOVE_TOTAL_LOCKIMPL_OVERHEAD =
          0 - ;
  
      private static final long REMOVE_TOTAL_THINLOCKIMPL_OVERHEAD =
          0 - ;
  
      private static final long THINLOCK_MUTATE_OVERHEAD =
          . -
          . +
          .;
      
      private static final List<ThreadLockerEMPTY_THREAD_LOCKERS =
          Collections.emptyList();
  
      int nLockTables = 1;
      Latch[] lockTableLatches;
      private final Map<Long,Lock>[] lockTables;          // keyed by LSN
     private final boolean oldLockExceptions;
     private final EnvironmentImpl envImpl;
     private final MemoryBudget memoryBudget;
 
     private final StatGroup stats;
     private final LongStat nRequests/* number of time a request was made. */
     private final LongStat nWaits;    /* number of time a request blocked. */
 
     private static RangeRestartException rangeRestartException =
         new RangeRestartException();
     private static boolean lockTableDump = false;

    
Maps a thread to a set of ThreadLockers. Currently this map is only maintained (non-null) in a replicated environment because it is only needed for determining when to throw LockPreemptedException. Access to the map need not be synchronized because it is a ConcurrentHashMap. Access to the TinyHashSet stored for each thread need not be synchronized, since it is only accessed by a single thread. A TinyHashSet is used because typically only a single ThreadLocker per thread will be open at one time.

 
     private final Map<ThreadTinyHashSet<ThreadLocker>> threadLockers;
 
     /*
      * @SuppressWarnings is used to stifle a type safety complaint about the
      * assignment of lockTables = new Map[nLockTables]. There's no way to
      * specify the type of the array.
      */
     @SuppressWarnings("unchecked")
     public LockManager(EnvironmentImpl envImpl) {
 
         DbConfigManager configMgr = envImpl.getConfigManager();
          = configMgr.getInt(.);
          =
             configMgr.getBoolean(.);
          = new Map[];
          = new Latch[];
         for (int i = 0; i < i++) {
             [i] = new HashMap<Long,Lock>();
             [i] = new Latch("Lock Table " + i);
         }
         this. = envImpl;
          = envImpl.getMemoryBudget();
 
          = new StatGroup();
          = new LongStat();
          = new LongStat();
 
         /* Initialize mutable properties and register for notifications. */
         envConfigUpdate(configMgrnull);
         envImpl.addConfigObserver(this);
 
         if (envImpl.isReplicated()) {
              =
                 new ConcurrentHashMap<ThreadTinyHashSet<ThreadLocker>>();
         } else {
              = null;
         }
     }

    
Process notifications of mutable property changes.
 
     public void envConfigUpdate(DbConfigManager configMgr,
                                 EnvironmentMutableConfig ignore) {
         LockInfo.setDeadlockStackTrace(configMgr.getBoolean
             (.));
         setLockTableDump(configMgr.getBoolean
             (.));
     }

    
Called when the je.txn.dumpLocks property is changed.
 
     static void setLockTableDump(boolean enable) {
          = enable;
     }
 
     int getLockTableIndex(Long lsn) {
         return (((intlsn.longValue()) & 0x7fffffff) %
             ;
     }
 
     int getLockTableIndex(long lsn) {
         return (((intlsn) & 0x7fffffff) % ;
     }

    
Attempt to acquire a lock of type on lsn. If the lock acquisition would result in a deadlock, throw an exception.
If the requested lock is not currently available, block until it is or until timeout milliseconds have elapsed.
If a lock of type is already held, return EXISTING.
If a WRITE lock is held and a READ lock is requested, return PROMOTION.
If a lock request is for a lock that is not currently held, return either NEW or DENIED depending on whether the lock is granted or not.

Parameters:
lsn The LSN to lock.
locker The Locker to lock this on behalf of.
type The lock type requested.
timeout milliseconds to time out after if lock couldn't be obtained. 0 means block indefinitely. Not used if nonBlockingRequest is true.
nonBlockingRequest if true, means don't block if lock can't be acquired, and ignore the timeout parameter.
jumpAheadOfWaiters grant the lock before other waiters, if any.
Returns:
a LockGrantType indicating whether the request was fulfilled or not. LockGrantType.NEW means the lock grant was fulfilled and the caller did not previously hold the lock. PROMOTION means the lock was granted and it was a promotion from READ to WRITE. EXISTING means the lock was already granted (not a promotion). DENIED means the lock was not granted because the timeout passed without acquiring the lock or timeout was 0 and the lock was not immediately available.
Throws:
com.sleepycat.je.LockConflictException if lock could not be acquired.
java.lang.IllegalArgumentException via db/cursor read/write methods, if non-transactional access to a replicated environment is attempted, and read-uncommitted is not specified.
 
     public LockGrantType lock(long lsn,
                               Locker locker,
                               LockType type,
                               long timeout,
                               boolean nonBlockingRequest,
                               boolean jumpAheadOfWaiters,
                               DatabaseImpl database)
         throws LockConflictExceptionDatabaseException {
 
         assert timeout >= 0;
 
         /* No lock needed for dirty-read, return as soon as possible. */
         if (type == .) {
             return .;
         }
 
         /*
          * Assert that a replication-defined locker is used for locks on
          * replicated databases.  Two cases are exempt from this rule:
          * - Only NameLNs that identify replicated DBs are replicated, not
          *   all NameLNs in the naming DB, so the naming DB is exempt.
          * - Non-preemption is permissible for selected internal operations
          *   because we can ensure that they are not long running and will not
          *   hold locks interminably.  A BasicLocker is often used internally
          *   in such cases.
          */
         if (.isReplicated() &&
             database != null &&
             database.isReplicated() &&
             !database.getId().equals(.) &&
             (locker.getPreemptable() || type.isWriteLock()) &&
             !locker.isReplicationDefined()) {
             throw EnvironmentFailureException.unexpectedState
                 ("Locker: " + locker.getClass().getName());
         }
 
         /*
          * Lock on locker before latching the lockTable to avoid having another
          * notifier perform the notify before the waiter is actually waiting.
          */
         synchronized (locker) {
             LockGrantType ret = null;
             ret = lockInternal(lsnlockertypetimeoutnonBlockingRequest,
                                jumpAheadOfWaitersdatabase);
             return ret;
         }
     }
 
     private LockGrantType lockInternal(long lsn,
                                        Locker locker,
                                        LockType type,
                                        long timeout,
                                        boolean nonBlockingRequest,
                                        boolean jumpAheadOfWaiters,
                                        DatabaseImpl database)
         throws DeadlockExceptionDatabaseException {
 
         Long nid = Long.valueOf(lsn);
         LockAttemptResult result = attemptLock
             (nidlockertypenonBlockingRequestjumpAheadOfWaiters);
         /* If we got the lock or a non-blocking lock was denied, return. */
         if (result.success ||
             result.lockGrant == .) {
             assert nonBlockingRequest || result.success;
             return result.lockGrant;
         }
 
         assert checkNoLatchesHeld(nonBlockingRequest):
             LatchSupport.countLatchesHeld() +
             " latches held while trying to lock, lock table =" +
             LatchSupport.latchesHeldToString();
 
         /*
          * We must have gotten WAIT_* from the lock request. We know that
          * this is a blocking request, because if it wasn't, Lock.lock
          * would have returned DENIED. Go wait!
          */
         assert !nonBlockingRequest;
         try {
             boolean doWait = true;
             boolean isImportunate = locker.getImportunate();
 
             /*
              * Before blocking, check locker/txn timeout. We need to check here
              * or lock timeouts will always take precedence and we'll never
              * actually get any txn timeouts.
              */
             if (locker.isTimedOut()) {
                 if (validateOwnership(nidlockertype,
                                       !isImportunate,
                                       )) {
                     doWait = false;
                 } else if (isImportunate) {
                     result = stealLock(nidlockertype);
                     if (result.success) {
                         doWait = false;
                     } else {
                         /* Lock holder is non-preemptable, wait below. */
                     }
                 } else {
                     throw makeTimeoutMsg(false /*isLockNotTxnTimeout*/,
                                          lockerlsntype,
                                          result.lockGrant,
                                          result.useLock,
                                          locker.getTxnTimeout(),
                                          locker.getTxnStartMillis(),
                                          System.currentTimeMillis(),
                                          database);
                 }
             }
 
             boolean keepTime = (timeout > 0);
             long startTime = (keepTime ? System.currentTimeMillis() : 0);
             while (doWait) {
                 locker.setWaitingFor(result.useLock);
 
                 try {
                     locker.wait(timeout);
                 } catch (InterruptedException IE) {
                     throw new ThreadInterruptedException(IE);
                 }
 
                 boolean lockerTimedOut = locker.isTimedOut();
                 long now = System.currentTimeMillis();
                 boolean thisLockTimedOut =
                     (keepTime && (now - startTime >= timeout));
                 boolean isRestart =
                     (result.lockGrant == .);
 
                 /*
                  * Re-check for ownership of the lock following wait.  If
                  * we timed out and we don't have ownership then flush this
                  * lock from both the waiters and owners while under the
                  * lock table latch.  See SR 10103.
                  */
                 if (validateOwnership(nidlockertype,
                                       (lockerTimedOut ||
                                       thisLockTimedOut ||
                                       isRestart) &&
                                       !isImportunate,
                                       )) {
                     break;
                 } else if (isImportunate) {
                     result = stealLock(nidlockertype);
                     if (result.success) {
                         break;
                     } else {
                         /* Lock holder is non-preemptable, wait again. */
                     }
                 } else {
                     /* After a restart conflict the lock will not be held. */
                     if (isRestart) {
                         throw ;
                     }
 
                     if (thisLockTimedOut) {
                         throw makeTimeoutMsg
                             (true /*isLockNotTxnTimeout*/lockerlsn,
                              typeresult.lockGrantresult.useLock,
                              timeoutstartTimenowdatabase);
                     }
 
                     if (lockerTimedOut) {
                         throw makeTimeoutMsg
                             (false /*isLockNotTxnTimeout*/lockerlsn,
                              typeresult.lockGrantresult.useLock,
                              locker.getTxnTimeout(),
                              locker.getTxnStartMillis(), nowdatabase);
                     }
                 }
             }
         } finally {
             locker.setWaitingFor(null);
             assert EnvironmentImpl.maybeForceYield();
         }
 
         /*
          * After waiting for the lock, we must break out of the wait loop and
          * add the lock to the locker.  This is true even for importunate
          * lockers, since an existing lock (acquired via a release) will not be
          * added to the locker by attemptLock. [#16879]
          */
         locker.addLock(nidtyperesult.lockGrant);
 
         return result.lockGrant;
     }

    
Returns the Lockers that own a lock on the given LSN. Note that when this method returns, there is nothing to prevent these lockers from releasing the lock or being closed.
 
     public abstract Set<LockInfogetOwners(Long lsn);
 
     Set<LockInfogetOwnersInternal(Long lsnint lockTableIndex) {
         /* Get the target lock. */
         Map<Long,LocklockTable = [lockTableIndex];
         Lock useLock = lockTable.get(lsn);
         if (useLock == null) {
             return null;
         }
         return useLock.getOwnersClone();
     }

    
Returns the LockType if the given locker owns a lock on the given node, or null if the lock is not owned.
 
     public abstract LockType getOwnedLockType(Long lsnLocker locker);
 
                                       Locker locker,
                                       int lockTableIndex) {
         /* Get the target lock. */
         Map<Long,LocklockTable = [lockTableIndex];
         Lock useLock = lockTable.get(lsn);
         if (useLock == null) {
             return null;
         }
         return useLock.getOwnedLockType(locker);
     }
 
     public abstract boolean isLockUncontended(Long lsn);
 
     boolean isLockUncontendedInternal(Long lsnint lockTableIndex) {
         /* Get the target lock. */
         Map<Long,LocklockTable = [lockTableIndex];
         Lock useLock = lockTable.get(lsn);
         if (useLock == null) {
             return true;
         }
         return useLock.nWaiters() == 0 &&
                useLock.nOwners() == 0;
     }
 
     abstract Lock lookupLock(Long lsn)
         throws DatabaseException;
 
     Lock lookupLockInternal(Long lsnint lockTableIndex) {
         /* Get the target lock. */
         Map<Long,LocklockTable = [lockTableIndex];
         Lock useLock = lockTable.get(lsn);
         return useLock;
     }
 
     abstract LockAttemptResult attemptLock(Long lsn,
                                            Locker locker,
                                            LockType type,
                                            boolean nonBlockingRequest,
                                            boolean jumpAheadOfWaiters)
         throws DatabaseException;
 
                                           Locker locker,
                                           LockType type,
                                           boolean nonBlockingRequest,
                                           boolean jumpAheadOfWaiters,
                                           int lockTableIndex)
         throws DatabaseException {
 
         .increment();
 
         /* Get the target lock. */
         Map<Long,LocklockTable = [lockTableIndex];
         Lock useLock = lockTable.get(lsn);
         if (useLock == null) {
             useLock = new ThinLockImpl();
             lockTable.put(lsnuseLock);
             .updateLockMemoryUsage
                 (lockTableIndex);
         }
 
         /*
          * Attempt to lock.  Possible return values are NEW, PROMOTION, DENIED,
          * EXISTING, WAIT_NEW, WAIT_PROMOTION, WAIT_RESTART.
          */
         LockAttemptResult lar = useLock.lock
             (typelockernonBlockingRequestjumpAheadOfWaiters,
              lockTableIndex);
         if (lar.useLock != useLock) {
             /* The lock mutated from ThinLockImpl to LockImpl. */
             useLock = lar.useLock;
             lockTable.put(lsnuseLock);
             /* We still have the overhead of the hashtable (locktable). */
             .updateLockMemoryUsage
                 (lockTableIndex);
         }
         LockGrantType lockGrant = lar.lockGrant;
         boolean success = false;
 
         /* Was the attempt successful? */
         if ((lockGrant == .) ||
             (lockGrant == .)) {
             locker.addLock(lsntypelockGrant);
             success = true;
         } else if (lockGrant == .) {
             success = true;
         } else if (lockGrant == .) {
             /* Locker.lock will throw LockNotAvailableException. */
         } else {
             .increment();
         }
         return new LockAttemptResult(useLocklockGrantsuccess);
     }

    
Create a informative lock or txn timeout message.
 
     abstract LockConflictException makeTimeoutMsg(boolean isLockNotTxnTimeout,
                                                   Locker locker,
                                                   long lsn,
                                                   LockType type,
                                                   LockGrantType grantType,
                                                   Lock useLock,
                                                   long timeout,
                                                   long start,
                                                   long now,
                                                   DatabaseImpl database)
         throws DatabaseException;

    
Do the real work of creating an lock or txn timeout message.
 
     LockConflictException makeTimeoutMsgInternal(boolean isLockNotTxnTimeout,
                                                  Locker locker,
                                                  long lsn,
                                                  LockType type,
                                                  LockGrantType grantType,
                                                  Lock useLock,
                                                  long timeout,
                                                  long start,
                                                  long now,
                                                  DatabaseImpl database) {
 
         /*
          * Because we're accessing parts of the lock, need to have protected
          * access to the lock table because things can be changing out from
          * underneath us.  This is a big hammer to grab for so long while we
          * traverse the graph, but it's only when we have a deadlock and we're
          * creating a debugging message.
          *
          * The alternative would be to handle ConcurrentModificationExceptions
          * and retry until none of them happen.
          */
         if () {
             ..println("++++++++++ begin lock table dump ++++++++++");
             for (int i = 0; i < i++) {
                 boolean success = false;
                 for (int j = 0; j < 3 && !successj++) {
                     try {
                         StringBuilder sb = new StringBuilder();
                         dumpToStringNoLatch(sbi);
                         ..println(sb.toString());
                         success = true;
                         break// for j...
                     } catch (ConcurrentModificationException CME) {
                         continue;
                     }
                 }
                 if (!success) {
                     ..println("Couldn't dump locktable " + i);
                 }
             }
             ..println("++++++++++ end lock table dump ++++++++++");
         }
 
         StringBuilder sb = new StringBuilder();
         sb.append(isLockNotTxnTimeout ? "Lock" : "Transaction");
         sb.append(" expired. Locker ").append(locker);
         sb.append(": waited for lock");
 
         if (database != null) {
             sb.append(" on database=").append(database.getDebugName());
         }
         sb.append(" LockAddr:").append(System.identityHashCode(useLock));
         sb.append(" LSN=").append(DbLsn.getNoFormatString(lsn));
         sb.append(" type=").append(type);
         sb.append(" grant=").append(grantType);
         sb.append(" timeoutMillis=").append(timeout);
         sb.append(" startTime=").append(start);
         sb.append(" endTime=").append(now);
         Set<LockInfoowners = useLock.getOwnersClone();
         List<LockInfowaiters = useLock.getWaitersListClone();
         sb.append("\nOwners: ").append(owners);
         sb.append("\nWaiters: ").append(waiters).append("\n");
         StringBuilder deadlockInfo = findDeadlock(useLocklocker);
         if (deadlockInfo != null) {
             sb.append(deadlockInfo);
         }
         LockConflictException ret = isLockNotTxnTimeout ?
             newLockTimeoutException(lockersb.toString()) :
             newTxnTimeoutException(lockersb.toString());
 
         ret.setOwnerTxnIds(getTxnIds(owners));
         ret.setWaiterTxnIds(getTxnIds(waiters));
         ret.setTimeoutMillis(timeout);
         return ret;
     }
 
     private long[] getTxnIds(Collection<LockInfoc) {
         long[] ret = new long[c.size()];
         Iterator<LockInfoiter = c.iterator();
         int i = 0;
         while (iter.hasNext()) {
             LockInfo info = iter.next();
             ret[i++] = info.getLocker().getId();
         }
 
         return ret;
     }

    
This method should always be called instead of explicitly creating TransactionTimeoutException, to ensure that je.lock.oldLockExceptions is enforced.
 
                                                          String msg) {
         return  ?
             new DeadlockException(lockermsg) :
             new TransactionTimeoutException(lockermsg);
     }

    
This method should always be called instead of explicitly creating LockTimeoutException, to ensure that je.lock.oldLockExceptions is enforced.
 
                                                           String msg) {
         return  ?
             new DeadlockException(lockermsg) :
             new LockTimeoutException(lockermsg);
     }

    
This method should always be called instead of explicitly creating LockNotAvailableException, to ensure that je.lock.oldLockExceptions is enforced.
 
                                                        String msg) {
         return  ?
             new LockNotGrantedException(lockermsg) :
             new LockNotAvailableException(lockermsg);
     }

    
Release a lock and possibly notify any waiters that they have been granted the lock.

Parameters:
lsn The LSN of the lock to release.
Returns:
true if the lock is released successfully, false if the lock is not currently being held.
 
     public boolean release(long lsnLocker locker)
         throws DatabaseException {
 
         synchronized (locker) {
             Set<LockernewOwners =
                 releaseAndFindNotifyTargets(lsnlocker);
 
             if (newOwners == null) {
                 return false;
             }
 
             if (newOwners.size() > 0) {
 
                 /*
                  * There is a new set of owners and/or there are restart
                  * waiters that should be notified.
                  */
                 Iterator<Lockeriter = newOwners.iterator();
 
                 while (iter.hasNext()) {
                     Locker lockerToNotify = iter.next();
 
                     /* Use notifyAll to support multiple threads per txn. */
                     synchronized (lockerToNotify) {
                         lockerToNotify.notifyAll();
                     }
 
                     assert EnvironmentImpl.maybeForceYield();
                 }
             }
 
             return true;
         }
     }

    
Release the lock, and return the set of new owners to notify, if any.

Returns:
null if the lock does not exist or the given locker was not the owner, a non-empty set if owners should be notified after releasing, an empty set if no notification is required.
 
     abstract Set<LockerreleaseAndFindNotifyTargets(long lsn,
                                                      Locker locker)
         throws DatabaseException;

    
Do the real work of releaseAndFindNotifyTargets
 
                                                     Locker locker,
                                                     int lockTableIndex) {
         Map<Long,LocklockTable = [lockTableIndex];
         Lock useLock = lockTable.get(lsn);
         if (useLock == null) {
             useLock = lockTable.get(Long.valueOf(lsn));
         }
 
         if (useLock == null) {
             /* Lock doesn't exist. */
             return null;
         }
 
         Set<LockerlockersToNotify =
             useLock.release(lockerlockTableIndex);
         if (lockersToNotify == null) {
             /* Not owner. */
             return null;
         }
 
         /* If it's not in use at all, remove it from the lock table. */
         if ((useLock.nWaiters() == 0) &&
             (useLock.nOwners() == 0)) {
             [lockTableIndex].remove(lsn);
             if (useLock.isThin()) {
                 .updateLockMemoryUsage
                     (lockTableIndex);
             } else {
                 .updateLockMemoryUsage
                     (lockTableIndex);
             }
         }
 
         return lockersToNotify;
     }

    
Demote a lock from write to read. Call back to the owning locker to move this to its read collection.

Parameters:
lock The lock to release. If null, use LSN to find lock
locker
 
     abstract void demote(long lsnLocker locker)
         throws DatabaseException;

    
Do the real work of demote.
 
     void demoteInternal(long lsnLocker lockerint lockTableIndex) {
         Map<Long,LocklockTable = [lockTableIndex];
         Lock useLock = lockTable.get(Long.valueOf(lsn));
         /* Lock may or may not be currently held. */
         if (useLock != null) {
             useLock.demote(locker);
             locker.moveWriteToReadLock(lsnuseLock);
         }
     }

    
Test the status of the lock on LSN. If any transaction holds any lock on it, true is returned. If no transaction holds a lock on it, false is returned. This method is only used by unit tests.

Parameters:
lsn The LSN to check.
Returns:
true if any transaction holds any lock on the LSN. false if no lock is held by any transaction.
 
     abstract boolean isLocked(Long lsn)
         throws DatabaseException;

    
Do the real work of isLocked.
 
     boolean isLockedInternal(Long lsnint lockTableIndex) {
 
         Map<Long,LocklockTable = [lockTableIndex];
         Lock entry = lockTable.get(lsn);
         if (entry == null) {
             return false;
         }
 
         return entry.nOwners() != 0;
     }

    
Return true if this locker owns this a lock of this type on given node. This method is only used by unit tests.
 
     abstract boolean isOwner(Long lsnLocker lockerLockType type)
         throws DatabaseException;

    
Do the real work of isOwner.
 
     boolean isOwnerInternal(Long lsn,
                             Locker locker,
                             LockType type,
                             int lockTableIndex) {
 
         Map<Long,LocklockTable = [lockTableIndex];
         Lock entry = lockTable.get(lsn);
         if (entry == null) {
             return false;
         }
 
         return entry.isOwner(lockertype);
     }

    
Return true if this locker is waiting on this lock. This method is only used by unit tests.
 
     abstract boolean isWaiter(Long lsnLocker locker)
         throws DatabaseException;

    
Do the real work of isWaiter.
 
     boolean isWaiterInternal(Long lsn,
                              Locker locker,
                              int lockTableIndex) {
 
         Map<Long,LocklockTable = [lockTableIndex];
         Lock entry = lockTable.get(lsn);
         if (entry == null) {
             return false;
         }
 
         return entry.isWaiter(locker);
     }

    
Return the number of waiters for this lock.
 
     abstract int nWaiters(Long lsn)
         throws DatabaseException;

    
Do the real work of nWaiters.
 
     int nWaitersInternal(Long lsnint lockTableIndex) {
 
         Map<Long,LocklockTable = [lockTableIndex];
         Lock entry = lockTable.get(lsn);
         if (entry == null) {
             return -1;
         }
 
         return entry.nWaiters();
     }

    
Return the number of owners of this lock.
 
     abstract int nOwners(Long lsn)
         throws DatabaseException;

    
Do the real work of nWaiters.
 
     int nOwnersInternal(Long lsnint lockTableIndex) {
 
         Map<Long,LocklockTable = [lockTableIndex];
         Lock entry = lockTable.get(lsn);
         if (entry == null) {
             return -1;
         }
 
         return entry.nOwners();
     }

    

Returns:
the transaction that owns the write lock for this
 
     abstract Locker getWriteOwnerLocker(Long lsn)
         throws DatabaseException;

    
Do the real work of getWriteOwnerLocker.
 
     Locker getWriteOwnerLockerInternal(Long lsnint lockTableIndex) {
         Map<Long,LocklockTable = [lockTableIndex];
         Lock lock = lockTable.get(lsn);
         if (lock == null) {
             return null;
         } else if (lock.nOwners() > 1) {
             /* not a write lock */
             return null;
         } else {
             return lock.getWriteOwnerLocker();
         }
     }
 
     /*
      * Check if we got ownership while we were waiting.  If we didn't get
      * ownership, and we timed out, remove this locker from the set of
      * waiters. Do this in a critical section to prevent any orphaning of the
      * lock -- we must be in a critical section between the time that we check
      * ownership and when we flush any waiters (SR #10103)
      * @return true if you are the owner.
      */
     abstract boolean validateOwnership(Long lsn,
                                        Locker locker,
                                        LockType type,
                                        boolean flushFromWaiters,
                                        MemoryBudget mb)
         throws DatabaseException;
 
     /*
      * Do the real work of validateOwnershipInternal.
      */
     boolean validateOwnershipInternal(Long lsn,
                                       Locker locker,
                                       LockType type,
                                       boolean flushFromWaiters,
                                       MemoryBudget mb,
                                       int lockTableIndex) {
         if (isOwnerInternal(lsnlockertypelockTableIndex)) {
             return true;
         }
 
         if (flushFromWaiters) {
             Lock entry = [lockTableIndex].get(lsn);
             if (entry != null) {
                 entry.flushWaiter(lockermblockTableIndex);
             }
         }
         return false;
     }
 
     abstract protected LockAttemptResult stealLock(Long lsn,
                                                    Locker locker,
                                                    LockType lockType,
                                                    MemoryBudget mb)
         throws DatabaseException;
 
     protected LockAttemptResult stealLockInternal(Long lsn,
                                                   Locker locker,
                                                   LockType lockType,
                                                   MemoryBudget mb,
                                                   int lockTableIndex)
         throws DatabaseException {
 
         Lock entry = [lockTableIndex].get(lsn);
         assert entry != null;
 
         /*
          * Note that flushWaiter may do nothing, because the lock may have been
          * granted to our locker after the prior call to attemptLock and before
          * the call to this method.
          */
         entry.flushWaiter(lockermblockTableIndex);
 
         /* Remove all owners except for our owner. */
         entry.stealLock(lockermblockTableIndex);
        /*
         * The lock attempt normally succeeds, but can fail if the lock holder
         * is non-preemptable.
         */
        return attemptLockInternal
            (lsnlockerlockTypefalse /*nonBlockingRequest*/,
             false /*jumpAheadOfWaiters*/lockTableIndex);
    }

    
Called when a ThreadLocker is created.
    public void registerThreadLocker(final ThreadLocker locker) {
        if ( == null) {
            return;
        }
        final Thread thread = Thread.currentThread();
        final TinyHashSet<ThreadLockerset = .get(thread);
        if (set != null) {
            final boolean added = set.add(locker);
            assert added;
        } else {
            .put(threadnew TinyHashSet(locker));
        }
    }

    
Called when a ThreadLocker is closed.
    public void unregisterThreadLocker(final ThreadLocker locker) {
        if ( == null) {
            return;
        }
        final Thread thread = Thread.currentThread();
        final TinyHashSet<ThreadLockerset = .get(thread);
        assert set != null;
        final boolean removed = set.remove(locker);
        assert removed;
        if (.size() == 0) {
            .remove(thread);
        }
    }

    
Returns an iterator over all thread lockers for the given thread, or an empty iterator if none.
    public Iterator<ThreadLockergetThreadLockers(final Thread thread) {
        if ( == null) {
            return .iterator();
        }
        final TinyHashSet<ThreadLockerset = .get(thread);
        if (set == null) {
            return .iterator();
        }
        return set.iterator();
    }

    
Statistics
    public LockStats lockStat(StatsConfig config)
        throws DatabaseException {
        StatGroup latchStats = new StatGroup("Locktable latches"
                                             "Shows lock table contention");
        for (int i = 0; i < i++) {
            latchStats.addAll([i].getLatchStats());
        }
        /* Dump info about the lock table. */
        StatGroup tableStats = 
            new StatGroup("Locktable",
                          "The types of locks held in the lock table");
        if (!config.getFast()) {
            dumpLockTable(tableStatsfalse /*clear*/);
        }
        
        return new LockStats(.cloneGroup(config.getClear()),
                             latchStats.cloneGroup(config.getClear()),
                             tableStats.cloneGroup(config.getClear()));
    }
    public StatGroup loadStats(StatsConfig config) {
        StatGroup copyStats = .cloneGroup(config.getClear());
        StatGroup latchStats = new StatGroup("Locktable latches"
                                             "Shows lock table contention");
        for (int i = 0; i < i++) {
            latchStats.addAll([i].getLatchStats());
            if (config.getClear()) {
                [i].clear();
            }
        }
        /* Add all the latch stats to the whole stats group. */
        copyStats.addAll(latchStats);
        StatGroup tableStats = 
            new StatGroup("Locktable",
                          "The types of locks held in the lock table");
        if (!config.getFast()) {
            dumpLockTable(tableStatsconfig.getClear());
        }
        /* Add all the lock table stats to the whole stats group. */
        copyStats.addAll(tableStats);
        
        return copyStats;
    }

    
Dump the lock table to the lock stats.
    abstract void dumpLockTable(StatGroup tableStatsboolean clear)
        throws DatabaseException;

    
Do the real work of dumpLockTableInternal.
    void dumpLockTableInternal(StatGroup tableStatsint iboolean clear) {
        StatGroup oneTable = new StatGroup("Single lock table",
                                           "Temporary stat group");
        IntStat totalLocks = new IntStat(oneTable);
        IntStat waiters = new IntStat(oneTable);
        IntStat owners = new IntStat(oneTable);
        IntStat readLocks = new IntStat(oneTable);
        IntStat writeLocks = new IntStat(oneTable);
        Map<LongLocklockTable = [i];
        totalLocks.add(lockTable.size());
        for (Lock lock : lockTable.values()) {
            waiters.add(lock.nWaiters());
            owners.add(lock.nOwners());
            /* Go through all the owners for a lock. */
            for (LockInfo info : lock.getOwnersClone()) {
                if (info.getLockType().isWriteLock()) {
                    writeLocks.increment();
                } else {
                    readLocks.increment();
                }
            }
        }
        tableStats.addAll(oneTable);
    }

    
Debugging
    public void dump()
        throws DatabaseException {
        ..println(dumpToString());
    }
    public String dumpToString()
        throws DatabaseException {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < i++) {
            [i].acquire();
            try {
                dumpToStringNoLatch(sbi);
            } finally {
                [i].release();
            }
        }
        return sb.toString();
    }
    private void dumpToStringNoLatch(StringBuilder sbint whichTable) {
        Map<Long,LocklockTable = [whichTable];
        Iterator<Map.Entry<Long,Lock>> entries =
            lockTable.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry<Long,Lockentry = entries.next();
            Long lsn = entry.getKey();
            Lock lock = entry.getValue();
            sb.append("---- LSN: ").
               append(DbLsn.getNoFormatString(lsn)).
               append("----\n");
            sb.append(lock);
            sb.append('\n');
        }
    }
    private boolean checkNoLatchesHeld(boolean nonBlockingRequest) {
        if (nonBlockingRequest) {
            return true// don't check if it's a non blocking request.
        } else {
            return (LatchSupport.countLatchesHeld() == 0);
        }
    }
    private StringBuilder findDeadlock(Lock lockLocker rootLocker) {
        Set<LockerownerSet = new HashSet<Locker>();
        ownerSet.add(rootLocker);
        StringBuilder ret = findDeadlock1(ownerSetlockrootLocker);
        if (ret != null) {
            return ret;
        } else {
            return null;
        }
    }
    private StringBuilder findDeadlock1(Set<LockerownerSet,
                                       Lock lock,
                                       Locker rootLocker) {
        Iterator<LockInfoownerIter = lock.getOwnersClone().iterator();
        while (ownerIter.hasNext()) {
            LockInfo info = ownerIter.next();
            Locker locker = info.getLocker();
            Lock waitsFor = locker.getWaitingFor();