blob: 65ffd92e5f8995b73a17f45abf72e342e7a67d2a [file] [log] [blame]
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.je.txn;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.OperationFailureException;
import com.sleepycat.je.Transaction;
import com.sleepycat.je.dbi.EnvironmentImpl;
/**
* Extends BasicLocker to share locks with another specific locker.
*
* <p>In general, a BuddyLocker can be used whenever the primary (API) locker
* is in use, and we need to lock a node and release that lock before the
* primary locker transaction ends. In other words, for this particular lock
* we don't want to use two-phase locking. To accomplish that we use a
* separate BuddyLocker instance to hold the lock, while sharing locks with the
* primary locker. The BuddyLocker can be closed to release this particular
* lock, without releasing the other locks held by the primary locker.</p>
*
* <p>In particular, a ReadCommittedLocker extends BuddyLocker. The
* ReadCommittedLocker keeps track of read locks, while its buddy Txn keeps
* track of write locks. The two lockers must share locks to prevent
* conflicts.</p>
*
* <p>In addition, a BuddyLocker is used when acquiring a RANGE_INSERT lock.
* RANGE_INSERT only needs to be held until the point we have inserted the new
* node into the BIN. A separate locker is therefore used so we can release
* that lock separately when the insertion into the BIN is complete. But the
* RANGE_INSERT lock must not conflict with locks held by the primary locker.
* So a BuddyLocker is used that shares locks with the primary locker.</p>
*/
public class BuddyLocker extends BasicLocker {
private final Locker buddy;
/**
* Creates a BuddyLocker.
*/
protected BuddyLocker(EnvironmentImpl env, Locker buddy) {
super(env, buddy.getDefaultNoWait());
this.buddy = buddy;
buddy.addBuddy(this);
}
public static BuddyLocker createBuddyLocker(EnvironmentImpl env,
Locker buddy)
throws DatabaseException {
return new BuddyLocker(env, buddy);
}
@Override
void close() {
super.close();
buddy.removeBuddy(this);
}
/**
* Returns the buddy locker.
*/
@Override
Locker getBuddy() {
return buddy;
}
/**
* Forwards this call to the buddy locker. This object itself is never
* transactional but the buddy may be.
*/
@Override
public Txn getTxnLocker() {
return buddy.getTxnLocker();
}
/**
* Forwards this call to the buddy locker. This object itself is never
* transactional but the buddy may be.
*/
@Override
public Transaction getTransaction() {
return buddy.getTransaction();
}
/**
* Forwards this call to the base class and to the buddy locker.
*/
@Override
public void releaseNonTxnLocks()
throws DatabaseException {
super.releaseNonTxnLocks();
buddy.releaseNonTxnLocks();
}
/**
* Returns whether this locker can share locks with the given locker.
*/
@Override
public boolean sharesLocksWith(Locker other) {
if (super.sharesLocksWith(other)) {
return true;
} else {
return (buddy == other ||
other.getBuddy() == this ||
buddy == other.getBuddy());
}
}
/**
* Returns the lock timeout of the buddy locker, since this locker has no
* independent timeout.
*/
@Override
public long getLockTimeout() {
return buddy.getLockTimeout();
}
/**
* Returns the transaction timeout of the buddy locker, since this locker
* has no independent timeout.
*/
@Override
public long getTxnTimeout() {
return buddy.getTxnTimeout();
}
/**
* Sets the lock timeout of the buddy locker, since this locker has no
* independent timeout.
*/
@Override
public void setLockTimeout(long timeout) {
buddy.setLockTimeout(timeout);
}
/**
* Sets the transaction timeout of the buddy locker, since this locker has
* no independent timeout.
*/
@Override
public void setTxnTimeout(long timeout) {
buddy.setTxnTimeout(timeout);
}
/**
* Returns whether the buddy locker is timed out, since this locker has no
* independent timeout.
*/
@Override
public boolean isTimedOut() {
return buddy.isTimedOut();
}
/**
* Returns the buddy locker's start time, since this locker has no
* independent timeout.
*/
@Override
public long getTxnStartMillis() {
return buddy.getTxnStartMillis();
}
/**
* Forwards to the buddy locker, since the buddy may be transactional.
*/
@Override
public void setOnlyAbortable(OperationFailureException cause) {
buddy.setOnlyAbortable(cause);
}
/**
* Forwards to the parent buddy locker, so the buddy can check itself and
* all of its child buddies.
*/
@Override
public void checkPreempted(final Locker allowPreemptedLocker)
throws OperationFailureException {
buddy.checkPreempted(allowPreemptedLocker);
}
/**
* Consider this locker replicated if its buddy (Txn) is replicated.
*/
@Override
public boolean isReplicated() {
return buddy.isReplicated();
}
/**
* Consider this locker local-write if its buddy is local-write.
*/
@Override
public boolean isLocalWrite() {
return buddy.isLocalWrite();
}
}