Package com.sun.sgs.impl.kernel

Source Code of com.sun.sgs.impl.kernel.LockingAccessCoordinator

/*
* Copyright 2007-2009 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.sun.sgs.impl.kernel;

import com.sun.sgs.app.TransactionAbortedException;
import com.sun.sgs.app.TransactionConflictException;
import com.sun.sgs.app.TransactionTimeoutException;
import com.sun.sgs.impl.profile.ProfileCollectorHandle;
import com.sun.sgs.impl.service.transaction.TransactionCoordinator;
import com.sun.sgs.impl.service.transaction.TransactionCoordinatorImpl;
import com.sun.sgs.impl.sharedutil.LoggerWrapper;
import static com.sun.sgs.impl.sharedutil.Objects.checkNull;
import com.sun.sgs.impl.sharedutil.PropertiesWrapper;
import com.sun.sgs.kernel.AccessCoordinator;
import com.sun.sgs.kernel.AccessReporter;
import com.sun.sgs.kernel.AccessReporter.AccessType;
import com.sun.sgs.kernel.AccessedObject;
import com.sun.sgs.profile.AccessedObjectsDetail;
import com.sun.sgs.profile.AccessedObjectsDetail.ConflictType;
import com.sun.sgs.service.Transaction;
import com.sun.sgs.service.TransactionInterruptedException;
import com.sun.sgs.service.TransactionListener;
import com.sun.sgs.service.TransactionProxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;


/*
* TODO: Consider creating a base class that provides the lock and
* lockNoWaitInternal level methods as a common utility.
* -tjb@sun.com (03/10/2009)
*/

/**
* An implementation of {@link AccessCoordinator} that uses locking to handle
* conflicts. <p>
*
* This implementation checks for deadlock whenever an access request is
* blocked due to a conflict.  It selects the youngest transaction as the
* deadlock victim, determining the age using the originally requested start
* time for the task associated with the transaction.  The implementation does
* not deny requests that would not result in deadlock.  When requests block,
* it services the requests in the order that they arrive, except for upgrade
* requests, which it puts ahead of non-upgrade requests.  The justification
* for special treatment of upgrade requests is that an upgrade request is
* useless if a conflicting request goes first and causes the waiter to lose
* its read lock. <p>
*
* The methods that this class provides to implement {@code AccessReporter} are
* not thread safe, and should either be called from a single thread or else
* protected with external synchronization. <p>
*
* The {@link #LockingAccessCoordinator constructor} supports the following
* configuration properties: <p>
*
* <dl style="margin-left: 1em">
*
* <dt> <i>Property:</i> <b>{@value #LOCK_TIMEOUT_PROPERTY}</b> <br>
<i>Default:</i> {@value #DEFAULT_LOCK_TIMEOUT_PROPORTION} times the
*  value of the {@code com.sun.sgs.txn.timeout} property, if specified,
*  otherwise times the value of the default transaction timeout.
*
* <dd style="padding-top: .5em">The maximum number of milliseconds to wait for
*  obtaining a lock.  The value must be greater than {@code 0}, and should
*  be less than the transaction timeout. <p>
*
* <dt> <i>Property:</i> <b>{@value #NUM_KEY_MAPS_PROPERTY}</b> <br>
<i>Default:</i> {@value #NUM_KEY_MAPS_DEFAULT}
*
* <dd style="padding-top: .5em">The number of maps to use for associating keys
*  and maps.  The number of maps controls the amount of concurrency, and
*  should typically be set to a value to support concurrent access by the
*  number of active threads.  The value must be greater than {@code
*  0}. <p>
*
* </dl> <p>
*
* This class uses the {@link Logger} named {@code
* com.sun.sgs.impl.kernel.LockingAccessCoordinator} to log information at the
* following logging levels: <p>
*
* <ul>
* <li> {@link Level#FINER FINER} - Beginning and ending transactions;
*  requesting, waiting for, and returning from lock requests; deadlocks
* <li> {@link Level#FINEST FINEST} - Notifying new lock owners, results of
*  requesting locks before waiting, releasing locks, results of attempting
*  to assign locks to waiters, details of checking deadlocks
* </ul> <p>
*
* The implementation of this class uses the following thread synchronization
* scheme to avoid internal deadlocks:
*
* <ul>
*
* <li>Synchronization is only used on {@link Locker} objects and on the {@code
*     Map}s that hold {@code Lock} objects
*
* <li>A thread can synchronize on at most one locker and one lock at a time,
*     always synchronizing on the locker first
*
* </ul>
*
* To make it easier to adhere to these rules, the implementation takes the
* following steps:
*
* <ul>
*
* <li>The {@code Lock} class is not synchronized <p>
*
*     Callers of non-{@code Object} methods on the {@code Lock} class should
*     make sure that they are synchronized on the associated key map.
*
* <li>The {@link Locker} class only uses synchronization for getter and setter
*     methods
*
* <li>Blocks synchronized on a {@code Lock} should not synchronize on anything
*     else <p>
*
*     The code enforces this requirement by having lock methods not make calls
*     to other classes, and by performing minimal work while synchronized on
*     the associated key map.
*
* <li>Blocks synchronized on a {@code Locker} should not synchronize on a
*     different locker, but can synchronize on a {@code Lock}
*
*     In fact, only one method synchronizes on a {@code Locker} and on a
*     {@code Lock} -- the {@link #waitForLockInternal waitForLockInternal}
*     method.  That method also makes sure that the only synchronized {@code
*     Locker} methods that it calls are on the locker it has already
*     synchronized on.  The {@code DeadlockChecker} class also synchronizes on
*     key maps and lockers, but is called outside of synchronized blocks and
*     only synchronizes on one thing at a time.
*
* <li>Use assertions to check adherence to the scheme
*
* </ul>
*/
public class LockingAccessCoordinator extends AbstractAccessCoordinator {

    /** The class name. */
    private static final String CLASS =
  "com.sun.sgs.impl.kernel.LockingAccessCoordinator";

    /**
     * The property for specifying the maximum number of milliseconds to wait
     * for obtaining a lock.
     */
    public static final String LOCK_TIMEOUT_PROPERTY =
  CLASS + ".lock.timeout";

    /**
     * The proportion of the transaction timeout to use for the lock timeout if
     * no lock timeout is specified.
     */
    public static final double DEFAULT_LOCK_TIMEOUT_PROPORTION = 0.1;

    /**
     * The default value of the lock timeout property, if no transaction
     * timeout is specified.
     */
    public static final long DEFAULT_LOCK_TIMEOUT =
  computeLockTimeout(TransactionCoordinatorImpl.BOUNDED_TIMEOUT_DEFAULT);

    /**
     * The property for specifying the number of maps to use for associating
     * keys and maps.  The number of maps controls the amount of concurrency.
     */
    public static final String NUM_KEY_MAPS_PROPERTY =
  CLASS + ".num.key.maps";

    /** The default number of key maps. */
    public static final int NUM_KEY_MAPS_DEFAULT = 8;

    /** The logger for this class. */
    static final LoggerWrapper logger = new LoggerWrapper(
  Logger.getLogger(LockingAccessCoordinator.class.getName()));

