/* * 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 java.util.logging.Level; import static java.util.logging.Level.FINER; import java.util.logging.Logger; /** * A class for managing lock conflicts where locks are not held by transactions * and the locker can make simultaneous requests from multiple threads. This * class does not detect deadlocks, but provides support for {@linkplain * #downgradeLock downgrading locks}. All {@link Locker} objects supplied to * this class should be instances of {@link MultiLocker}. <p> * * This class and its {@linkplain LockManager superclass} use the {@link * Logger} named {@code com.sun.sgs.impl.util.lock} to log information at the * following logging levels: <p> * * <ul> * <li> {@link Level#FINER FINER} - Releasing locks; requesting, waiting for, * and returning from lock requests * <li> {@link Level#FINEST FINEST} - Notifying new lock owners, results of * requesting locks before waiting, releasing locks, results of attempting * to assign locks to waiters * </ul> * * @param <K> the type of key */ public final class MultiLockManager<K> extends LockManager<K> { /** * A thread local that stores the result of the lock request that the * current thread is waiting for, or {@code null} if it is not waiting. */ private final ThreadLocal<LockAttemptResult<K>> waitingFor = new ThreadLocal<LockAttemptResult<K>>(); /* -- Public constructor -- */ /** * Creates an instance of this class. * * @param lockTimeout the maximum number of milliseconds to acquire a * lock * @param numKeyMaps the number of separate maps to use for storing keys * @throws IllegalArgumentException if {@code lockTimeout} or {@code * numKeyMaps} is less than {@code 1} */ public MultiLockManager(long lockTimeout, int numKeyMaps) { super(lockTimeout, numKeyMaps); } /* -- Public methods -- */ /** * {@inheritDoc} * * @throws IllegalArgumentException {@inheritDoc}, or if {@code locker} is * not an instance of {@link MultiLocker} */ @Override public LockConflict<K> lock(Locker<K> locker, K key, boolean forWrite) { checkMultiLocker(locker); return super.lock(locker, key, forWrite); } /** * {@inheritDoc} * * @throws IllegalArgumentException {@inheritDoc}, or if {@code locker} is * not an instance of {@link MultiLocker} */ @Override public LockConflict<K> lockNoWait( Locker<K> locker, K key, boolean forWrite) { checkMultiLocker(locker); return super.lockNoWait(locker, key, forWrite); } /** * {@inheritDoc} * * @throws IllegalArgumentException {@inheritDoc}, or if {@code locker} is * not an instance of {@link MultiLocker} */ public LockConflict<K> waitForLock(Locker<K> locker) { checkMultiLocker(locker); return super.waitForLock(locker); } /** * Downgrades a lock held by a locker from write to read access. This * method does nothing if the lock is not held for write. * * @param locker the locker holding the lock * @param key the key identifying the lock * @throws IllegalArgumentException if {@code locker} has a different lock * manager, or if {@code locker} is not an instance of {@link * MultiLocker} */ public void downgradeLock(Locker<K> locker, K key) { if (logger.isLoggable(FINER)) { logger.log(FINER, "downgrade {0} {1}", locker, key); } checkMultiLocker(locker); releaseLockInternal(locker, key, true); } /* -- Other methods -- */ /** * Checks if this thread is waiting for a lock. * * @return the result of the lock request this thread is waiting * for or {@code null} if it is not waiting */ LockAttemptResult<K> getWaitingFor() { return waitingFor.get(); } /** * Records the lock that this thread is waiting for, or marks that it is * not waiting if the argument is {@code null}. * * @param waitingFor the lock or {@code null} */ void setWaitingFor(LockAttemptResult<K> waitingFor) { this.waitingFor.set(waitingFor); } /** * Throws IllegalArgumentException if the argument is not a MultiLocker. */ private static void checkMultiLocker(Locker<?> locker) { if (locker != null && !(locker instanceof MultiLocker<?>)) { throw new IllegalArgumentException("Locker is not a MultiLocker"); } } }