예제 #1
0
  LockAttemptResult attemptLockInternal(
      Long nodeId, Locker locker, LockType type, boolean nonBlockingRequest, int lockTableIndex)
      throws DatabaseException {

    nRequests.increment();

    /* Get the target lock. */
    Map<Long, Lock> lockTable = lockTables[lockTableIndex];
    Lock useLock = lockTable.get(nodeId);
    if (useLock == null) {
      useLock = new ThinLockImpl();
      lockTable.put(nodeId, useLock);
      memoryBudget.updateLockMemoryUsage(TOTAL_THINLOCKIMPL_OVERHEAD, lockTableIndex);
    }

    /*
     * Attempt to lock.  Possible return values are NEW, PROMOTION, DENIED,
     * EXISTING, WAIT_NEW, WAIT_PROMOTION, WAIT_RESTART.
     */
    LockAttemptResult lar =
        useLock.lock(type, locker, nonBlockingRequest, memoryBudget, lockTableIndex);
    if (lar.useLock != useLock) {
      /* The lock mutated from ThinLockImpl to LockImpl. */
      useLock = lar.useLock;
      lockTable.put(nodeId, useLock);
      /* We still have the overhead of the hashtable (locktable). */
      memoryBudget.updateLockMemoryUsage(THINLOCK_MUTATE_OVERHEAD, lockTableIndex);
    }
    LockGrantType lockGrant = lar.lockGrant;
    boolean success = false;

    /* Was the attempt successful? */
    if ((lockGrant == LockGrantType.NEW) || (lockGrant == LockGrantType.PROMOTION)) {
      locker.addLock(nodeId, type, lockGrant);
      success = true;
    } else if (lockGrant == LockGrantType.EXISTING) {
      success = true;
    } else if (lockGrant == LockGrantType.DENIED) {
      /* Locker.lock will throw LockNotAvailableException. */
    } else {
      nWaits.increment();
    }
    return new LockAttemptResult(useLock, lockGrant, success);
  }
예제 #2
0
  private LockGrantType lockInternal(
      long nodeId,
      Locker locker,
      LockType type,
      long timeout,
      boolean nonBlockingRequest,
      DatabaseImpl database)
      throws DeadlockException, DatabaseException {

    Long nid = Long.valueOf(nodeId);
    LockAttemptResult result = attemptLock(nid, locker, type, nonBlockingRequest);
    /* If we got the lock or a non-blocking lock was denied, return. */
    if (result.success || result.lockGrant == LockGrantType.DENIED) {
      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 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(nid, locker, type, !isImportunate, memoryBudget)) {
          doWait = false;
        } else if (isImportunate) {
          result = stealLock(nid, locker, type, memoryBudget);
          if (result.success) {
            doWait = false;
          } else {
            /* Lock holder is non-preemptable, wait below. */
          }
        } else {
          throw makeTimeoutMsg(
              false /*isLockNotTxnTimeout*/,
              locker,
              nodeId,
              type,
              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(envImpl, IE);
        }

        boolean lockerTimedOut = locker.isTimedOut();
        long now = System.currentTimeMillis();
        boolean thisLockTimedOut = (keepTime && (now - startTime >= timeout));
        boolean isRestart = (result.lockGrant == LockGrantType.WAIT_RESTART);

        /*
         * 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(
            nid,
            locker,
            type,
            (lockerTimedOut || thisLockTimedOut || isRestart) && !isImportunate,
            memoryBudget)) {
          break;
        } else if (isImportunate) {
          result = stealLock(nid, locker, type, memoryBudget);
          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 rangeRestartException;
          }

          if (thisLockTimedOut) {
            throw makeTimeoutMsg(
                true /*isLockNotTxnTimeout*/,
                locker,
                nodeId,
                type,
                result.lockGrant,
                result.useLock,
                timeout,
                startTime,
                now,
                database);
          }

          if (lockerTimedOut) {
            throw makeTimeoutMsg(
                false /*isLockNotTxnTimeout*/,
                locker,
                nodeId,
                type,
                result.lockGrant,
                result.useLock,
                locker.getTxnTimeout(),
                locker.getTxnStartMillis(),
                now,
                database);
          }
        }
      }
    } 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(nid, type, result.lockGrant);

    return result.lockGrant;
  }