    /** Maps transactions to lockers. */
    private final ConcurrentMap<Transaction, Locker> txnMap =
  new ConcurrentHashMap<Transaction, Locker>();

    /**
     * The maximum number of milliseconds to spend attempting to acquire a
     * lock.
     */
    private final long lockTimeout;

    /**
     * The number of separate maps to use for storing keys in order to support
     * concurrent access.
     */
    private final int numKeyMaps;

    /**
     * An array of maps from key to lock.  The map to use is chosen by using
     * the key's hash code mod the number of key maps.  Synchronization for
     * locks is based on locking the associated key map.  Non-{@code Object}
     * methods on locks should not be used without synchronizing on the
     * associated key map lock.
     */
    private final Map<Key, Lock>[] keyMaps;

    /* -- Public constructor -- */

    /**
     * Creates an instance of this class.
     *
     * @param  properties the configuration properties
     * @param  txnProxy the transaction proxy
     * @param  profileCollectorHandle the profile collector handle
     * @throws  IllegalArgumentException if the values of the configuration
     *    properties are illegal
     */
    public LockingAccessCoordinator(
  Properties properties,
  TransactionProxy txnProxy,
  ProfileCollectorHandle profileCollectorHandle)
    {
  super(txnProxy, profileCollectorHandle);
  PropertiesWrapper wrappedProps = new PropertiesWrapper(properties);
  long txnTimeout = wrappedProps.getLongProperty(
      TransactionCoordinator.TXN_TIMEOUT_PROPERTY, -1);
  long defaultLockTimeout = (txnTimeout < 1)
      ? DEFAULT_LOCK_TIMEOUT : computeLockTimeout(txnTimeout);
  lockTimeout = wrappedProps.getLongProperty(
      LOCK_TIMEOUT_PROPERTY, defaultLockTimeout, 1, Long.MAX_VALUE);
  numKeyMaps = wrappedProps.getIntProperty(
      NUM_KEY_MAPS_PROPERTY, NUM_KEY_MAPS_DEFAULT, 1, Integer.MAX_VALUE);
  keyMaps = createKeyMaps(numKeyMaps);
  for (int i = 0; i < numKeyMaps; i++) {
      keyMaps[i] = new HashMap<Key, Lock>();
  }
    }

    /* -- Implement AccessCoordinator -- */

    /** {@inheritDoc} */
    public <T> AccessReporter<T> registerAccessSource(
  String sourceName, Class<T> objectIdType)
    {
  checkNull("objectIdType", objectIdType);
  return new AccessReporterImpl<T>(sourceName);
    }

    /**
     * {@inheritDoc} <p>
     *
     * This implementation does not record information about completed
     * transactions, so it always returns {@code null}.
     */
    public Transaction getConflictingTransaction(Transaction txn) {
  checkNull("txn", txn);
  return null;
    }

    /* -- Implement AccessCoordinatorHandle -- */

    /** {@inheritDoc} */
    public void notifyNewTransaction(
  Transaction txn, long requestedStartTime, int tryCount)
    {
  if (tryCount < 1) {
      throw new IllegalArgumentException(
    "The tryCount must not be less than 1");
  }
  Locker locker = new Locker(txn, requestedStartTime);
  Locker existing = txnMap.putIfAbsent(txn, locker);
  if (existing != null) {
      throw new IllegalStateException("Transaction already started");
  }
  if (logger.isLoggable(Level.FINER)) {
      logger.log(Level.FINER,
           "begin {0}, requestedStartTime:{1,number,#}",
           locker, requestedStartTime);
  }
  txn.registerListener(new TxnListener(txn));
    }

    /* -- Other public methods -- */

    /**
     * Attempts to acquire a lock, waiting if needed.  Returns information
     * about conflicts that occurred while attempting to acquire the lock that
     * prevented the lock from being acquired, or else {@code null} if the lock
     * was acquired.  If the {@code type} field of the return value is {@link
     * LockConflictType#DEADLOCK DEADLOCK}, then the caller should abort the
     * transaction, and any subsequent lock or wait requests will throw {@code
     * IllegalStateException}.
     *
     * @param  txn the transaction requesting the lock
     * @param  source the source of the object
     * @param  objectId the ID of the object
     * @param  forWrite whether to request a write lock
     * @param  description a description of the object being accessed, or
     *    {@code null}
     * @return  lock conflict information, or {@code null} if there was no
     *    conflict
     * @throws  IllegalArgumentException if {@link #notifyNewTransaction
     *    notifyNewTransaction} has not been called for {@code txn}
     * @throws  IllegalStateException if an earlier lock attempt for this
     *    transaction produced a deadlock, or if still waiting for an
     *    earlier attempt to complete
     */
    public LockConflict lock(Transaction txn,
           String source,
           Object objectId,
           boolean forWrite,
           Object description)
    {
  Locker locker = getLocker(txn);
  checkLockerNotAborted(locker);
  LockConflict conflict = lockNoWaitInternal(
      locker, new Key(source, objectId), forWrite, description);
  return (conflict == null) ? null : waitForLockInternal(locker);
    }

    /**
     * Attempts to acquire a lock, returning immediately.  Returns information
     * about any conflict that occurred while attempting to acquire the lock,
     * or else {@code null} if the lock was acquired.  If the attempt to
     * acquire the lock was blocked, returns a value with a {@code type} field
     * of {@link LockConflictType#BLOCKED BLOCKED} rather than waiting.  If the
     * {@code type} field of the return value is {@link
     * LockConflictType#DEADLOCK DEADLOCK}, then the caller should abort the
     * transaction, and any subsequent lock or wait requests will throw {@code
     * IllegalStateException}.
     *
     * @param  txn the transaction requesting the lock
     * @param  source the source of the object
     * @param  objectId the ID of the object
     * @param  forWrite whether to request a write lock
     * @param  description a description of the object being accessed, or
     *    {@code null}
     * @return  lock conflict information, or {@code null} if there was no
     *    conflict
     * @throws  IllegalArgumentException if {@link #notifyNewTransaction
     *    notifyNewTransaction} has not been called for {@code txn}
     * @throws  IllegalStateException if an earlier lock attempt for this
     *    transaction produced a deadlock, or if still waiting for an
     *    earlier attempt to complete
     */
    public LockConflict lockNoWait(Transaction txn,
           String source,
           Object objectId,
           boolean forWrite,
           Object description)
    {
  Locker locker = getLocker(txn);
  checkLockerNotAborted(locker);
  return lockNoWaitInternal(
      locker, new Key(source, objectId), forWrite, description);
    }

    /**
     * Waits for a previous attempt to obtain a lock that blocked.  Returns
     * information about any conflict that occurred while attempting to acquire
     * the lock, or else {@code null} if the lock was acquired or the
     * transaction was not waiting.  If the {@code type} field of the return
     * value is {@link LockConflictType#DEADLOCK DEADLOCK}, then the caller
     * should abort the transaction, and any subsequent lock or wait requests
     * will throw {@code IllegalStateException}.
     *
     * @param  txn the transaction requesting the lock
     * @return  lock conflict information, or {@code null} if there was no
     *    conflict
     * @throws  IllegalArgumentException if {@link #notifyNewTransaction
     *    notifyNewTransaction} has not been called for {@code txn}
     * @throws  IllegalStateException if an earlier lock attempt for this
     *    transaction produced a deadlock
     */
    public LockConflict waitForLock(Transaction txn) {
  Locker locker = getLocker(txn);
  checkLockerNotAborted(locker);
  return waitForLockInternal(locker);
    }

