/* * 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; /** * Records information about an entity that requests locks from a {@link * LockManager}. * * @param <K> the type of key */ public abstract class Locker<K> { /** The lock manager for this locker. */ final LockManager<K> lockManager; /* -- Constructor -- */ /** * Creates an instance of this class. * * @param lockManager the lock manager for this locker */ protected Locker(LockManager<K> lockManager) { checkNull("lockManager", lockManager); this.lockManager = lockManager; } /* -- Public methods -- */ /** * Returns the lock manager for this locker. * * @return the lock manager for this locker. */ public LockManager<K> getLockManager() { return lockManager; } /* -- Protected methods -- */ /** * Returns the time when a lock attempt should stop, given the current time * and the lock timeout supplied by the caller. Subclasses can override * this method, for example to enforce a transaction timeout. * * @param now the current time in milliseconds * @param lockTimeout the amount of time in milliseconds to wait for a * lock * @return the time in milliseconds when the lock attempt should timeout */ protected long getLockTimeoutTime(long now, long lockTimeout) { return addCheckOverflow(now, lockTimeout); } /** * Creates a new lock request. <p> * * The default implementation creates and returns an instance of {@link * LockRequest}. * * @param key the key that identifies the lock * @param forWrite whether the request is for write * @param upgrade whether the request is for an upgrade * @return the lock request */ protected LockRequest<K> newLockRequest( K key, boolean forWrite, boolean upgrade) { return new LockRequest<K>(this, key, forWrite, upgrade); } /** * Checks if there is a conflict that should cause this locker's * current request to be denied. Returns {@code null} if there was no * conflict. <p> * * The default implementation of this method always returns {@code null}. * * @return the conflicting request or {@code null} */ protected LockConflict<K> getConflict() { return null; } /** * Clears the conflict that should cause this locker's current request to * be denied. If there is no conflict, then this method has no effect. If * the conflict is a deadlock, represented by a non-{@code null} return * value from {@link #getConflict getConflict} with a {@code type} field * equal to {@link LockConflictType#DEADLOCK DEADLOCK}, then the conflict * cannot be cleared and {@code IllegalStateException} will be thrown. <p> * * The default implementation of this method does nothing. * * @throws IllegalStateException if the conflict is a deadlock */ protected void clearConflict() { } /* -- Package access methods -- */ /** * Checks if this locker is waiting for a lock. * * @return the result of the attempt to request a lock that this locker * is waiting for, or {@code null} if it is not waiting */ abstract LockAttemptResult<K> getWaitingFor(); /** * Records that this locker is waiting for a lock, or marks that it is not * waiting if the argument is {@code null}. If {@code waitingFor} is not * {@code null}, then it should represent a conflict, and it's {@code * conflict} field must not be {@code null}. * * @param waitingFor the lock or {@code null} * @throws IllegalArgumentException if {@code waitingFor} is not {@code * null} and its {@code conflict} field is {@code null} */ abstract void setWaitingFor(LockAttemptResult<K> waitingFor); /** * Checks that the current thread is permitted to synchronize on this * locker. Throws an {@link AssertionError} if already synchronized on * a locker other than this one or any lock, otherwise returns {@code * true}. */ boolean checkAllowSync() { Locker<K> locker = lockManager.currentLockerSync.get(); if (locker != null && locker != this) { throw new AssertionError( "Attempt to synchronize on locker " + this + ", but already synchronized on " + locker); } Lock.checkNoSync(lockManager); return true; } /** * Notes the start of synchronization on this locker. Throws {@link * AssertionError} if already synchronized on any locker or lock, * otherwise returns {@code true}. */ boolean noteSync() { Locker<K> locker = lockManager.currentLockerSync.get(); if (locker != null) { throw new AssertionError( "Attempt to synchronize on locker " + this + ", but already synchronized on " + locker); } Lock.checkNoSync(lockManager); lockManager.currentLockerSync.set(this); return true; } /** * Notes the end of synchronization on this locker. Throws {@link * AssertionError} if not already synchronized on this locker, * otherwise returns {@code true}. */ boolean noteUnsync() { Locker<K> locker = lockManager.currentLockerSync.get(); if (locker == null) { throw new AssertionError( "Attempt to unsynchronize on locker " + this + ", but not currently synchronized on a locker"); } else if (locker != this) { throw new AssertionError( "Attempt to unsynchronize on locker " + this + ", but currently synchronized on " + locker); } lockManager.currentLockerSync.remove(); return true; } }