/** * Attempt to acquire a lock of <i>type</i> on <i>nodeId</i>. If the lock acquisition would result * in a deadlock, throw an exception.<br> * If the requested lock is not currently available, block until it is or until timeout * milliseconds have elapsed.<br> * If a lock of <i>type</i> is already held, return EXISTING.<br> * If a WRITE lock is held and a READ lock is requested, return PROMOTION.<br> * 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.<br> * * @param nodeId The NodeId to lock. * @param locker The Locker to lock this on behalf of. * @param type The lock type requested. * @param timeout milliseconds to time out after if lock couldn't be obtained. 0 means block * indefinitely. Not used if nonBlockingRequest is true. * @param nonBlockingRequest if true, means don't block if lock can't be acquired, and ignore the * timeout parameter. * @return 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 LockConflictException if lock could not be acquired. * @throws 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 nodeId, Locker locker, LockType type, long timeout, boolean nonBlockingRequest, DatabaseImpl database) throws LockConflictException, DatabaseException { assert timeout >= 0; /* No lock needed for dirty-read, return as soon as possible. */ if (type == LockType.NONE) { return LockGrantType.NONE_NEEDED; } /* * 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 (envImpl.isReplicated() && database != null && database.isReplicated() && !database.getId().equals(DbTree.NAME_DB_ID) && (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(nodeId, locker, type, timeout, nonBlockingRequest, database); return ret; } }