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; }