    /* -- Public classes -- */

    /** A class for representing a conflict resulting from a lock request. */
    public static final class LockConflict {

  /** The type of conflict. */
  final LockConflictType type;

  /** A transaction that caused the conflict. */
  final Transaction conflictingTxn;

  /**
   * Creates an instance of this class.
   *
   * @param  type the type of conflict
   * @param  conflictingTxn a transaction that caused the conflict
   */
  public LockConflict(
      LockConflictType type, Transaction conflictingTxn)
  {
      assert type != null;
      assert conflictingTxn != null;
      this.type = type;
      this.conflictingTxn = conflictingTxn;
  }

  /**
   * Returns the type of conflict.
   *
   * @return  the type of conflict
   */
  public LockConflictType getType() {
      return type;
  }

  /**
   * Returns a transaction that caused the conflict.
   *
   * @return  a transaction that caused the conflict
   */
  public Transaction getConflictingTransaction() {
      return conflictingTxn;
  }

  /**
   * Returns a string representation of this instance, for debugging.
   *
   * @return  a string representation of this instance
   */
  @Override
  public String toString() {
      return "LockConflict[type:" + type +
    ", conflict:" + conflictingTxn + "]";
  }
    }

    /** The type of a lock conflict. */
    public enum LockConflictType {

  /** The request is currently blocked. */
  BLOCKED,

  /** The request timed out. */
  TIMEOUT,

  /** The request was denied. */
  DENIED,

  /** The request was interrupted. */
  INTERRUPTED,

  /** The request resulted in deadlock and was chosen to be aborted. */
  DEADLOCK;
    }

    /* -- Other methods -- */

    /**
     * Returns the locker associated with a transaction.
     *
     * @param  txn the transaction
     * @return  the locker
     * @throws  IllegalArgumentException if the transaction is not active
     */
    Locker getLocker(Transaction txn) {
  checkNull("txn", txn);
  Locker locker = txnMap.get(txn);
  if (locker == null) {
      throw new IllegalArgumentException(
    "Transaction not active: " + txn);
  }
  return locker;
    }

    /**
     * Returns the key map to use for the specified key.
     *
     * @param  key the key
     * @return  the associated key map
     */
    Map<Key, Lock> getKeyMap(Key key) {
  /* Mask off the sign bit to get a positive value */
  int index = (key.hashCode() & Integer.MAX_VALUE) % numKeyMaps;
  return keyMaps[index];
    }

    /**
     * Returns the lock associated with the specified key from the key map,
     * which should be the one returned by calling {@link #getKeyMap
     * getKeyMap}.  The lock on {@code keyMap} should be held.
     *
     * @param  key the key
     * @param  keyMap the keyMap
     * @return  the associated lock
     */
    static Lock getLock(Key key, Map<Key, Lock> keyMap) {
  assert Thread.holdsLock(keyMap);
  Lock lock = keyMap.get(key);
  if (lock == null) {
      lock = new Lock(key);
      keyMap.put(key, lock);
  }
  return lock;
    }

    /** Creates the key maps array. */
    @SuppressWarnings("unchecked")
    private Map<Key, Lock>[] createKeyMaps(int n) {
  return new Map[n];
    }

    /** Checks if the locker has been requested to abort. */
    private static void checkLockerNotAborted(Locker locker) {
  LockConflict lockConflict = locker.getConflict();
  if (lockConflict != null &&
      lockConflict.type == LockConflictType.DEADLOCK)
  {
      throw new IllegalStateException(
    "Transaction must abort: " + locker.txn);
  }
    }

    /**
     * Releases the locks for the transaction and reports object accesses to
     * the profiling system.
     *
     * @param  txn the finished transaction
     */
    private void endTransaction(Transaction txn) {
  Locker locker = getLocker(txn);
  logger.log(Level.FINER, "end {0}", locker);
  for (LockRequest request : locker.requests) {
      List<Locker> newOwners = Collections.emptyList();
      Key key = request.key;
      Map<Key, Lock> keyMap = getKeyMap(key);
      assert Lock.noteSync(key);
      try {
    synchronized (keyMap) {
        /* Don't create the lock if it isn't present */
        Lock lock = keyMap.get(key);
        if (lock != null) {
      newOwners = lock.release(locker);
      if (!lock.inUse()) {
          keyMap.remove(key);
      }
        }
    }
      } finally {
    assert Lock.noteUnsync(key);
      }
      for (Locker newOwner : newOwners) {
    logger.log(Level.FINEST, "notify new owner {0}", newOwner);
    assert newOwner.noteSync();
    try {
        synchronized (newOwner) {
      newOwner.notify();
        }
    } finally {
        assert newOwner.noteUnsync();
    }
      }
  }
  txnMap.remove(txn);
  profileCollectorHandle.setAccessedObjectsDetail(locker);
    }

    /** Attempts to acquire a lock, returning immediately. */
    private LockConflict lockNoWaitInternal(
  Locker locker, Key key, boolean forWrite, Object description)
    {
  if (locker.getWaitingFor() != null) {
      throw new IllegalStateException(
    "Attempt to obtain a new lock while waiting");
  }
  LockConflict conflict = locker.getConflict();
  if (conflict != null) {
      if (conflict.type == LockConflictType.DEADLOCK) {
    throw new IllegalStateException(
        "Attempt to obtain a new lock after a deadlock");
      } else {
    /* Ignoring the previous conflict */
    locker.setConflict(null);
      }
  }
  if (description != null) {
      locker.setDescription(key, description);
  }
  LockAttemptResult result;
  Map<Key, Lock> keyMap = getKeyMap(key);
  assert Lock.noteSync(key);
  try {
      synchronized (keyMap) {
    Lock lock = getLock(key, keyMap);
    result = lock.lock(locker, forWrite, false);
      }
  } finally {
      assert Lock.noteUnsync(key);
  }
  if (result == null) {
      if (logger.isLoggable(Level.FINER)) {
    logger.log(Level.FINER,
         "lock {0}, {1}, forWrite:{2}" +
         "\n  returns null (already granted)",
         locker, key, forWrite);
      }
      return null;
  }
  locker.requests.add(result.request);
  if (result.conflict == null) {
      if (logger.isLoggable(Level.FINER)) {
    logger.log(Level.FINER,
         "lock {0}, {1}, forWrite:{2}" +
         "\n  returns null (granted)",
         locker, key, forWrite);
      }
      return null;
  }
  if (logger.isLoggable(Level.FINEST)) {
      logger.log(Level.FINEST,
           "lock attempt {0}, {1}, forWrite:{2}" +
           "\n  returns blocked",
           locker, key, forWrite);
  }
  locker.setWaitingFor(result);
  conflict = new DeadlockChecker(locker).check();
  if (conflict == null) {
      conflict = new LockConflict(
    LockConflictType.BLOCKED, result.conflict.txn);
  }
  if (logger.isLoggable(Level.FINER)) {
      logger.log(Level.FINER,
           "lock {0}, {1}, forWrite:{2}\n  returns {3}",
      locker, key, forWrite, conflict);
  }
  return conflict;
    }

