/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2002-2006 * Sleepycat Software. All rights reserved. * * $Id: BasicLocker.java,v 1.1 2006/05/06 08:59:04 ckaestne Exp $ */ package com.sleepycat.je.txn; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import com.sleepycat.je.Database; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.LockStats; import com.sleepycat.je.dbi.CursorImpl; import com.sleepycat.je.dbi.DatabaseImpl; import com.sleepycat.je.dbi.EnvironmentImpl; import com.sleepycat.je.utilint.DbLsn; /** * A concrete Locker that simply tracks locks and releases them when * operationEnd is called. */ public class BasicLocker extends Locker { /* * A BasicLocker can release all locks, so there is no need to distinguish * between read and write locks. * * ownedLock is used for the first lock obtained, and ownedLockSet is * instantiated and used only if more than one lock is obtained. This is * an optimization for the common case where only one lock is held by a * non-transactional locker. * * There's no need to track memory utilization for these non-txnal lockers, * because the lockers are short lived. */ private Lock ownedLock; private Set ownedLockSet; /** * Creates a BasicLocker. */ public BasicLocker(EnvironmentImpl env) throws DatabaseException { super(env, false, false); } /** * BasicLockers always have a fixed id, because they are never used for * recovery. */ protected long generateId(TxnManager txnManager) { return TxnManager.NULL_TXN_ID; } protected void checkState(boolean ignoreCalledByAbort) throws DatabaseException { /* Do nothing. */ } /** * @see Locker#lockInternal * @Override */ LockResult lockInternal(long nodeId, LockType lockType, boolean noWait, DatabaseImpl database) throws DatabaseException { /* Does nothing in BasicLocker. synchronized is for posterity. */ synchronized (this) { checkState(false); } long timeout = 0; boolean useNoWait = noWait || defaultNoWait; if (!useNoWait) { synchronized (this) { timeout = lockTimeOutMillis; } } /* Ask for the lock. */ LockGrantType grant = lockManager.lock (nodeId, this, lockType, timeout, useNoWait, database); return new LockResult(grant, null); } /** * Get the txn that owns the lock on this node. Return null if there's no * owning txn found. */ public Locker getWriteOwnerLocker(long nodeId) throws DatabaseException { return lockManager.getWriteOwnerLocker(new Long(nodeId)); } /** * Get the abort LSN for this node in the txn that owns the lock on this * node. Return null if there's no owning txn found. */ public long getOwnerAbortLsn(long nodeId) throws DatabaseException { Locker ownerTxn = lockManager.getWriteOwnerLocker(new Long(nodeId)); if (ownerTxn != null) { return ownerTxn.getAbortLsn(nodeId); } return DbLsn.NULL_LSN; } /** * Is never transactional. */ public boolean isTransactional() { return false; } /** * Is never serializable isolation. */ public boolean isSerializableIsolation() { return false; } /** * Is never read-committed isolation. */ public boolean isReadCommittedIsolation() { return false; } /** * No transactional locker is available. */ public Txn getTxnLocker() { return null; } /** * Creates a new instance of this txn for the same environment. No * transactional locks are held by this object, so no locks are retained. */ public Locker newNonTxnLocker() throws DatabaseException { return new BasicLocker(envImpl); } /** * Releases all locks, since all locks held by this locker are * non-transactional. */ public void releaseNonTxnLocks() throws DatabaseException { operationEnd(true); } /** * Release locks at the end of the transaction. */ public void operationEnd() throws DatabaseException { operationEnd(true); } /** * Release locks at the end of the transaction. */ public void operationEnd(boolean operationOK) throws DatabaseException { /* * Don't remove locks from txn's lock collection until iteration is * done, lest we get a ConcurrentModificationException during deadlock * graph "display". [#9544] */ if (ownedLock != null) { lockManager.release(ownedLock, this); ownedLock = null; } if (ownedLockSet != null) { Iterator iter = ownedLockSet.iterator(); while (iter.hasNext()) { Lock l = (Lock) iter.next(); lockManager.release(l, this); } /* Now clear lock collection. */ ownedLockSet.clear(); } /* Unload delete info, but don't wake up the compressor. */ synchronized (this) { if ((deleteInfo != null) && (deleteInfo.size() > 0)) { envImpl.addToCompressorQueue(deleteInfo.values(), false); // no wakeup deleteInfo.clear(); } } } /** * Transfer any MapLN locks to the db handle. */ public void setHandleLockOwner(boolean ignore /*operationOK*/, Database dbHandle, boolean dbIsClosing) throws DatabaseException { if (dbHandle != null) { if (!dbIsClosing) { transferHandleLockToHandle(dbHandle); } unregisterHandle(dbHandle); } } /** * This txn doesn't store cursors. */ public void registerCursor(CursorImpl cursor) throws DatabaseException { } /** * This txn doesn't store cursors. */ public void unRegisterCursor(CursorImpl cursor) throws DatabaseException { } /* * Transactional methods are all no-oped. */ /** * @return the abort LSN for this node. */ public long getAbortLsn(long nodeId) throws DatabaseException { return DbLsn.NULL_LSN; } /** * @return a dummy WriteLockInfo for this node. */ public WriteLockInfo getWriteLockInfo(long nodeId) throws DatabaseException { return WriteLockInfo.basicWriteLockInfo; } public void markDeleteAtTxnEnd(DatabaseImpl db, boolean deleteAtCommit) throws DatabaseException { if (deleteAtCommit) { db.deleteAndReleaseINs(); } } /** * Add a lock to set owned by this transaction. */ void addLock(Long nodeId, Lock lock, LockType type, LockGrantType grantStatus) throws DatabaseException { if (ownedLock == lock || (ownedLockSet != null && ownedLockSet.contains(lock))) { return; // Already owned } if (ownedLock == null) { ownedLock = lock; } else { if (ownedLockSet == null) { ownedLockSet = new HashSet(); } ownedLockSet.add(lock); } } /** * Remove a lock from the set owned by this txn. */ void removeLock(long nodeId, Lock lock) throws DatabaseException { if (lock == ownedLock) { ownedLock = null; } else if (ownedLockSet != null) { ownedLockSet.remove(lock); } } /** * Always false for this txn. */ public boolean createdNode(long nodeId) throws DatabaseException { return false; } /** * A lock is being demoted. Move it from the write collection into the read * collection. */ void moveWriteToReadLock(long nodeId, Lock lock) { } /** * stats */ public LockStats collectStats(LockStats stats) throws DatabaseException { if (ownedLock != null) { if (ownedLock.isOwnedWriteLock(this)) { stats.setNWriteLocks(stats.getNWriteLocks() + 1); } else { stats.setNReadLocks(stats.getNReadLocks() + 1); } } if (ownedLockSet != null) { Iterator iter = ownedLockSet.iterator(); while (iter.hasNext()) { Lock l = (Lock) iter.next(); if (l.isOwnedWriteLock(this)) { stats.setNWriteLocks(stats.getNWriteLocks() + 1); } else { stats.setNReadLocks(stats.getNReadLocks() + 1); } } } return stats; } }