/* * Copyright 2007-2010 Sun Microsystems, Inc. * * This file is part of Project Darkstar Server. * * Project Darkstar Server is free software: you can redistribute it * and/or modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation and * distributed hereunder to you. * * Project Darkstar Server is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * -- */ package com.sun.sgs.impl.util.lock; import static com.sun.sgs.impl.sharedutil.Objects.checkNull; import static com.sun.sgs.impl.util.Numbers.addCheckOverflow; import com.sun.sgs.service.Transaction; /** * Records information about an entity that requests locks from a {@link * TxnLockManager} as part of a transaction. * * @param <K> the type of key */ public class TxnLocker<K> extends BasicLocker<K> { /** The associated transaction. */ protected final Transaction txn; /** * The time in milliseconds when the associated transaction was originally * requested to start. */ protected final long requestedStartTime; /** * A conflict that should cause this transaction's request to be * denied, or {@code null}. This value is cleared after the conflict * has been reported unless the conflict is a deadlock. Synchronize on * this locker when accessing this field. */ private LockConflict<K> conflict; /* -- Constructor -- */ /** * Creates an instance of this class. * * @param lockManager the lock manager for this locker * @param txn the associated transaction * @param requestedStartTime the time in milliseconds that the task * associated with the transaction was originally * requested to start * @throws IllegalArgumentException if {@code requestedStartTime} * is less than {@code 0} */ public TxnLocker(TxnLockManager<K> lockManager, Transaction txn, long requestedStartTime) { super(lockManager); checkNull("txn", txn); if (requestedStartTime < 0) { throw new IllegalArgumentException( "The requestedStartTime must not be less than 0"); } this.txn = txn; this.requestedStartTime = requestedStartTime; } /* -- Public methods -- */ /** * Returns the transaction associated with this locker. * * @return the transaction associated with this locker */ public Transaction getTransaction() { return txn; } /** * Returns the time in milliseconds that the transaction associated with * this locker was originally requested to start, as specified in the * constructor. * * @return the requested start time in milliseconds */ public long getRequestedStartTime() { return requestedStartTime; } /* -- Protected methods -- */ /** * {@inheritDoc} <p> * * This implementation stops the lock attempt when the transaction ends. */ @Override protected long getLockTimeoutTime(long now, long lockTimeout) { return Math.min( addCheckOverflow(now, lockTimeout), addCheckOverflow(txn.getCreationTime(), txn.getTimeout())); } /** * {@inheritDoc} <p> * * This implementation returns the conflict recorded for this locker. * * @throws IllegalStateException {@inheritDoc} */ @Override protected LockConflict<K> getConflict() { assert checkAllowSync(); synchronized (this) { return conflict; } } /** * {@inheritDoc} <p> * * This implementation checks for deadlocks and notifies waiters. * * @throws IllegalStateException {@inheritDoc} */ @Override protected void clearConflict() { assert checkAllowSync(); synchronized (this) { if (conflict != null) { if (conflict.type == LockConflictType.DEADLOCK) { throw new IllegalStateException( "Transaction " + this + " must abort due to conflict: " + conflict); } conflict = null; } notifyAll(); } } /** * Specifies a conflict that should cause this locker's current request to * be denied, and notifies waiters. * * @param conflict the conflicting request * @throws IllegalStateException if a conflicting request has already been * specified */ protected void setConflict(LockConflict<K> conflict) { assert checkAllowSync(); synchronized (this) { if (this.conflict != null) { throw new IllegalStateException( "This locker already has a conflict"); } this.conflict = conflict; notifyAll(); } } }