    /** Attempts to acquire a lock, waiting if needed. */
    private LockConflict waitForLockInternal(Locker locker) {
  assert locker.noteSync();
  try {
      synchronized (locker) {
    LockAttemptResult result = locker.getWaitingFor();
    if (result == null) {
        logger.log(
      Level.FINER,
      "lock {0}\n  returns null (not waiting)", locker);
        return null;
    }
    Lock lock;
    Key key = result.request.key;
    Map<Key, Lock> keyMap = getKeyMap(key);
    assert Lock.noteSync(key);
    try {
        synchronized (keyMap) {
      lock = getLock(key, keyMap);
        }
    } finally {
        assert Lock.noteUnsync(key);
    }
    long now = System.currentTimeMillis();
    long stop = Math.min(addCheckOverflow(now, lockTimeout),
             locker.stopTime);
    LockConflict conflict = null;
    while (true) {
        conflict = locker.getConflict();
        if (conflict != null) {
      break;
        } else if (now >= stop) {
      conflict = new LockConflict(
          LockConflictType.TIMEOUT, result.conflict.txn);
      locker.setConflict(conflict);
      break;
        }
        boolean isOwner;
        assert Lock.noteSync(key);
        try {
      synchronized (keyMap) {
          isOwner = lock.isOwner(result.request);
      }
        } finally {
      assert Lock.noteUnsync(key);
        }
        if (isOwner) {
      locker.setWaitingFor(null);
      if (logger.isLoggable(Level.FINER)) {
          logger.log(
        Level.FINER,
        "lock {0}, {1}, forWrite:{2}" +
        "\n  returns null (granted)",
        locker, key, result.request.getForWrite());
      }
      return null;
        }
        if (logger.isLoggable(Level.FINER)) {
      logger.log(Level.FINER,
           "wait for lock {0}, {1}, forWrite:{2}" +
           ", wait:{3,number,#}",
           locker, key, result.request.getForWrite(),
           stop - now);
        }
        try {
      locker.wait(stop - now);
        } catch (InterruptedException e) {
      conflict = new LockConflict(
          LockConflictType.INTERRUPTED, result.conflict.txn);
      locker.setConflict(conflict);
      break;
        }
        now = System.currentTimeMillis();
    }
    assert conflict != null;
    assert Lock.noteSync(key);
    try {
        synchronized (keyMap) {
      lock.flushWaiter(locker);
        }
    } finally {
        assert Lock.noteUnsync(key);
    }
    locker.setWaitingFor(null);
    if (logger.isLoggable(Level.FINER)) {
        logger.log(Level.FINER,
             "lock {0}, {1}, forWrite:{2}\n  returns {3}",
             locker, key, result.request.getForWrite(),
             conflict);
    }
    return conflict;
      }
  } finally {
      assert locker.noteUnsync();
  }
    }

    /**
     * Computes the lock timeout based on the specified transaction timeout and
     * {@link #DEFAULT_LOCK_TIMEOUT_PROPORTION}.
     */
    private static long computeLockTimeout(long txnTimeout) {
  long result = (long) (txnTimeout * DEFAULT_LOCK_TIMEOUT_PROPORTION);
  /* Lock timeout should be at least 1 */
  if (result < 1) {
      result = 1;
  }
  return result;
    }

    /** Add two non-negative longs and don't wrap around on overflow. */
    static long addCheckOverflow(long x, long y) {
  assert x >= 0 && y >= 0;
  long result = x + y;
  return (result >= 0) ? result : Long.MAX_VALUE;
    }

    /* -- Other classes -- */

    /** Records information about a transaction requesting locks. */
    static class Locker implements AccessedObjectsDetail {

  /**
   * When assertions are enabled, holds the {@code Locker} that the
   * current thread is synchronized on, if any.
   */
  private static final ThreadLocal<Locker> currentSync =
      new ThreadLocal<Locker>();

  /** The transaction. */
  final Transaction txn;

  /**
   * The time in milliseconds when the task associated with this
   * transaction was originally requested to start.
   */
  final long requestedStartTime;

  /** The time in milliseconds when this transaction times out. */
  final long stopTime;

  /** The lock requests made by this transaction. */
  final List<LockRequest> requests = new ArrayList<LockRequest>();

  /** A map from keys to descriptions, or {@code null}. */
  private Map<Key, Object> keyToDescriptionMap = null;

  /**
   * The result of the lock request that this transaction is waiting for,
   * or {@code null} if it is not waiting.  Synchronize on this locker
   * when accessing this field.
   */
  private LockAttemptResult waitingFor;

  /**
   * A conflict that should cause this transaction's request to be
   * denied, or {@code null}.  This value is cleared after the conflict
   * has been reported unless the conflict is a deadlock.  Synchronize on
   * this locker when accessing this field.
   */
  private LockConflict conflict;

  /**
   * Creates an instance of this class.
   *
   * @param  txn the associated transaction
   * @param  requestedStartTime the time milliseconds that the task
   *    associated with the transaction was originally
   *    requested to start
   * @throws  IllegalArgumentException if {@code requestedStartTime}
   *    is less than {@code 0}
   */
  Locker(Transaction txn, long requestedStartTime) {
      checkNull("txn", txn);
      if (requestedStartTime < 0) {
    throw new IllegalArgumentException(
        "The requestedStartTime must not be less than 0");
      }
      this.txn = txn;
      this.requestedStartTime = requestedStartTime;
      this.stopTime = addCheckOverflow(
    System.currentTimeMillis(), txn.getTimeout());
  }

  /* -- Implement AccessedObjectsDetail -- */

  /** {@inheritDoc} */
  public List<? extends AccessedObject> getAccessedObjects() {
      return Collections.unmodifiableList(requests);
  }

  /** {@inheritDoc} */
  public ConflictType getConflictType() {
      if (conflict == null) {
    return ConflictType.NONE;
      } else if (conflict.type == LockConflictType.DEADLOCK) {
    return ConflictType.DEADLOCK;
      } else {
    return ConflictType.ACCESS_NOT_GRANTED;
      }
  }

  /** {@inheritDoc} */
  public byte[] getConflictingId() {
      return (conflict != null)
    ? conflict.getConflictingTransaction().getId() : null;
  }

  /* -- Other methods -- */

  /**
   * Sets the description associated with a key for this locker.  The
   * description should not be {@code null}.  Does not replace an
   * existing description.
   *
   * @param  key the key
   * @param  description the description
   */
  void setDescription(Key key, Object description) {
      assert key != null;
      assert description != null;
      if (keyToDescriptionMap == null) {
    keyToDescriptionMap = new HashMap<Key, Object>();
      }
      if (!keyToDescriptionMap.containsKey(key)) {
    keyToDescriptionMap.put(key, description);
      }
  }

  /**
   * Gets the description associated with a key for this locker.
   *
   * @param  key the key
   * @return  the description or {@code null}
   */
  Object getDescription(Key key) {
      return (keyToDescriptionMap == null)
    ? null : keyToDescriptionMap.get(key);
  }

