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