/* * 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 com.sun.sgs.impl.sharedutil.LoggerWrapper; import static com.sun.sgs.impl.sharedutil.Objects.checkNull; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import static java.util.logging.Level.FINEST; import java.util.logging.Logger; /** * A class used to represent locks. <p> * * Callers should only call non-{@code Object} methods on instances of this * class if they hold the lock on the key map associated with the instance. * * @param <K> the type of key * @see LockManager */ final class Lock<K> { /** The logger for this class. */ private static final LoggerWrapper logger = new LoggerWrapper( Logger.getLogger(LockManager.class.getName())); /** An empty array of lock requests. */ private static final LockRequest<?>[] NO_LOCK_REQUESTS = { }; /** The key that identifies this lock. */ final K key; /** * The requests that currently own this lock. Use a small initial * size, since the number of owners is typically small. */ private final List<LockRequest<K>> owners = new ArrayList<LockRequest<K>>(2); /** * The requests that are waiting for this lock. Use a small initial * size, since the number of owners is typically small. */ private final List<LockRequest<K>> waiters = new ArrayList<LockRequest<K>>(2); /** * Creates a lock. * * @param key the key that identifies this lock */ Lock(K key) { checkNull("key", key); this.key = key; } /** * Attempts to obtain this lock. If {@code waiting} is {@code true}, the * locker is known to be waiting for the lock, although it may also be * waiting otherwise. Adds the locker as an owner of the lock if the lock * was obtained, and removes the locker from the waiters list if it was * waiting. Returns {@code null} if the locker already owned this lock. * Otherwise, adds the locker to the waiters list, and returns a {@code * LockAttemptResult} containing the {@link LockRequest}, with its {@code * conflict} field set to {@code null} if the lock was acquired or a * conflicting transaction if the lock could not be obtained. * * @param locker the locker requesting the lock * @param forWrite whether a write lock is requested * @param waiting whether the locker is known to be a waiter * @return a {@code LockAttemptResult} or {@code null} */ LockAttemptResult<K> lock( Locker<K> locker, boolean forWrite, boolean waiting) { assert checkSync(locker.lockManager); boolean upgrade = false; Locker<K> conflict = null; if (!owners.isEmpty()) { /* Check conflicting owners */ for (LockRequest<K> ownerRequest : owners) { if (locker == ownerRequest.getLocker()) { if (!forWrite || ownerRequest.getForWrite()) { /* Already locked */ assert validateInUse(); return null; } else { /* Upgrade */ upgrade = true; } } else if (forWrite || ownerRequest.getForWrite()) { /* Found conflict */ conflict = ownerRequest.getLocker(); } } } LockRequest<K> request = null; if (!waiters.isEmpty()) { /* Check waiters for conflicts or already waiting */ for (Iterator<LockRequest<K>> i = waiters.iterator(); i.hasNext(); ) { LockRequest<K> waiterRequest = i.next(); if (locker == waiterRequest.getLocker()) { assert forWrite == waiterRequest.getForWrite(); request = waiterRequest; if (conflict == null) { i.remove(); } break; } else if (conflict == null && (forWrite || waiterRequest.getForWrite())) { /* Found a conflicting waiter */ conflict = waiterRequest.getLocker(); } } } assert !waiting || request != null : "Should have found waiter"; if (conflict == null && upgrade) { /* Upgrading -- remove the read lock request from owners */ assert owners.size() == 1 && owners.get(0).getLocker() == locker; owners.remove(0); } if (conflict == null) { if (request == null) { request = locker.newLockRequest(key, forWrite, upgrade); } owners.add(request); } else if (request == null) { request = locker.newLockRequest(key, forWrite, upgrade); addWaiter(request); } assert validateInUse(); return new LockAttemptResult<K>(request, conflict); } /** * Adds a lock request to the list of requests waiting for this lock. * If this is an upgrade request, puts the request after any other * upgrade requests, but before other requests. Otherwise, puts the * request at the end of the list. */ private void addWaiter(LockRequest<K> request) { if (waiters.isEmpty() || !request.getUpgrade()) { waiters.add(request); } else { /* * Add upgrade requests after any existing ones, but before * other requests. */ boolean added = false; for (int i = 0; i < waiters.size(); i++) { if (!waiters.get(i).getUpgrade()) { waiters.add(i, request); added = true; break; } } if (!added) { waiters.add(request); } } } /** * Releases the ownership of this lock by the locker. Attempts to * acquire locks for current waiters, removing the ones that acquire * the lock from the waiters list, adding them to the owners, and * returning them. * * @param locker the locker whose ownership will be released * @param downgrade whether to downgrade ownership from write to read * rather than releasing all ownership * @return the newly added owners */ List<Locker<K>> release(Locker<K> locker, boolean downgrade) { assert checkSync(locker.lockManager); if (logger.isLoggable(FINEST)) { logger.log(FINEST, "release {0}, downgrade:{1}, {2}", locker, downgrade, this); } boolean owned = false; for (Iterator<LockRequest<K>> i = owners.iterator(); i.hasNext(); ) { LockRequest<K> ownerRequest = i.next(); if (locker == ownerRequest.getLocker()) { if (!downgrade || ownerRequest.getForWrite()) { i.remove(); owned = true; if (downgrade) { owners.add( locker.newLockRequest( ownerRequest.getKey(), false, false)); } } break; } } List<Locker<K>> lockersToNotify = Collections.emptyList(); if (owned && !waiters.isEmpty()) { boolean found = false; for (int i = 0; i < waiters.size(); i++) { LockRequest<K> waiter = waiters.get(i); LockAttemptResult<K> result = lock(waiter.getLocker(), waiter.getForWrite(), true); if (logger.isLoggable(FINEST)) { logger.log(FINEST, "attempt to lock waiter {0} returns {1}", waiter, result); } /* * Stop when the first conflict is detected. This * implementation always services waiters in order, so * there is no reason to look further after we've found a * waiter that is still blocked. */ if (result != null && result.conflict != null) { break; } /* Back up because this waiter was removed */ i--; if (!found) { found = true; lockersToNotify = new ArrayList<Locker<K>>(); } lockersToNotify.add(waiter.getLocker()); } } assert !inUse(locker.lockManager) || validateInUse(); return lockersToNotify; } /** * Validates the state of this lock, given that it is known to be in * use. <p> * * Locks that are in use must satisfy the following consistency * constraints: * <ul> * <li>at least one owner * <li>all upgrade waiters precede other waiters * <li>locker appears once in owners and waiters <em>except</em> that * an upgrade request can be a waiter if a read request is an owner * <li>no upgrade waiters if the locker is not a read owner * </ul> * * @return {@code true} if the state is valid, otherwise throws * {@link AssertionError} */ /** {@inheritDoc} */ private boolean validateInUse() { int numWaiters = waiters.size(); int numOwners = owners.size(); if (numOwners == 0) { throw new AssertionError("No owners: " + this); } boolean seenNonUpgrade = false; for (int i = 0; i < numWaiters - 1; i++) { LockRequest<K> waiter = waiters.get(i); if (!waiter.getUpgrade()) { seenNonUpgrade = true; } else if (seenNonUpgrade) { throw new AssertionError( "Upgrade waiter follows non-upgrade: " + this + ", waiters: " + waiters); } for (int j = i + 1; j < numWaiters; j++) { LockRequest<K> waiter2 = waiters.get(j); if (waiter.getLocker() == waiter2.getLocker()) { throw new AssertionError( "Locker waits twice: " + this + ", " + waiter + ", " + waiter2); } } boolean foundOwner = false; for (int j = 0; j < numOwners; j++) { LockRequest<K> owner = owners.get(j); if (waiter.getLocker() == owner.getLocker()) { foundOwner = true; if (!waiter.getUpgrade()) { throw new AssertionError( "Locker owns and waits, but not for" + " upgrade: " + this + ", owner:" + owner + ", waiter:" + waiter); } else if (owner.getForWrite()) { throw new AssertionError( "Locker owns for write but waits for" + " upgrade: " + this + ", owner:" + owner + ", waiter:" + waiter); } } } if (waiter.getUpgrade() && !foundOwner) { throw new AssertionError( "Waiting for upgrade but not owner: " + this + ", waiter: " + waiter); } } return true; } /** * Returns whether this lock is currently in use, or whether it is not * in use and can be removed from the lock table. Locks are in use if * they have any owners or waiters. */ boolean inUse(LockManager<K> lockManager) { assert checkSync(lockManager); return !owners.isEmpty() || !waiters.isEmpty(); } /** * Returns a possibly read-only copy of the lock requests for the owners. */ List<LockRequest<K>> copyOwners(LockManager<K> lockManager) { assert checkSync(lockManager); return copyList(owners); } /** * Returns a possibly read-only copy of the lock requests for the * waiters. */ List<LockRequest<K>> copyWaiters(LockManager<K> lockManager) { assert checkSync(lockManager); return copyList(waiters); } /** * Returns a possibly read-only copy of a list, optimized to save space in * the empty and single-element cases. */ private static <E> List<E> copyList(List<E> list) { if (list.isEmpty()) { return Collections.emptyList(); } else if (list.size() == 1) { return Collections.singletonList(list.get(0)); } else { return new ArrayList<E>(list); } } /** * Removes a locker from the list of waiters for this lock. The locker * must be present in the list. */ void flushWaiter(Locker<K> locker) { assert checkSync(locker.lockManager); for (Iterator<LockRequest<K>> iter = waiters.iterator(); iter.hasNext(); ) { LockRequest<K> request = iter.next(); if (request.getLocker() == locker) { iter.remove(); return; } } throw new AssertionError("Waiter was not found: " + locker); } /** * Checks if the lock is already owned in a way the satisfies the * specified request. */ boolean isOwner(LockRequest<K> request) { assert checkSync(request.getLocker().lockManager); for (LockRequest<K> owner : owners) { if (request.getLocker() == owner.getLocker()) { return !request.getForWrite() || owner.getForWrite(); } } return false; } /** * Notes the start of synchronization on the map associated with {@code * key}. Throws {@link AssertionError} if already synchronized on a * key, otherwise returns {@code true}. */ static <K> boolean noteSync(LockManager<K> lockManager, K key) { K currentKey = lockManager.currentKeySync.get(); if (currentKey != null) { throw new AssertionError( "Attempt to synchronize on map for key " + key + ", but already synchronized on " + currentKey); } lockManager.currentKeySync.set(key); return true; } /** * Notes the end of synchronization on the map associated with {@code * key}. Throws {@link AssertionError} if not already synchronized on * {@code key}, otherwise returns {@code true}. */ static <K> boolean noteUnsync(LockManager<K> lockManager, K key) { K currentKey = lockManager.currentKeySync.get(); if (currentKey == null) { throw new AssertionError( "Attempt to unsynchronize on map for key " + key + ", but not currently synchronized on a key"); } else if (!currentKey.equals(key)) { throw new AssertionError( "Attempt to unsynchronize on map for key " + key + ", but currently synchronized on " + currentKey); } lockManager.currentKeySync.remove(); return true; } /** * Checks that the current thread is not synchronized on the map * associated with a key, throwing {@link AssertionError} if it is, and * otherwise returning {@code true}. */ static <K> boolean checkNoSync(LockManager<K> lockManager) { K currentKey = lockManager.currentKeySync.get(); if (currentKey != null) { throw new AssertionError( "Currently synchronized on key " + currentKey); } return true; } /** * Checks that the current thread is synchronized on the map associated * with this lock, throwing {@link AssertionError} if it is not, and * otherwise returning {@code true}. */ boolean checkSync(LockManager<K> lockManager) { K currentKey = lockManager.currentKeySync.get(); if (currentKey == null) { throw new AssertionError("Currently not synchronized on a key"); } else if (!currentKey.equals(key)) { throw new AssertionError( "Should be synchronized on " + key + ", but currently synchronized on " + currentKey); } return true; } /** Print fields, for debugging. */ @Override public String toString() { return "Lock[" + key + "]"; } }