  /**
   * Checks if there is a conflict that should cause this locker's
   * request to be denied.  This value can be cleared to permit a new
   * request unless the conflict is a deadlock.
   *
   * @return  the conflicting request or {@code null}
   */
  synchronized LockConflict getConflict() {
      assert checkAllowSync();
      return conflict;
  }

  /**
   * Requests that this locker request be denied because of a conflict
   * with the specified request.
   */
  synchronized void setConflict(LockConflict conflict) {
      assert checkAllowSync();
      this.conflict = conflict;
      notify();
  }

  /**
   * Checks if this locker is waiting for a lock.
   *
   * @return  the result of the lock request this locker is waiting
   *    for or {@code null} if it is not waiting
   */
  synchronized LockAttemptResult getWaitingFor() {
      assert checkAllowSync();
      return waitingFor;
  }

  /**
   * Sets the lock that this locker is waiting for, or marks that it is
   * not waiting if the argument is {@code null}.  If {@code waitingFor}
   * is not {@code null}, then it should represent a conflict, and it's
   * {@code conflict} field must not be {@code null}.
   *
   * @param  waitingFor the lock or {@code null}
   */
  synchronized void setWaitingFor(LockAttemptResult waitingFor) {
      assert checkAllowSync();
      assert waitingFor == null || waitingFor.conflict != null;
      this.waitingFor = waitingFor;
  }

  /**
   * Checks that the current thread is permitted to synchronize on this
   * locker.  Throws an {@link AssertionError} if already synchronized on
   * a locker other than this one or any lock, otherwise returns {@code
   * true}.
   */
  boolean checkAllowSync() {
      Locker locker = currentSync.get();
      if (locker != null && locker != this) {
    throw new AssertionError(
        "Attempt to synchronize on locker " + this +
        ", but already synchronized on " + locker);
      }
      Lock.checkNoSync();
      return true;
  }

  /**
   * Notes the start of synchronization on this locker.  Throws {@link
   * AssertionError} if already synchronized on any locker or lock,
   * otherwise returns {@code true}.
   */
  boolean noteSync() {
      Locker locker = currentSync.get();
      if (locker != null) {
    throw new AssertionError(
        "Attempt to synchronize on locker " + this +
        ", but already synchronized on " + locker);
      }
      Lock.checkNoSync();
      currentSync.set(this);
      return true;
  }

  /**
   * Notes the end of synchronization on this locker.  Throws {@link
   * AssertionError} if not already synchronized on this locker,
   * otherwise returns {@code true}.
   */
  boolean noteUnsync() {
      Locker locker = currentSync.get();
      if (locker == null) {
    throw new AssertionError(
        "Attempt to unsynchronize on locker " + this +
        ", but not currently synchronized on a locker");
      } else if (locker != this) {
    throw new AssertionError(
        "Attempt to unsynchronize on locker " + this +
        ", but currently synchronized on " + locker);
      }
      currentSync.remove();
      return true;
  }
     
  /** Print the associated transaction, for debugging. */
  @Override
  public String toString() {
      return txn.toString();
  }
    }

    /** Represents an object as identified by a source and an object ID. */
    private static final class Key {

  /** The source. */
  final String source;

  /** The object ID. */
  final Object objectId;

  /**
   * Creates an instance of this class.
   *
   * @param  source the source of the object
   * @param  objectId the object ID of the object
   */
  Key(String source, Object objectId) {
      checkNull("source", source);
      checkNull("objectId", objectId);
      this.source = source;
      this.objectId = objectId;
  }

  /* -- Compare source and object ID -- */

  @Override
  public boolean equals(Object object) {
      if (object == this) {
    return true;
      } else if (object instanceof Key) {
    Key key = (Key) object;
    return source.equals(key.source) &&
        objectId.equals(key.objectId);
      } else {
    return false;
      }
  }

  @Override
  public int hashCode() {
      return source.hashCode() ^ objectId.hashCode();
  }

  /** Print fields, for debugging. */
  @Override
  public String toString() {
      return source + ":" + objectId;
  }
    }

    /**
     * A class used to represent locks. <p>
     *
     * Callers should only call non-Object methods on instances of this class
     * if they hold the lock on the key map associated with the instance.
     */
    private static class Lock {

  /**
   * When assertions are enabled, hold the {@code Key} whose associated
   * {@code Map} the current thread is synchronized on, if any.
   */
  private static final ThreadLocal<Key> currentSync =
      new ThreadLocal<Key>();

  /** An empty array of lock requests. */
  private static final LockRequest[] NO_LOCK_REQUESTS = { };

  /** The key that identifies this lock. */
  final Key key;

  /**
   * The requests that currently own this lock.  Use a small initial
   * size, since the number of owners is typically small.
   */
  private final List<LockRequest> owners = new ArrayList<LockRequest>(2);

  /**
   * The requests that are waiting for this lock.  Use a small initial
   * size, since the number of owners is typically small.
   */
  private final List<LockRequest> waiters =
      new ArrayList<LockRequest>(2);

  /**
   * Creates a lock.
   *
   * @param  key the key that identifies this lock
   */
  Lock(Key key) {
      checkNull("key", key);
      this.key = key;
  }

  /**
   * Attempts to obtain this lock.  If {@code waiting} is {@code true},
   * the locker is already waiting for the lock.  Adds the locker as an
   * owner of the lock if the lock was obtained, and removes the locker
   * from the waiters list if it was waiting.  Returns {@code null} if
   * the locker already owned this lock.  Otherwise, returns a {@code
   * LockAttemptResult} containing the {@link LockRequest}, and with the
   * {@code conflict} field set to {@code null} if the lock was acquired,
   * else set to a conflicting transaction if the lock could not be
   * obtained.
   *
   * @param  locker the locker requesting the lock
   * @param  forWrite whether a write lock is requested
   * @param  waiting whether the locker is already a waiter
   * @return  a {@code LockAttemptResult} or {@code null}
   */
  LockAttemptResult lock(
      Locker locker, boolean forWrite, boolean waiting)
  {
      assert checkSync();
      if (owners.isEmpty()) {
    LockRequest request =
        new LockRequest(locker, key, forWrite, false);
    owners.add(request);
    if (waiting) {
        flushWaiter(locker);
    }
    assert validateInUse();
    return new LockAttemptResult(request, null);
      }
      boolean upgrade = false;
      Locker conflict = null;
      for (LockRequest ownerRequest : owners) {
    if (locker == ownerRequest.locker) {
        if (!forWrite || ownerRequest.getForWrite()) {
      if (waiting) {
          flushWaiter(locker);
      }
      assert validateInUse();
      return null;
        } else {
      upgrade = true;
        }
    } else if (forWrite || ownerRequest.getForWrite()) {
        conflict = ownerRequest.locker;
    }
      }
      LockRequest request =
    new LockRequest(locker, key, forWrite, upgrade);
      if (conflict == null) {
    if (upgrade) {
        boolean found = false;
        for (Iterator<LockRequest> i = owners.iterator();
       i.hasNext(); )
        {
      LockRequest ownerRequest = i.next();
      if (locker == ownerRequest.locker) {
          assert !ownerRequest.getForWrite()
        : "Should own for read when upgrading";
          i.remove();
          found = true;
          break;
      }
        }
        assert found : "Should own when upgrading";
    }
    owners.add(request);
    if (waiting) {
        flushWaiter(locker);
    }
    assert validateInUse();
    return new LockAttemptResult(request, null);
      }
      if (!waiting) {
    addWaiter(request);
      }
      assert validateInUse();
      return new LockAttemptResult(request, conflict);
  }

