/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 2002-2006
 *      Sleepycat Software.  All rights reserved.
 *
 * $Id: BuddyLocker.java,v 1.5 2006/01/03 21:55:58 bostic Exp $
 */

package com.sleepycat.je.txn;

import com.sleepycat.je.DatabaseException;
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 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 Locker buddy;

    /**
     * Creates a BuddyLocker.
     */
    public BuddyLocker(EnvironmentImpl env, Locker buddy)
        throws DatabaseException {

        super(env);
        this.buddy = buddy;
    }

    /**
     * Returns the buddy locker.
     */
    Locker getBuddy() {
        return buddy;
    }

    /**
     * Forwards this call to the buddy locker.
     */
    public Txn getTxnLocker() {
        return buddy.getTxnLocker();
    }

    /**
     * Creates a new instance of this txn for the same environment.  No
     * transactional locks are held by this object, so no locks are retained.
     * newNonTxnLocker is also called for the BuddyLocker.
     */
    public Locker newNonTxnLocker()
        throws DatabaseException {

        return new BuddyLocker(envImpl, buddy.newNonTxnLocker());
    }

    /**
     * Forwards this call to the base class and to the buddy locker.
     */
    public void releaseNonTxnLocks()
        throws DatabaseException {

        super.releaseNonTxnLocks();
        buddy.releaseNonTxnLocks();
    }

    /**
     * Returns whether this locker can share locks with the given locker.
     */
    public boolean sharesLocksWith(Locker other) {

        if (super.sharesLocksWith(other)) {
            return true;
        } else {
            return buddy == other;
        }
    }
}