  /**
   * Adds a lock request to the list of requests waiting for this lock.
   * If this is an upgrade request, puts the request after any other
   * upgrade requests, but before other requests.  Otherwise, puts the
   * request at the end of the list.
   */
  private void addWaiter(LockRequest request) {
      if (waiters.isEmpty() || !request.getUpgrade()) {
    waiters.add(request);
      } else {
    /*
     * Add upgrade requests after any existing ones, but before
     * other requests.
     */
    boolean added = false;
    for (int i = 0; i < waiters.size(); i++) {
        if (!waiters.get(i).getUpgrade()) {
      waiters.add(i, request);
      added = true;
      break;
        }
    }
    if (!added) {
        waiters.add(request);
    }
      }
  }

  /**
   * Releases the ownership of this lock by the locker.  Attempts to
   * acquire locks for current waiters, removing the ones that acquire
   * the lock from the waiters list, adding them to the owners, and
   * returning them.
   *
   * @param  locker the locker whose ownership will be released
   * @return  the newly added owners
   */
  List<Locker> release(Locker locker) {
      assert checkSync();
      if (logger.isLoggable(Level.FINEST)) {
    logger.log(Level.FINEST, "release {0}, {1}", locker, this);
      }
      boolean owned = false;
      for (Iterator<LockRequest> i = owners.iterator(); i.hasNext(); ) {
    LockRequest ownerRequest = i.next();
    if (locker == ownerRequest.locker) {
        i.remove();
        owned = true;
        break;
    }
      }
      List<Locker> lockersToNotify = Collections.emptyList();
      if (owned && !waiters.isEmpty()) {
    boolean found = false;
    for (int i = 0; i < waiters.size(); i++) {
        LockRequest waiter = waiters.get(i);
        LockAttemptResult result =
      lock(waiter.locker, waiter.getForWrite(), true);
        if (logger.isLoggable(Level.FINEST)) {
      logger.log(
          Level.FINEST,
          "attempt to lock waiter {0} returns {1}",
          waiter, result);
        }
        /*
         * Stop when the first conflict is detected.  This
         * implementation always services waiters in order, so
         * there is no reason to look further after we've found a
         * waiter that is still blocked.
         */
        if (result != null && result.conflict != null) {
      break;
        }
        /* Back up because this waiter was removed */
        i--;
        if (!found) {
      found = true;
      lockersToNotify = new ArrayList<Locker>();
        }
        lockersToNotify.add(waiter.locker);
    }
      }
      assert !inUse() || validateInUse();
      return lockersToNotify;
  }

  /**
   * Validates the state of this lock, given that it is known to be in
   * use. <p>
   *
   * Locks that are in use must satisfy the following consistency
   * constraints:
   * <ul>
   * <li>at least one owner
   * <li>all upgrade waiters precede other waiters
   * <li>locker appears once in owners and waiters <em>except</em> that
   *     an upgrade request can be a waiter if a read request is an owner
   * <li>no upgrade waiters if the locker is not a read owner
   * </ul>
   *
   * @return  {@code true} if the state is valid, otherwise throws
   *    {@link AssertionError}
   */
  private boolean validateInUse() {
      int numWaiters = waiters.size();
      int numOwners = owners.size();
      if (numOwners == 0) {
    throw new AssertionError("No owners: " + this);
      }
      boolean seenNonUpgrade = false;
      for (int i = 0; i < numWaiters - 1; i++) {
    LockRequest waiter = waiters.get(i);
    if (!waiter.getUpgrade()) {
        seenNonUpgrade = true;
    } else if (seenNonUpgrade) {
        throw new AssertionError(
      "Upgrade waiter follows non-upgrade: " + this +
      ", waiters: " + waiters);
    }
    for (int j = i + 1; j < numWaiters; j++) {
        LockRequest waiter2 = waiters.get(j);
        if (waiter.locker == waiter2.locker) {
      throw new AssertionError(
          "Locker waits twice: " + this + ", " +
          waiter + ", " + waiter2);
        }
    }
    boolean foundOwner = false;
    for (int j = 0; j < numOwners; j++) {
        LockRequest owner = owners.get(j);
        if (waiter.locker == owner.locker) {
      foundOwner = true;
      if (!waiter.getUpgrade()) {
          throw new AssertionError(
        "Locker owns and waits, but not for" +
        " upgrade: " + this + ", owner:" + owner +
        ", waiter:" + waiter);
      } else if (owner.getForWrite()) {
          throw new AssertionError(
        "Locker owns for write but waits for" +
        " upgrade: " + this + ", owner:" + owner +
        ", waiter:" + waiter);
      }
        }
    }
    if (waiter.getUpgrade() && !foundOwner) {
        throw new AssertionError(
      "Waiting for upgrade but not owner: " + this +
      ", waiter: " + waiter);
    }
      }
      return true;
  }

  /**
   * Returns whether this lock is currently in use, or whether it is not
   * in use and can be removed from the lock table.  Locks are in use if
   * they have any owners or waiters.
   */
  boolean inUse() {
      assert checkSync();
      return !owners.isEmpty() || !waiters.isEmpty();
  }

  /** Returns a copy of the locker requests for the owners. */
  LockRequest[] copyOwners() {
      assert checkSync();
      if (owners.isEmpty()) {
    return NO_LOCK_REQUESTS;
      } else {
    return owners.toArray(new LockRequest[owners.size()]);
      }
  }

  /** Removes a locker from the list of waiters for this lock. */
  void flushWaiter(Locker locker) {
      assert checkSync();
      for (Iterator<LockRequest> iter = waiters.iterator();
     iter.hasNext(); )
      {
    LockRequest request = iter.next();
    if (request.locker == locker) {
        iter.remove();
        break;
    }
      }
  }

  /**
   * Checks if the lock is already owned in a way the satisfies the
   * specified request.
   */
  boolean isOwner(LockRequest request) {
      assert checkSync();
      for (LockRequest owner : owners) {
    if (request.locker == owner.locker) {
        return !request.getForWrite() || owner.getForWrite();
    }
      }
      return false;
  }

  /**
   * Notes the start of synchronization on the map associated with {@code
   * key}.  Throws {@link AssertionError} if already synchronized on a
   * key, otherwise returns {@code true}.
   */
  static boolean noteSync(Key key) {
      Key currentKey = currentSync.get();
      if (currentKey != null) {
    throw new AssertionError(
        "Attempt to synchronize on map for key " + key +
        ", but already synchronized on " + currentKey);
      }
      currentSync.set(key);
      return true;
  }

  /**
   * Notes the end of synchronization on the map associated with {@code
   * key}.  Throws {@link AssertionError} if not already synchronized on
   * {@code key}, otherwise returns {@code true}.
   */
  static boolean noteUnsync(Key key) {
      Key currentKey = currentSync.get();
      if (currentKey == null) {
    throw new AssertionError(
        "Attempt to unsynchronize on map for key " + key +
        ", but not currently synchronized on a key");
      } else if (!currentKey.equals(key)) {
    throw new AssertionError(
        "Attempt to unsynchronize on map for key " + key +
        ", but currently synchronized on " + currentKey);
      }
      currentSync.remove();
      return true;
  }

  /**
   * Checks that the current thread is not synchronized on the map
   * associated with a key, throwing {@link AssertionError} if it is, and
   * otherwise returning {@code true}.
   */
  static boolean checkNoSync() {
      Key currentKey = currentSync.get();
      if (currentKey != null) {
    throw new AssertionError(
        "Currently synchronized on key " + currentKey);
      }
      return true;
  }

  /**
   * Checks that the current thread is synchronized on the map associated
   * with this lock, throwing {@link AssertionError} if it is not, and
   * otherwise returning {@code true}.
   */
  boolean checkSync() {
      Key currentKey = currentSync.get();
      if (currentKey == null) {
    throw new AssertionError(
        "Currently not synchronized on a key");
      } else if (!currentKey.equals(key)) {
    throw new AssertionError(
        "Should be synchronized on " + key +
        ", but currently synchronized on " + currentKey);
      }
      return true;
  }

  /** Print fields, for debugging. */
  @Override
  public String toString() {
      return "Lock[" + key + "]";
  }
    }

    /** The result of attempting to request a lock. */
    private static class LockAttemptResult {

  /** The lock request. */
  final LockRequest request;

  /**
   * A conflicting locker, if the request was not granted, or {@code
   * null}.
   */
  final Locker conflict;

  /**
   * Creates an instance of this class.
   *
   * @param  request the lock request
   * @param  conflict a conflicting locker or {@code null}
   */
  LockAttemptResult(LockRequest request, Locker conflict) {
      assert request != null;
      this.request = request;
      this.conflict = conflict;
  }

  /** Print fields, for debugging. */
  @Override
  public String toString() {
      return "LockAttemptResult[" + request +
    ", conflict:" + conflict + "]";
  }
    }

    /** Implement {@link AccessReporter}. */
    private class AccessReporterImpl<T> extends AbstractAccessReporter<T> {

  /**
   * Creates an instance of this class.
   *
   * @param  source the source of the objects managed by this
   *    reporter
   */
  AccessReporterImpl(String source) {
      super(source);
  }

  /* -- Implement AccessReporter -- */

  /** {@inheritDoc} */
  public void reportObjectAccess(
      Transaction txn, T objectId, AccessType type, Object description)
  {
      checkNull("type", type);
      LockConflict conflict = lock(
    txn, source, objectId, type == AccessType.WRITE, description);
      if (conflict != null) {
    String descriptionMsg = "";
    if (description != null) {
        try {
      descriptionMsg = ", description:" + description;
        } catch (RuntimeException e) {
        }
    }
    String accessMsg = "Access txn:" + txn +
        ", type:" + type +
        ", source:" + source +
        ", objectId:" + objectId +
        descriptionMsg +
        " failed: ";
    String conflictMsg = ", with conflicting transaction " +
        conflict.getConflictingTransaction();
    TransactionAbortedException exception;
    switch (conflict.getType()) {
    case TIMEOUT:
        exception = new TransactionTimeoutException(
      accessMsg + "Transaction timed out" + conflictMsg);
        break;
    case DENIED:
        exception = new TransactionConflictException(
      accessMsg + "Access denied" + conflictMsg);
        break;
    case INTERRUPTED:
        exception = new TransactionInterruptedException(
      accessMsg + "Transaction interrupted" + conflictMsg);
        break;
    case DEADLOCK:
        exception = new TransactionConflictException(
      accessMsg + "Transaction deadlock" + conflictMsg);
        break;
    default:
        throw new AssertionError(
      "Should not be " + conflict.getType());
    }
    txn.abort(exception);
    throw exception;
      }
  }

  /** {@inheritDoc} */
  public void setObjectDescription(
      Transaction txn, T objectId, Object description)
  {
      Locker locker = getLocker(txn);
      if (description == null) {
    checkNull("objectId", objectId);
      } else {
    locker.setDescription(
        new Key(source, objectId), description);
      }
  }
    }

    /** A class representing a request for a lock. */
    private static final class LockRequest implements AccessedObject {

  /** Types of requests. */
  private enum Type { READ, WRITE, UPGRADE; }

  /** The locker that requested the lock. */
  final Locker locker;

  /** The key identifying the lock. */
  final Key key;

  /** The request type. */
  private final Type type;

  /**
   * Creates a lock request.
   *
   * @param  locker the locker that requested the lock
   * @param  key the key identifying the lock
   * @param  forWrite whether a write lock was requested
   * @param  upgrade whether an upgrade was requested
   */
  LockRequest(
      Locker locker, Key key, boolean forWrite, boolean upgrade)
  {
      assert locker != null;
      assert key != null;
      assert !upgrade || forWrite : "Upgrade implies forWrite";
      this.locker = locker;
      this.key = key;
      type = !forWrite ? Type.READ
    : !upgrade ? Type.WRITE
    : Type.UPGRADE;
  }

  /* -- Implement AccessedObject -- */

  /** {@inheritDoc} */
  public String getSource() {
      return key.source;
  }

  /** {@inheritDoc} */
  public Object getObjectId() {
      return key.objectId;
  }

  /** {@inheritDoc} */
  public AccessType getAccessType() {
      return (type == Type.READ) ? AccessType.READ : AccessType.WRITE;
  }

  /** {@inheritDoc} */
  public Object getDescription() {
      return locker.getDescription(key);
  }

  /**
   * Two instances are equal if they are instances of this class, and
   * have the same source, object ID, and access type.
   *
   * @param  object the object to compare with
   * @return  whether this instance equals the argument
   */
  @Override
  public boolean equals(Object object) {
      if (object == this) {
    return true;
      } else if (object instanceof LockRequest) {
    LockRequest request = (LockRequest) object;
    return key.equals(request.key) &&
        getForWrite() == request.getForWrite();
      } else {
    return false;
      }
  }

  @Override
  public int hashCode() {
      return key.hashCode() ^ (getForWrite() ? 1 : 0);
  }

  /* -- Other methods -- */

  /** Print fields, for debugging. */
  @Override
  public String toString() {
      return "LockRequest[" + locker + ", " + key +
    ", type:" + type + "]";
  }

  /** Returns whether the request was for write. */
  boolean getForWrite() {
      return type != Type.READ;
  }

  /** Returns whether the request was for an upgrade. */
  boolean getUpgrade() {
      return type == Type.UPGRADE;
  }
    }

    /** Utility class for detecting deadlocks. */
    private class DeadlockChecker {

  /**
   * Maps lockers to information about lockers they are waiting for.
   * This map serves as a cache for information about lock owners, to
   * avoid the synchronization needed to retrieve it again when checking
   * for multiple deadlocks.
   */
  private final Map<Locker, WaiterInfo> waiterMap =
      new HashMap<Locker, WaiterInfo>();

  /** The top level locker we are checking for deadlocks. */
  private final Locker rootLocker;

  /**
   * The pass number of the current deadlock check.  There could be
   * multiple deadlocks active simultaneously, so deadlock checking is
   * repeated until no deadlocks are found.
   */
  private int pass;

  /** The locker that was found in a circular reference. */
  private Locker cycleBoundary;

  /** The current choice of a locker to abort. */
  private Locker victim;

  /** Another locker in the deadlock. */
  private Locker conflict;

  /**
   * Creates an instance of this class.
   *
   * @param  locker the locker to check
   */
  DeadlockChecker(Locker locker) {
      assert locker != null;
      rootLocker = locker;
  }

  /**
   * Checks for a deadlock starting with the root locker.
   *
   * @return  a lock conflict if a deadlock was found, else {@code
   *    null}
   */
  LockConflict check() {
      LockConflict result = null;
      for (pass = 1; true; pass++) {
    if (!checkInternal(rootLocker, getWaiterInfo(rootLocker))) {
        if (result == null) {
      logger.log(Level.FINEST,
           "check deadlock {0}: no deadlock",
           rootLocker);
      return null;
        } else {
      return result;
        }
    }
    if (logger.isLoggable(Level.FINER)) {
        logger.log(Level.FINER, "check deadlock {0}: victim {1}",
             rootLocker, victim);
    }
    LockConflict deadlock =
        new LockConflict(LockConflictType.DEADLOCK, conflict.txn);
    getWaiterInfo(victim).waitingFor = null;
    victim.setConflict(deadlock);
    if (victim == rootLocker) {
        return deadlock;
    } else {
        result = new LockConflict(
      LockConflictType.BLOCKED, conflict.txn);
    }
      }
  }

  /**
   * Checks for deadlock starting with the specified locker and
   * information about its waiters.  Returns whether a deadlock was
   * found.
   */
  private boolean checkInternal(Locker locker, WaiterInfo waiterInfo) {
      waiterInfo.pass = pass;

            if (waiterInfo.waitingFor == null) {
                if (logger.isLoggable(Level.FINEST)) {
                    logger.log(Level.FINEST,
                               "checking deadlock {0}, pass {1}:" +
                               " locker {2}: ignore not waiting",
                               rootLocker, pass, locker);
                }
           
                return false;
            }

      for (LockRequest request : waiterInfo.waitingFor) {
    Locker owner = request.locker;
    if (owner == locker) {
        if (logger.isLoggable(Level.FINEST)) {
      logger.log(Level.FINEST,
           "checking deadlock {0}, pass {1}:" +
           " locker {2}, waiting for {3}:" +
           " ignore self-reference",
           rootLocker, pass, locker, request);
        }
    } else {
        WaiterInfo ownerInfo = getWaiterInfo(owner);
        if (ownerInfo.waitingFor == null) {
      if (logger.isLoggable(Level.FINEST)) {
          logger.log(Level.FINEST,
               "checking deadlock {0}, pass {1}:" +
               " locker {2}, waiting for {3}:" +
               " ignore not waiting",
               rootLocker, pass, locker, request);
      }
        } else if (ownerInfo.pass == pass) {
      /* Found a deadlock! */
      cycleBoundary = owner;
      victim = owner;
      if (logger.isLoggable(Level.FINEST)) {
          logger.log(Level.FINEST,
               "checking deadlock {0}, pass {1}:" +
               " locker {2}, waiting for {3}:" +
               " deadlock",
               rootLocker, pass, locker, request);
      }
      return true;
        } else {
      if (logger.isLoggable(Level.FINEST)) {
          logger.log(Level.FINEST,
               "checking deadlock {0}, pass {1}:" +
               " locker {2}, waiting for {3}:" +
               " recurse",
               rootLocker, pass, locker, request);
      }
      if (checkInternal(owner, ownerInfo)) {
          maybeUpdateVictim(owner);
          return true;
      }
        }
    }
      }
      return false;
  }

  /**
   * Returns information about the lockers that the specified locker is
   * waiting for.
   */
  private WaiterInfo getWaiterInfo(Locker locker) {
      WaiterInfo waiterInfo = waiterMap.get(locker);
      if (waiterInfo == null) {
    LockRequest[] waitingFor;
    LockAttemptResult result = locker.getWaitingFor();
    if (result == null || locker.getConflict() != null) {
        waitingFor = null;
    } else {
        Key key = result.request.key;
        Map<Key, Lock> keyMap = getKeyMap(key);
        assert Lock.noteSync(key);
        try {
      synchronized (keyMap) {
          waitingFor = getLock(key, keyMap).copyOwners();
      }
        } finally {
      assert Lock.noteUnsync(key);
        }
    }
    waiterInfo = new WaiterInfo(waitingFor);
    waiterMap.put(locker, waiterInfo);
      }
      return waiterInfo;
  }

  /**
   * Updates the victim and conflict fields to reflect an additional
   * locker in the deadlock chain.  Use the argument as the victim if it
   * has a newer requested start time than the previously selected
   * victim.
   */
  private void maybeUpdateVictim(Locker locker) {
      assert locker != null;
      if (conflict == null) {
    conflict = locker;
      }
      if (locker == cycleBoundary) {
    /* We've gone all the way around the circle, so we're done */
    cycleBoundary = null;
      } else if (cycleBoundary != null &&
           locker.requestedStartTime > victim.requestedStartTime)
      {
    /*
     * We're still within the cycle and this locker started later
     * than the current victim, so use it instead.
     */
    if (conflict == locker) {
        conflict = victim;
    }
    victim = locker;
    logger.log(Level.FINEST,
         "checking deadlock {0}, pass {1}: new victim: {2}",
         rootLocker, pass, victim);
      }
  }
    }

    /**
     * Provides information about the requests a locker is waiting for.  Used
     * in deadlock detection.
     */
    private static class WaiterInfo {

  /**
   * The requests the locker is waiting for, or {@code null} if not
   * waiting.
   */
  LockRequest[] waitingFor;

  /**
   * The pass in which the locker was checked.  If we encounter an
   * instance with the current pass number, then we've found a cycle.
   */
  int pass = 0;

  /**
   * Creates an instance of this class.
   *
   * @param  waitingFor the requests the locker is waiting for
   */
  WaiterInfo(LockRequest[] waitingFor) {
      this.waitingFor = waitingFor;
  }
    }

    /**
     * A transaction listener that calls {@link #endTransaction} when called
     * after the transaction completes.  Use a listener instead of a
     * transaction participant to make sure that locks are released only after
     * all of the transaction participants have finished their work.
     */
    private class TxnListener implements TransactionListener {

  /** The transaction. */
  private final Transaction txn;

  /**
   * Creates an instance of this class.
   *
   * @param  txn the transaction we're listening for
   */
  TxnListener(Transaction txn) {
      this.txn = txn;
  }

  /**
   * {@inheritDoc} <p>
   *
   * This implementation does nothing.
   */
  public void beforeCompletion() { }

  /**
   * {@inheritDoc} <p>
   *
   * This implementation calls {@link #endTransaction}.
   */
  public void afterCompletion(boolean committed) {
      endTransaction(txn);
  }
    }
}
TOP

Related Classes of com.sun.sgs.impl.kernel.LockingAccessCoordinator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.