blob: e479f6aaaca0a3165d04ee1d44a0f6e3f70a9f00 [file] [log] [blame]
/* $Id: LockManager.java 988245 2010-08-23 18:39:35Z kwright $ */
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.manifoldcf.core.lockmanager;
import org.apache.manifoldcf.core.interfaces.*;
import org.apache.manifoldcf.core.system.Logging;
import org.apache.manifoldcf.core.system.ManifoldCF;
import java.util.*;
import java.io.*;
/** A lock manager manages locks and shared information across all threads and JVMs
* and cluster members. There should be no more than ONE instance of this class per thread!!!
* The factory should enforce this.
* This is the base lock manager class. Its implementation works solely within one JVM,
* which makes it ideal for single-process work. Classes that handle multiple JVMs and thus
* need cross-JVM synchronization are thus expected to extend this class and override pertinent
* methods.
*/
public class BaseLockManager implements ILockManager
{
public static final String _rcsid = "@(#)$Id: LockManager.java 988245 2010-08-23 18:39:35Z kwright $";
// These are the lock/section types, in order of escalation
protected final static int TYPE_READ = 1;
protected final static int TYPE_WRITENONEX = 2;
protected final static int TYPE_WRITE = 3;
// These are for locks which putatitively cross JVM boundaries.
// In this implementation, they ar strictly local, and are distinct from sections
// just because of the namespace issues.
protected final LocalLockPool localLocks = new LocalLockPool();
protected final static LockPool myLocks = new LockPool(new LockObjectFactory());
// These are for critical sections (which do not cross JVM boundaries)
protected final LocalLockPool localSections = new LocalLockPool();
protected final static LockPool mySections = new LockPool(new LockObjectFactory());
/** Global flag information. This is used only when all of ManifoldCF is run within one process. */
protected final static Map<String,Boolean> globalFlags = new HashMap<String,Boolean>();
/** Global resource data. Used only when ManifoldCF is run entirely out of one process. */
protected final static Map<String,byte[]> globalData = new HashMap<String,byte[]>();
public BaseLockManager()
throws ManifoldCFException
{
}
// Node synchronization
// The node synchronization model involves keeping track of active agents entities, so that other entities
// can perform any necessary cleanup if one of the agents processes goes away unexpectedly. There is a
// registration primitive (which can fail if the same guid is used as is already registered and active), a
// shutdown primitive (which makes a process id go inactive), and various inspection primitives.
// This implementation of the node infrastructure uses other primitives implemented by the lock
// manager for the implementation. Specifically, instead of synchronizers, we use a write lock
// to prevent conflicts, and we use flags to determine whether a service is active or not. The
// tricky thing, though, is the global registry - which must be able to list its contents. To acheive
// that, we use data with a counter scheme; if the data is not found, it's presumed we are at the
// end of the list.
//
// By building on other primitives in this way, the same implementation will suffice for many derived
// lockmanager implementations - although ZooKeeper will want a native form.
/** The service-type global write lock to control sync, followed by the service type */
protected final static String serviceTypeLockPrefix = "_SERVICELOCK_";
/** A data name prefix, followed by the service type, and then followed by "_" and the instance number */
protected final static String serviceListPrefix = "_SERVICELIST_";
/** A flag prefix, followed by the service type, and then followed by "_" and the service name */
protected final static String servicePrefix = "_SERVICE_";
/** A flag prefix, followed by the service type, and then followed by "_" and the service name */
protected final static String activePrefix = "_ACTIVE_";
/** A data name prefix, followed by the service type, and then followed by "_" and the service name and "_" and the datatype */
protected final static String serviceDataPrefix = "_SERVICEDATA_";
/** Anonymous service name prefix, to be followed by an integer */
protected final static String anonymousServiceNamePrefix = "_ANON_";
/** Anonymous global variable name prefix, to be followed by the service type */
protected final static String anonymousServiceTypeCounter = "_SERVICECOUNTER_";
/** Register a service and begin service activity.
* This atomic operation creates a permanent registration entry for a service.
* If the permanent registration entry already exists, this method will not create it or
* treat it as an error. This operation also enters the "active" zone for the service. The "active" zone will remain in force until it is
* canceled, or until the process is interrupted. Ideally, the corresponding endServiceActivity method will be
* called when the service shuts down. Some ILockManager implementations require that this take place for
* proper management.
* If the transient registration already exists, it is treated as an error and an exception will be thrown.
* If registration will succeed, then this method may call an appropriate IServiceCleanup method to clean up either the
* current service, or all services on the cluster.
*@param serviceType is the type of service.
*@param serviceName is the name of the service to register. If null is passed, a transient unique service name will be
* created, and will be returned to the caller.
*@param cleanup is called to clean up either the current service, or all services of this type, if no other active service exists.
* May be null. Local service cleanup is never called if the serviceName argument is null.
*@return the actual service name.
*/
@Override
public String registerServiceBeginServiceActivity(String serviceType, String serviceName,
IServiceCleanup cleanup)
throws ManifoldCFException
{
return registerServiceBeginServiceActivity(serviceType, serviceName, null, cleanup);
}
/** Register a service and begin service activity.
* This atomic operation creates a permanent registration entry for a service.
* If the permanent registration entry already exists, this method will not create it or
* treat it as an error. This operation also enters the "active" zone for the service. The "active" zone will remain in force until it is
* canceled, or until the process is interrupted. Ideally, the corresponding endServiceActivity method will be
* called when the service shuts down. Some ILockManager implementations require that this take place for
* proper management.
* If the transient registration already exists, it is treated as an error and an exception will be thrown.
* If registration will succeed, then this method may call an appropriate IServiceCleanup method to clean up either the
* current service, or all services on the cluster.
*@param serviceType is the type of service.
*@param serviceName is the name of the service to register. If null is passed, a transient unique service name will be
* created, and will be returned to the caller.
*@param initialData is the initial service data for this service.
*@param cleanup is called to clean up either the current service, or all services of this type, if no other active service exists.
* May be null. Local service cleanup is never called if the serviceName argument is null.
*@return the actual service name.
*/
@Override
public String registerServiceBeginServiceActivity(String serviceType, String serviceName,
byte[] initialData, IServiceCleanup cleanup)
throws ManifoldCFException
{
String serviceTypeLockName = buildServiceTypeLockName(serviceType);
enterWriteLock(serviceTypeLockName);
try
{
if (serviceName == null)
serviceName = constructUniqueServiceName(serviceType);
// First, do an active check
String serviceActiveFlag = makeActiveServiceFlagName(serviceType, serviceName);
if (checkGlobalFlag(serviceActiveFlag))
throw new ManifoldCFException("Service '"+serviceName+"' of type '"+serviceType+"' is already active");
// First, see where we stand.
// We need to find out whether (a) our service is already registered; (b) how many registered services there are;
// (c) whether there are other active services. But no changes will be made at this time.
boolean foundService = false;
boolean foundActiveService = false;
String resourceName;
int i = 0;
while (true)
{
resourceName = buildServiceListEntry(serviceType, i);
String x = readServiceName(resourceName);
if (x == null)
break;
if (x.equals(serviceName))
foundService = true;
else if (checkGlobalFlag(makeActiveServiceFlagName(serviceType, x)))
foundActiveService = true;
i++;
}
// Call the appropriate cleanup. This will depend on what's actually registered, and what's active.
// If there were no services registered at all when we started, then no cleanup is needed, just cluster init.
// If this fails, we must revert to having our service not be registered and not be active.
boolean unregisterAll = false;
if (cleanup != null)
{
if (i == 0)
{
// If we could count on locks never being cleaned up, clusterInit()
// would be sufficient here. But then there's no way to recover from
// a lock clean.
cleanup.cleanUpAllServices();
cleanup.clusterInit();
}
else if (foundService && foundActiveService)
cleanup.cleanUpService(serviceName);
else if (!foundActiveService)
{
cleanup.cleanUpAllServices();
cleanup.clusterInit();
unregisterAll = true;
}
}
if (unregisterAll)
{
// Unregister all (since we did a global cleanup)
int k = i;
while (k > 0)
{
k--;
resourceName = buildServiceListEntry(serviceType, k);
String x = readServiceName(resourceName);
clearGlobalFlag(makeRegisteredServiceFlagName(serviceType, x));
writeServiceName(resourceName, null);
}
foundService = false;
}
// Now, register (if needed)
if (!foundService)
{
writeServiceName(resourceName, serviceName);
try
{
setGlobalFlag(makeRegisteredServiceFlagName(serviceType, serviceName));
}
catch (Throwable e)
{
writeServiceName(resourceName, null);
if (e instanceof Error)
throw (Error)e;
if (e instanceof RuntimeException)
throw (RuntimeException)e;
if (e instanceof ManifoldCFException)
throw (ManifoldCFException)e;
else
throw new RuntimeException("Unknown exception of type: "+e.getClass().getName()+": "+e.getMessage(),e);
}
}
// Last, set the appropriate active flag
setGlobalFlag(serviceActiveFlag);
writeServiceData(serviceType, serviceName, initialData);
return serviceName;
}
finally
{
leaveWriteLock(serviceTypeLockName);
}
}
/** Set service data for a service.
*@param serviceType is the type of service.
*@param serviceName is the name of the service.
*@param serviceData is the data to update to (may be null).
* This updates the service's transient data (or deletes it). If the service is not active, an exception is thrown.
*/
@Override
public void updateServiceData(String serviceType, String serviceName, byte[] serviceData)
throws ManifoldCFException
{
String serviceTypeLockName = buildServiceTypeLockName(serviceType);
enterWriteLock(serviceTypeLockName);
try
{
String serviceActiveFlag = makeActiveServiceFlagName(serviceType, serviceName);
if (!checkGlobalFlag(serviceActiveFlag))
throw new ManifoldCFException("Service '"+serviceName+"' of type '"+serviceType+"' is not active");
writeServiceData(serviceType, serviceName, serviceData);
}
finally
{
leaveWriteLock(serviceTypeLockName);
}
}
/** Retrieve service data for a service.
*@param serviceType is the type of service.
*@param serviceName is the name of the service.
*@return the service's transient data.
*/
@Override
public byte[] retrieveServiceData(String serviceType, String serviceName)
throws ManifoldCFException
{
String serviceTypeLockName = buildServiceTypeLockName(serviceType);
enterReadLock(serviceTypeLockName);
try
{
String serviceActiveFlag = makeActiveServiceFlagName(serviceType, serviceName);
if (!checkGlobalFlag(serviceActiveFlag))
return null;
byte[] rval = readServiceData(serviceType, serviceName);
if (rval == null)
rval = new byte[0];
return rval;
}
finally
{
leaveReadLock(serviceTypeLockName);
}
}
/** Scan service data for a service type. Only active service data will be considered.
*@param serviceType is the type of service.
*@param dataType is the type of data.
*@param dataAcceptor is the object that will be notified of each item of data for each service name found.
*/
@Override
public void scanServiceData(String serviceType, IServiceDataAcceptor dataAcceptor)
throws ManifoldCFException
{
String serviceTypeLockName = buildServiceTypeLockName(serviceType);
enterReadLock(serviceTypeLockName);
try
{
int i = 0;
while (true)
{
String resourceName = buildServiceListEntry(serviceType, i);
String x = readServiceName(resourceName);
if (x == null)
break;
if (checkGlobalFlag(makeActiveServiceFlagName(serviceType, x)))
{
byte[] serviceData = readServiceData(serviceType, x);
if (dataAcceptor.acceptServiceData(x, serviceData))
break;
}
i++;
}
}
finally
{
leaveReadLock(serviceTypeLockName);
}
}
/** Count all active services of a given type.
*@param serviceType is the service type.
*@return the count.
*/
@Override
public int countActiveServices(String serviceType)
throws ManifoldCFException
{
String serviceTypeLockName = buildServiceTypeLockName(serviceType);
enterReadLock(serviceTypeLockName);
try
{
int count = 0;
int i = 0;
while (true)
{
String resourceName = buildServiceListEntry(serviceType, i);
String x = readServiceName(resourceName);
if (x == null)
break;
if (checkGlobalFlag(makeActiveServiceFlagName(serviceType, x)))
count++;
i++;
}
return count;
}
finally
{
leaveReadLock(serviceTypeLockName);
}
}
/** Clean up any inactive services found.
* Calling this method will invoke cleanup of one inactive service at a time.
* If there are no inactive services around, then false will be returned.
* Note that this method will block whatever service it finds from starting up
* for the time the cleanup is proceeding. At the end of the cleanup, if
* successful, the service will be atomically unregistered.
*@param serviceType is the service type.
*@param cleanup is the object to call to clean up an inactive service.
*@return true if there were no cleanup operations necessary.
*/
@Override
public boolean cleanupInactiveService(String serviceType, IServiceCleanup cleanup)
throws ManifoldCFException
{
String serviceTypeLockName = buildServiceTypeLockName(serviceType);
enterWriteLock(serviceTypeLockName);
try
{
// We find ONE service that is registered but inactive, and clean up after that one.
// Presumably the caller will lather, rinse, and repeat.
String serviceName;
String resourceName;
int i = 0;
while (true)
{
resourceName = buildServiceListEntry(serviceType, i);
serviceName = readServiceName(resourceName);
if (serviceName == null)
return true;
if (!checkGlobalFlag(makeActiveServiceFlagName(serviceType, serviceName)))
break;
i++;
}
// Found one, in serviceName, at position i
// Ideally, we should signal at this point that we're cleaning up after it, and then leave
// the exclusive lock, so that other activity can take place. MHL
cleanup.cleanUpService(serviceName);
// Clean up the registration
String serviceRegisteredFlag = makeRegisteredServiceFlagName(serviceType, serviceName);
// Find the end of the list
int k = i + 1;
String lastResourceName = null;
String lastServiceName = null;
while (true)
{
String rName = buildServiceListEntry(serviceType, k);
String x = readServiceName(rName);
if (x == null)
break;
lastResourceName = rName;
lastServiceName = x;
k++;
}
// Rearrange the registration
clearGlobalFlag(serviceRegisteredFlag);
if (lastServiceName != null)
writeServiceName(resourceName, lastServiceName);
writeServiceName(lastResourceName, null);
return false;
}
finally
{
leaveWriteLock(serviceTypeLockName);
}
}
/** End service activity.
* This operation exits the "active" zone for the service. This must take place using the same ILockManager
* object that was used to registerServiceBeginServiceActivity() - which implies that it is the same thread.
*@param serviceType is the type of service.
*@param serviceName is the name of the service to exit.
*/
@Override
public void endServiceActivity(String serviceType, String serviceName)
throws ManifoldCFException
{
String serviceTypeLockName = buildServiceTypeLockName(serviceType);
enterWriteLock(serviceTypeLockName);
try
{
String serviceActiveFlag = makeActiveServiceFlagName(serviceType, serviceName);
if (!checkGlobalFlag(serviceActiveFlag))
throw new ManifoldCFException("Service '"+serviceName+"' of type '"+serviceType+" is not active");
deleteServiceData(serviceType, serviceName);
clearGlobalFlag(serviceActiveFlag);
}
finally
{
leaveWriteLock(serviceTypeLockName);
}
}
/** Check whether a service is active or not.
* This operation returns true if the specified service is considered active at the moment. Once a service
* is not active anymore, it can only return to activity by calling beginServiceActivity() once more.
*@param serviceType is the type of service.
*@param serviceName is the name of the service to check on.
*@return true if the service is considered active.
*/
@Override
public boolean checkServiceActive(String serviceType, String serviceName)
throws ManifoldCFException
{
String serviceTypeLockName = buildServiceTypeLockName(serviceType);
enterReadLock(serviceTypeLockName);
try
{
return checkGlobalFlag(makeActiveServiceFlagName(serviceType, serviceName));
}
finally
{
leaveReadLock(serviceTypeLockName);
}
}
/** Construct a unique service name given the service type.
*/
protected String constructUniqueServiceName(String serviceType)
throws ManifoldCFException
{
String serviceCounterName = makeServiceCounterName(serviceType);
int serviceUID = readServiceCounter(serviceCounterName);
writeServiceCounter(serviceCounterName,serviceUID+1);
return anonymousServiceNamePrefix + serviceUID;
}
/** Make the service counter name for a service type.
*/
protected static String makeServiceCounterName(String serviceType)
{
return anonymousServiceTypeCounter + serviceType;
}
/** Read service counter.
*/
protected int readServiceCounter(String serviceCounterName)
throws ManifoldCFException
{
byte[] serviceCounterData = readData(serviceCounterName);
if (serviceCounterData == null || serviceCounterData.length != 4)
return 0;
return ((int)serviceCounterData[0]) & 0xff +
(((int)serviceCounterData[1]) << 8) & 0xff00 +
(((int)serviceCounterData[2]) << 16) & 0xff0000 +
(((int)serviceCounterData[3]) << 24) & 0xff000000;
}
/** Write service counter.
*/
protected void writeServiceCounter(String serviceCounterName, int counter)
throws ManifoldCFException
{
byte[] serviceCounterData = new byte[4];
serviceCounterData[0] = (byte)(counter & 0xff);
serviceCounterData[1] = (byte)((counter >> 8) & 0xff);
serviceCounterData[2] = (byte)((counter >> 16) & 0xff);
serviceCounterData[3] = (byte)((counter >> 24) & 0xff);
writeData(serviceCounterName,serviceCounterData);
}
protected void writeServiceData(String serviceType, String serviceName, byte[] serviceData)
throws ManifoldCFException
{
writeData(makeServiceDataName(serviceType, serviceName), serviceData);
}
protected byte[] readServiceData(String serviceType, String serviceName)
throws ManifoldCFException
{
return readData(makeServiceDataName(serviceType, serviceName));
}
protected void deleteServiceData(String serviceType, String serviceName)
throws ManifoldCFException
{
writeServiceData(serviceType, serviceName, null);
}
protected static String makeServiceDataName(String serviceType, String serviceName)
{
return serviceDataPrefix + serviceType + "_" + serviceName;
}
protected static String makeActiveServiceFlagName(String serviceType, String serviceName)
{
return activePrefix + serviceType + "_" + serviceName;
}
protected static String makeRegisteredServiceFlagName(String serviceType, String serviceName)
{
return servicePrefix + serviceType + "_" + serviceName;
}
protected String readServiceName(String resourceName)
throws ManifoldCFException
{
byte[] bytes = readData(resourceName);
if (bytes == null)
return null;
try
{
return new String(bytes, "utf-8");
}
catch (UnsupportedEncodingException e)
{
throw new RuntimeException("Unsupported encoding: "+e.getMessage(),e);
}
}
protected void writeServiceName(String resourceName, String serviceName)
throws ManifoldCFException
{
try
{
writeData(resourceName, (serviceName==null)?null:serviceName.getBytes("utf-8"));
}
catch (UnsupportedEncodingException e)
{
throw new RuntimeException("Unsupported encoding: "+e.getMessage(),e);
}
}
protected static String buildServiceListEntry(String serviceType, int i)
{
return serviceListPrefix + serviceType + "_" + i;
}
protected static String buildServiceTypeLockName(String serviceType)
{
return serviceTypeLockPrefix + serviceType;
}
/** Get the current shared configuration. This configuration is available in common among all nodes,
* and thus must not be accessed through here for the purpose of finding configuration data that is specific to any one
* specific node.
*@param configurationData is the globally-shared configuration information.
*/
@Override
public ManifoldCFConfiguration getSharedConfiguration()
throws ManifoldCFException
{
// Local implementation vectors through to system property file, which is shared in this case
return ManifoldCF.getConfiguration();
}
/** Raise a flag. Use this method to assert a condition, or send a global signal. The flag will be reset when the
* entire system is restarted.
*@param flagName is the name of the flag to set.
*/
@Override
public void setGlobalFlag(String flagName)
throws ManifoldCFException
{
// Keep local flag information in memory
synchronized (globalFlags)
{
globalFlags.put(flagName,new Boolean(true));
}
}
/** Clear a flag. Use this method to clear a condition, or retract a global signal.
*@param flagName is the name of the flag to clear.
*/
@Override
public void clearGlobalFlag(String flagName)
throws ManifoldCFException
{
// Keep flag information in memory
synchronized (globalFlags)
{
globalFlags.remove(flagName);
}
}
/** Check the condition of a specified flag.
*@param flagName is the name of the flag to check.
*@return true if the flag is set, false otherwise.
*/
@Override
public boolean checkGlobalFlag(String flagName)
throws ManifoldCFException
{
// Keep flag information in memory
synchronized (globalFlags)
{
return globalFlags.get(flagName) != null;
}
}
/** Read data from a shared data resource. Use this method to read any existing data, or get a null back if there is no such resource.
* Note well that this is not necessarily an atomic operation, and it must thus be protected by a lock.
*@param resourceName is the global name of the resource.
*@return a byte array containing the data, or null.
*/
@Override
public byte[] readData(String resourceName)
throws ManifoldCFException
{
// Keep resource data local
synchronized (globalData)
{
return globalData.get(resourceName);
}
}
/** Write data to a shared data resource. Use this method to write a body of data into a shared resource.
* Note well that this is not necessarily an atomic operation, and it must thus be protected by a lock.
*@param resourceName is the global name of the resource.
*@param data is the byte array containing the data. Pass null if you want to delete the resource completely.
*/
@Override
public void writeData(String resourceName, byte[] data)
throws ManifoldCFException
{
// Keep resource data local
synchronized (globalData)
{
if (data == null)
globalData.remove(resourceName);
else
globalData.put(resourceName,data);
}
}
/** Wait for a time before retrying a lock.
*/
@Override
public final void timedWait(int time)
throws ManifoldCFException
{
if (Logging.lock.isDebugEnabled())
{
Logging.lock.debug("Waiting for time "+Integer.toString(time));
}
try
{
ManifoldCF.sleep(time);
}
catch (InterruptedException e)
{
throw new ManifoldCFException("Interrupted",e,ManifoldCFException.INTERRUPTED);
}
}
/** Override this method to change the nature of global locks.
*/
protected LockPool getGlobalLockPool()
{
return myLocks;
}
/** Enter a non-exclusive write-locked area (blocking out all readers, but letting in other "writers").
* This kind of lock is designed to be used in conjunction with read locks. It is used typically in
* a situation where the read lock represents a query and the non-exclusive write lock represents a modification
* to an individual item that might affect the query, but where multiple modifications do not individually
* interfere with one another (use of another, standard, write lock per item can guarantee this).
*/
@Override
public final void enterNonExWriteLock(String lockKey)
throws ManifoldCFException
{
enterNonExWrite(lockKey, "lock", localLocks, getGlobalLockPool());
}
@Override
public final void enterNonExWriteLockNoWait(String lockKey)
throws ManifoldCFException, LockException
{
enterNonExWriteNoWait(lockKey, "lock", localLocks, getGlobalLockPool());
}
/** Leave a non-exclusive write lock.
*/
@Override
public final void leaveNonExWriteLock(String lockKey)
throws ManifoldCFException
{
leaveNonExWrite(lockKey, "lock", localLocks, getGlobalLockPool());
}
/** Enter a write locked area (i.e., block out both readers and other writers)
* NOTE: Can't enter until all readers have left.
*/
@Override
public final void enterWriteLock(String lockKey)
throws ManifoldCFException
{
enterWrite(lockKey, "lock", localLocks, getGlobalLockPool());
}
@Override
public final void enterWriteLockNoWait(String lockKey)
throws ManifoldCFException, LockException
{
enterWriteNoWait(lockKey, "lock", localLocks, getGlobalLockPool());
}
@Override
public final void leaveWriteLock(String lockKey)
throws ManifoldCFException
{
leaveWrite(lockKey, "lock", localLocks, getGlobalLockPool());
}
/** Enter a read-only locked area (i.e., block ONLY if there's a writer)
*/
@Override
public final void enterReadLock(String lockKey)
throws ManifoldCFException
{
enterRead(lockKey, "lock", localLocks, getGlobalLockPool());
}
@Override
public final void enterReadLockNoWait(String lockKey)
throws ManifoldCFException, LockException
{
enterReadNoWait(lockKey, "lock", localLocks, getGlobalLockPool());
}
@Override
public final void leaveReadLock(String lockKey)
throws ManifoldCFException
{
leaveRead(lockKey, "lock", localLocks, getGlobalLockPool());
}
/** Enter multiple locks
*/
@Override
public final void enterLocks(String[] readLocks, String[] nonExWriteLocks, String[] writeLocks)
throws ManifoldCFException
{
enter(readLocks, nonExWriteLocks, writeLocks, "lock", localLocks, getGlobalLockPool());
}
@Override
public final void enterLocksNoWait(String[] readLocks, String[] nonExWriteLocks, String[] writeLocks)
throws ManifoldCFException, LockException
{
enterNoWait(readLocks, nonExWriteLocks, writeLocks, "lock", localLocks, getGlobalLockPool());
}
/** Leave multiple locks
*/
@Override
public final void leaveLocks(String[] readLocks, String[] writeNonExLocks, String[] writeLocks)
throws ManifoldCFException
{
leave(readLocks, writeNonExLocks, writeLocks, "lock", localLocks, getGlobalLockPool());
}
@Override
public final void clearLocks()
throws ManifoldCFException
{
clear("lock", localLocks, getGlobalLockPool());
}
/** Enter a named, read critical section (NOT a lock). Critical sections never cross JVM boundaries.
* Critical section names do not collide with lock names; they have a distinct namespace.
*@param sectionKey is the name of the section to enter. Only one thread can be in any given named
* section at a time.
*/
@Override
public final void enterReadCriticalSection(String sectionKey)
throws ManifoldCFException
{
enterRead(sectionKey, "critical section", localSections, mySections);
}
/** Leave a named, read critical section (NOT a lock). Critical sections never cross JVM boundaries.
* Critical section names do not collide with lock names; they have a distinct namespace.
*@param sectionKey is the name of the section to leave. Only one thread can be in any given named
* section at a time.
*/
@Override
public final void leaveReadCriticalSection(String sectionKey)
throws ManifoldCFException
{
leaveRead(sectionKey, "critical section", localSections, mySections);
}
/** Enter a named, non-exclusive write critical section (NOT a lock). Critical sections never cross JVM boundaries.
* Critical section names do not collide with lock names; they have a distinct namespace.
*@param sectionKey is the name of the section to enter. Only one thread can be in any given named
* section at a time.
*/
@Override
public final void enterNonExWriteCriticalSection(String sectionKey)
throws ManifoldCFException
{
enterNonExWrite(sectionKey, "critical section", localSections, mySections);
}
/** Leave a named, non-exclusive write critical section (NOT a lock). Critical sections never cross JVM boundaries.
* Critical section names do not collide with lock names; they have a distinct namespace.
*@param sectionKey is the name of the section to leave. Only one thread can be in any given named
* section at a time.
*/
@Override
public final void leaveNonExWriteCriticalSection(String sectionKey)
throws ManifoldCFException
{
leaveNonExWrite(sectionKey, "critical section", localSections, mySections);
}
/** Enter a named, exclusive critical section (NOT a lock). Critical sections never cross JVM boundaries.
* Critical section names should be distinct from all lock names.
*@param sectionKey is the name of the section to enter. Only one thread can be in any given named
* section at a time.
*/
@Override
public final void enterWriteCriticalSection(String sectionKey)
throws ManifoldCFException
{
enterWrite(sectionKey, "critical section", localSections, mySections);
}
/** Leave a named, exclusive critical section (NOT a lock). Critical sections never cross JVM boundaries.
* Critical section names should be distinct from all lock names.
*@param sectionKey is the name of the section to leave. Only one thread can be in any given named
* section at a time.
*/
@Override
public final void leaveWriteCriticalSection(String sectionKey)
throws ManifoldCFException
{
leaveWrite(sectionKey, "critical section", localSections, mySections);
}
/** Enter multiple critical sections simultaneously.
*@param readSectionKeys is an array of read section descriptors, or null if there are no read sections desired.
*@param nonExSectionKeys is an array of non-ex write section descriptors, or null if none desired.
*@param writeSectionKeys is an array of write section descriptors, or null if there are none desired.
*/
@Override
public final void enterCriticalSections(String[] readSectionKeys, String[] nonExSectionKeys, String[] writeSectionKeys)
throws ManifoldCFException
{
enter(readSectionKeys, nonExSectionKeys, writeSectionKeys, "critical section", localSections, mySections);
}
/** Leave multiple critical sections simultaneously.
*@param readSectionKeys is an array of read section descriptors, or null if there are no read sections desired.
*@param nonExSectionKeys is an array of non-ex write section descriptors, or null if none desired.
*@param writeSectionKeys is an array of write section descriptors, or null if there are none desired.
*/
@Override
public final void leaveCriticalSections(String[] readSectionKeys, String[] nonExSectionKeys, String[] writeSectionKeys)
throws ManifoldCFException
{
leave(readSectionKeys, nonExSectionKeys, writeSectionKeys, "critical section", localSections, mySections);
}
// Protected methods
protected static void enterNonExWrite(String lockKey, String description, LocalLockPool localLocks, LockPool crossLocks)
throws ManifoldCFException
{
if (Logging.lock.isDebugEnabled())
Logging.lock.debug("Entering non-ex write "+description+" '"+lockKey+"'");
LocalLock ll = localLocks.getLocalLock(lockKey);
// See if we already own a write lock for the object
// If we do, there is no reason to change the status of the global lock we own.
if (ll.hasNonExWriteLock() || ll.hasWriteLock())
{
ll.incrementNonExWriteLocks();
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Successfully obtained "+description+"!");
return;
}
// Check for illegalities
if (ll.hasReadLock())
{
throw new ManifoldCFException("Illegal "+description+" sequence: NonExWrite "+description+" can't be within read "+description,ManifoldCFException.GENERAL_ERROR);
}
// We don't own a local non-ex write lock. Get one. The global lock will need
// to know if we already have a a read lock.
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
try
{
lo.enterNonExWriteLock();
break;
}
catch (InterruptedException e)
{
throw new ManifoldCFException("Interrupted",e,ManifoldCFException.INTERRUPTED);
}
catch (ExpiredObjectException e)
{
// Try again to get a valid object
}
}
ll.incrementNonExWriteLocks();
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Successfully obtained "+description+"!");
}
protected static void enterNonExWriteNoWait(String lockKey, String description, LocalLockPool localLocks, LockPool crossLocks)
throws ManifoldCFException, LockException
{
if (Logging.lock.isDebugEnabled())
Logging.lock.debug("Entering non-ex write "+description+" no wait '"+lockKey+"'");
LocalLock ll = localLocks.getLocalLock(lockKey);
// See if we already own a write lock for the object
// If we do, there is no reason to change the status of the global lock we own.
if (ll.hasNonExWriteLock() || ll.hasWriteLock())
{
ll.incrementNonExWriteLocks();
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Successfully obtained "+description+"!");
return;
}
// Check for illegalities
if (ll.hasReadLock())
{
throw new ManifoldCFException("Illegal "+description+" sequence: NonExWrite "+description+" can't be within read "+description,ManifoldCFException.GENERAL_ERROR);
}
// We don't own a local non-ex write lock. Get one. The global lock will need
// to know if we already have a a read lock.
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
try
{
synchronized (lo)
{
lo.enterNonExWriteLockNoWait();
break;
}
}
catch (LocalLockException e)
{
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Could not non-ex write "+description+" '"+lockKey+"', lock exception");
// Throw LockException instead
throw new LockException(e.getMessage());
}
catch (InterruptedException e)
{
throw new ManifoldCFException("Interrupted",e,ManifoldCFException.INTERRUPTED);
}
catch (ExpiredObjectException e)
{
// Try again to get a valid object
}
}
ll.incrementNonExWriteLocks();
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Successfully obtained "+description+"!");
}
protected static void leaveNonExWrite(String lockKey, String description, LocalLockPool localLocks, LockPool crossLocks)
throws ManifoldCFException
{
if (Logging.lock.isDebugEnabled())
Logging.lock.debug("Leaving non-ex write "+description+" '"+lockKey+"'");
LocalLock ll = localLocks.getLocalLock(lockKey);
ll.decrementNonExWriteLocks();
// See if we no longer have a write lock for the object.
// If we retain the stronger exclusive lock, we still do not need to
// change the status of the global lock.
if (!(ll.hasNonExWriteLock() || ll.hasWriteLock()))
{
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
try
{
lo.leaveNonExWriteLock();
break;
}
catch (InterruptedException e)
{
// try one more time
try
{
lo.leaveNonExWriteLock();
throw new ManifoldCFException("Interrupted",e,ManifoldCFException.INTERRUPTED);
}
catch (InterruptedException e2)
{
ll.incrementNonExWriteLocks();
throw new ManifoldCFException("Interrupted",e2,ManifoldCFException.INTERRUPTED);
}
catch (ExpiredObjectException e2)
{
ll.incrementNonExWriteLocks();
throw new ManifoldCFException("Interrupted",e,ManifoldCFException.INTERRUPTED);
}
}
catch (ExpiredObjectException e)
{
// Try again to get a valid object
}
}
localLocks.releaseLocalLock(lockKey);
}
}
protected static void enterWrite(String lockKey, String description, LocalLockPool localLocks, LockPool crossLocks)
throws ManifoldCFException
{
if (Logging.lock.isDebugEnabled())
Logging.lock.debug("Entering write "+description+" '"+lockKey+"'");
LocalLock ll = localLocks.getLocalLock(lockKey);
// See if we already own the write lock for the object
if (ll.hasWriteLock())
{
ll.incrementWriteLocks();
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Successfully obtained "+description+"!");
return;
}
// Check for illegalities
if (ll.hasReadLock() || ll.hasNonExWriteLock())
{
throw new ManifoldCFException("Illegal "+description+" sequence: Write "+description+" can't be within read "+description+" or non-ex write "+description,ManifoldCFException.GENERAL_ERROR);
}
// We don't own a local write lock. Get one. The global lock will need
// to know if we already have a non-exclusive lock or a read lock, which we don't because
// it's illegal.
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
try
{
lo.enterWriteLock();
break;
}
catch (InterruptedException e)
{
throw new ManifoldCFException("Interrupted",e,ManifoldCFException.INTERRUPTED);
}
catch (ExpiredObjectException e)
{
// Try again
}
}
ll.incrementWriteLocks();
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Successfully obtained "+description+"!");
}
protected static void enterWriteNoWait(String lockKey, String description, LocalLockPool localLocks, LockPool crossLocks)
throws ManifoldCFException, LockException
{
if (Logging.lock.isDebugEnabled())
Logging.lock.debug("Entering write "+description+" no wait '"+lockKey+"'");
LocalLock ll = localLocks.getLocalLock(lockKey);
// See if we already own the write lock for the object
if (ll.hasWriteLock())
{
ll.incrementWriteLocks();
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Successfully obtained "+description+"!");
return;
}
// Check for illegalities
if (ll.hasReadLock() || ll.hasNonExWriteLock())
{
throw new ManifoldCFException("Illegal "+description+" sequence: Write "+description+" can't be within read "+description+" or non-ex write "+description,ManifoldCFException.GENERAL_ERROR);
}
// We don't own a local write lock. Get one. The global lock will need
// to know if we already have a non-exclusive lock or a read lock, which we don't because
// it's illegal.
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
try
{
synchronized (lo)
{
lo.enterWriteLockNoWait();
break;
}
}
catch (LocalLockException e)
{
if (Logging.lock.isDebugEnabled())
{
Logging.lock.debug(" Could not write "+description+" '"+lockKey+"', lock exception");
}
throw new LockException(e.getMessage());
}
catch (InterruptedException e)
{
throw new ManifoldCFException("Interrupted",e,ManifoldCFException.INTERRUPTED);
}
catch (ExpiredObjectException e)
{
// Try again
}
}
ll.incrementWriteLocks();
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Successfully obtained "+description+"!");
}
protected static void leaveWrite(String lockKey, String description, LocalLockPool localLocks, LockPool crossLocks)
throws ManifoldCFException
{
if (Logging.lock.isDebugEnabled())
Logging.lock.debug("Leaving write "+description+" '"+lockKey+"'");
LocalLock ll = localLocks.getLocalLock(lockKey);
ll.decrementWriteLocks();
if (!ll.hasWriteLock())
{
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
try
{
lo.leaveWriteLock();
break;
}
catch (InterruptedException e)
{
// try one more time
try
{
lo.leaveWriteLock();
throw new ManifoldCFException("Interrupted",e,ManifoldCFException.INTERRUPTED);
}
catch (InterruptedException e2)
{
ll.incrementWriteLocks();
throw new ManifoldCFException("Interrupted",e2,ManifoldCFException.INTERRUPTED);
}
catch (ExpiredObjectException e2)
{
ll.incrementWriteLocks();
throw new ManifoldCFException("Interrupted",e,ManifoldCFException.INTERRUPTED);
}
}
catch (ExpiredObjectException e)
{
// Try again
}
}
localLocks.releaseLocalLock(lockKey);
}
}
protected static void enterRead(String lockKey, String description, LocalLockPool localLocks, LockPool crossLocks)
throws ManifoldCFException
{
if (Logging.lock.isDebugEnabled())
Logging.lock.debug("Entering read "+description+" '"+lockKey+"'");
LocalLock ll = localLocks.getLocalLock(lockKey);
// See if we already own the read lock for the object.
// Write locks or non-ex writelocks count as well (they're stronger).
if (ll.hasReadLock() || ll.hasNonExWriteLock() || ll.hasWriteLock())
{
ll.incrementReadLocks();
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Successfully obtained "+description+"!");
return;
}
// We don't own a local read lock. Get one.
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
try
{
lo.enterReadLock();
break;
}
catch (InterruptedException e)
{
throw new ManifoldCFException("Interrupted",e,ManifoldCFException.INTERRUPTED);
}
catch (ExpiredObjectException e)
{
// Try again
}
}
ll.incrementReadLocks();
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Successfully obtained "+description+"!");
}
protected static void enterReadNoWait(String lockKey, String description, LocalLockPool localLocks, LockPool crossLocks)
throws ManifoldCFException, LockException
{
if (Logging.lock.isDebugEnabled())
Logging.lock.debug("Entering read "+description+" no wait '"+lockKey+"'");
LocalLock ll = localLocks.getLocalLock(lockKey);
// See if we already own the read lock for the object.
// Write locks or non-ex writelocks count as well (they're stronger).
if (ll.hasReadLock() || ll.hasNonExWriteLock() || ll.hasWriteLock())
{
ll.incrementReadLocks();
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Successfully obtained "+description+"!");
return;
}
// We don't own a local read lock. Get one.
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
try
{
synchronized (lo)
{
lo.enterReadLockNoWait();
break;
}
}
catch (LocalLockException e)
{
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Could not read "+description+" '"+lockKey+"', lock exception");
throw new LockException(e.getMessage());
}
catch (InterruptedException e)
{
throw new ManifoldCFException("Interrupted",e,ManifoldCFException.INTERRUPTED);
}
catch (ExpiredObjectException e)
{
// Try again
}
}
ll.incrementReadLocks();
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Successfully obtained "+description+"!");
}
protected static void leaveRead(String lockKey, String description, LocalLockPool localLocks, LockPool crossLocks)
throws ManifoldCFException
{
if (Logging.lock.isDebugEnabled())
Logging.lock.debug("Leaving read "+description+" '"+lockKey+"'");
LocalLock ll = localLocks.getLocalLock(lockKey);
ll.decrementReadLocks();
if (!(ll.hasReadLock() || ll.hasNonExWriteLock() || ll.hasWriteLock()))
{
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
try
{
lo.leaveReadLock();
break;
}
catch (InterruptedException e)
{
// Try one more time
try
{
lo.leaveReadLock();
throw new ManifoldCFException("Interrupted",e,ManifoldCFException.INTERRUPTED);
}
catch (InterruptedException e2)
{
ll.incrementReadLocks();
throw new ManifoldCFException("Interrupted",e2,ManifoldCFException.INTERRUPTED);
}
catch (ExpiredObjectException e2)
{
ll.incrementReadLocks();
throw new ManifoldCFException("Interrupted",e,ManifoldCFException.INTERRUPTED);
}
}
catch (ExpiredObjectException e)
{
// Try again
}
}
localLocks.releaseLocalLock(lockKey);
}
}
protected static void clear(String description, LocalLockPool localLocks, LockPool crossLocks)
throws ManifoldCFException
{
if (Logging.lock.isDebugEnabled())
Logging.lock.debug("Clearing all "+description+"s");
for (String keyValue : localLocks.keySet())
{
LocalLock ll = localLocks.getLocalLock(keyValue);
while (ll.hasWriteLock())
leaveWrite(keyValue, description, localLocks, crossLocks);
while (ll.hasNonExWriteLock())
leaveNonExWrite(keyValue, description, localLocks, crossLocks);
while (ll.hasReadLock())
leaveRead(keyValue, description, localLocks, crossLocks);
}
}
protected static void enter(String[] readLocks, String[] nonExWriteLocks, String[] writeLocks, String description, LocalLockPool localLocks, LockPool crossLocks)
throws ManifoldCFException
{
if (Logging.lock.isDebugEnabled())
{
Logging.lock.debug("Entering multiple "+description+"s:");
int i;
if (readLocks != null)
{
i = 0;
while (i < readLocks.length)
{
Logging.lock.debug(" Read "+description+" '"+readLocks[i++]+"'");
}
}
if (nonExWriteLocks != null)
{
i = 0;
while (i < nonExWriteLocks.length)
{
Logging.lock.debug(" Non-ex write "+description+" '"+nonExWriteLocks[i++]+"'");
}
}
if (writeLocks != null)
{
i = 0;
while (i < writeLocks.length)
{
Logging.lock.debug(" Write "+description+" '"+writeLocks[i++]+"'");
}
}
}
// Sort the locks. This improves the chances of making it through the locking process without
// contention!
LockDescription lds[] = getSortedUniqueLocks(readLocks,nonExWriteLocks,writeLocks);
int locksProcessed = 0;
try
{
while (locksProcessed < lds.length)
{
LockDescription ld = lds[locksProcessed];
int lockType = ld.getType();
String lockKey = ld.getKey();
LocalLock ll;
switch (lockType)
{
case TYPE_WRITE:
ll = localLocks.getLocalLock(lockKey);
// Check for illegalities
if ((ll.hasReadLock() || ll.hasNonExWriteLock()) && !ll.hasWriteLock())
{
throw new ManifoldCFException("Illegal "+description+" sequence: Write "+description+" can't be within read "+description+" or non-ex write "+description,ManifoldCFException.GENERAL_ERROR);
}
// See if we already own the write lock for the object
if (!ll.hasWriteLock())
{
// We don't own a local write lock. Get one.
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
try
{
lo.enterWriteLock();
break;
}
catch (ExpiredObjectException e)
{
// Try again
}
}
}
ll.incrementWriteLocks();
break;
case TYPE_WRITENONEX:
ll = localLocks.getLocalLock(lockKey);
// Check for illegalities
if (ll.hasReadLock() && !(ll.hasNonExWriteLock() || ll.hasWriteLock()))
{
throw new ManifoldCFException("Illegal "+description+" sequence: NonExWrite "+description+" can't be within read "+description,ManifoldCFException.GENERAL_ERROR);
}
// See if we already own the write lock for the object
if (!(ll.hasNonExWriteLock() || ll.hasWriteLock()))
{
// We don't own a local write lock. Get one.
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
try
{
lo.enterNonExWriteLock();
break;
}
catch (ExpiredObjectException e)
{
// Try again
}
}
}
ll.incrementNonExWriteLocks();
break;
case TYPE_READ:
ll = localLocks.getLocalLock(lockKey);
if (!(ll.hasReadLock() || ll.hasNonExWriteLock() || ll.hasWriteLock()))
{
// We don't own a local read lock. Get one.
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
try
{
lo.enterReadLock();
break;
}
catch (ExpiredObjectException e)
{
// Try again
}
}
}
ll.incrementReadLocks();
break;
}
locksProcessed++;
}
// Got all; we are done!
Logging.lock.debug(" Successfully obtained multiple "+description+"s!");
return;
}
catch (Throwable ex)
{
// No matter what, undo the locks we've taken
ManifoldCFException ae = null;
int errno = 0;
while (--locksProcessed >= 0)
{
LockDescription ld = lds[locksProcessed];
int lockType = ld.getType();
String lockKey = ld.getKey();
try
{
switch (lockType)
{
case TYPE_READ:
leaveRead(lockKey,description,localLocks,crossLocks);
break;
case TYPE_WRITENONEX:
leaveNonExWrite(lockKey,description,localLocks,crossLocks);
break;
case TYPE_WRITE:
leaveWrite(lockKey,description,localLocks,crossLocks);
break;
}
}
catch (ManifoldCFException e)
{
ae = e;
}
}
if (ae != null)
{
throw ae;
}
if (ex instanceof ManifoldCFException)
{
throw (ManifoldCFException)ex;
}
if (ex instanceof InterruptedException)
{
// It's InterruptedException
throw new ManifoldCFException("Interrupted",ex,ManifoldCFException.INTERRUPTED);
}
if (!(ex instanceof Error))
{
throw new Error("Unexpected exception",ex);
}
throw (Error)ex;
}
}
protected static void enterNoWait(String[] readLocks, String[] nonExWriteLocks, String[] writeLocks, String description, LocalLockPool localLocks, LockPool crossLocks)
throws ManifoldCFException, LockException
{
if (Logging.lock.isDebugEnabled())
{
Logging.lock.debug("Entering multiple "+description+"s no wait:");
int i;
if (readLocks != null)
{
i = 0;
while (i < readLocks.length)
{
Logging.lock.debug(" Read "+description+" '"+readLocks[i++]+"'");
}
}
if (nonExWriteLocks != null)
{
i = 0;
while (i < nonExWriteLocks.length)
{
Logging.lock.debug(" Non-ex write "+description+" '"+nonExWriteLocks[i++]+"'");
}
}
if (writeLocks != null)
{
i = 0;
while (i < writeLocks.length)
{
Logging.lock.debug(" Write "+description+" '"+writeLocks[i++]+"'");
}
}
}
// Sort the locks. This improves the chances of making it through the locking process without
// contention!
LockDescription lds[] = getSortedUniqueLocks(readLocks,nonExWriteLocks,writeLocks);
int locksProcessed = 0;
try
{
while (locksProcessed < lds.length)
{
LockDescription ld = lds[locksProcessed];
int lockType = ld.getType();
String lockKey = ld.getKey();
LocalLock ll;
switch (lockType)
{
case TYPE_WRITE:
ll = localLocks.getLocalLock(lockKey);
// Check for illegalities
if ((ll.hasReadLock() || ll.hasNonExWriteLock()) && !ll.hasWriteLock())
{
throw new ManifoldCFException("Illegal "+description+" sequence: Write "+description+" can't be within read "+description+" or non-ex write "+description,ManifoldCFException.GENERAL_ERROR);
}
// See if we already own the write lock for the object
if (!ll.hasWriteLock())
{
// We don't own a local write lock. Get one.
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
synchronized (lo)
{
try
{
lo.enterWriteLockNoWait();
break;
}
catch (ExpiredObjectException e)
{
// Try again
}
}
}
}
ll.incrementWriteLocks();
break;
case TYPE_WRITENONEX:
ll = localLocks.getLocalLock(lockKey);
// Check for illegalities
if (ll.hasReadLock() && !(ll.hasNonExWriteLock() || ll.hasWriteLock()))
{
throw new ManifoldCFException("Illegal "+description+" sequence: NonExWrite "+description+" can't be within read "+description,ManifoldCFException.GENERAL_ERROR);
}
// See if we already own the write lock for the object
if (!(ll.hasNonExWriteLock() || ll.hasWriteLock()))
{
// We don't own a local write lock. Get one.
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
synchronized (lo)
{
try
{
lo.enterNonExWriteLockNoWait();
break;
}
catch (ExpiredObjectException e)
{
// Try again
}
}
}
}
ll.incrementNonExWriteLocks();
break;
case TYPE_READ:
ll = localLocks.getLocalLock(lockKey);
if (!(ll.hasReadLock() || ll.hasNonExWriteLock() || ll.hasWriteLock()))
{
// We don't own a local read lock. Get one.
while (true)
{
LockObject lo = crossLocks.getObject(lockKey);
synchronized (lo)
{
try
{
lo.enterReadLockNoWait();
break;
}
catch (ExpiredObjectException e)
{
// Try again
}
}
}
}
ll.incrementReadLocks();
break;
}
locksProcessed++;
}
// Got all; we are done!
if (Logging.lock.isDebugEnabled())
Logging.lock.debug(" Successfully obtained multiple "+description+"s!");
return;
}
catch (Throwable ex)
{
// No matter what, undo the locks we've taken
ManifoldCFException ae = null;
int errno = 0;
while (--locksProcessed >= 0)
{
LockDescription ld = lds[locksProcessed];
int lockType = ld.getType();
String lockKey = ld.getKey();
try
{
switch (lockType)
{
case TYPE_READ:
leaveRead(lockKey,description,localLocks,crossLocks);
break;
case TYPE_WRITENONEX:
leaveNonExWrite(lockKey,description,localLocks,crossLocks);
break;
case TYPE_WRITE:
leaveWrite(lockKey,description,localLocks,crossLocks);
break;
}
}
catch (ManifoldCFException e)
{
ae = e;
}
}
if (ae != null)
{
throw ae;
}
if (ex instanceof ManifoldCFException)
{
throw (ManifoldCFException)ex;
}
if (ex instanceof LockException || ex instanceof LocalLockException)
{
Logging.lock.debug(" Couldn't get "+description+"; throwing LockException");
// It's either LockException or LocalLockException
throw new LockException(ex.getMessage());
}
if (ex instanceof InterruptedException)
{
throw new ManifoldCFException("Interrupted",ex,ManifoldCFException.INTERRUPTED);
}
if (!(ex instanceof Error))
{
throw new Error("Unexpected exception",ex);
}
throw (Error)ex;
}
}
protected static void leave(String[] readLocks, String[] writeNonExLocks, String[] writeLocks, String description, LocalLockPool localLocks, LockPool crossLocks)
throws ManifoldCFException
{
LockDescription[] lds = getSortedUniqueLocks(readLocks,writeNonExLocks,writeLocks);
// Free them all... one at a time is fine
ManifoldCFException ae = null;
int i = lds.length;
while (--i >= 0)
{
LockDescription ld = lds[i];
String lockKey = ld.getKey();
int lockType = ld.getType();
try
{
switch (lockType)
{
case TYPE_READ:
leaveRead(lockKey,description,localLocks,crossLocks);
break;
case TYPE_WRITENONEX:
leaveNonExWrite(lockKey,description,localLocks,crossLocks);
break;
case TYPE_WRITE:
leaveWrite(lockKey,description,localLocks,crossLocks);
break;
}
}
catch (ManifoldCFException e)
{
ae = e;
}
}
if (ae != null)
{
throw ae;
}
}
/** Process inbound locks into a sorted vector of most-restrictive unique locks
*/
protected static LockDescription[] getSortedUniqueLocks(String[] readLocks, String[] writeNonExLocks,
String[] writeLocks)
{
// First build a unique hash of lock descriptions
Map<String,LockDescription> ht = new HashMap<String,LockDescription>();
int i;
if (readLocks != null)
{
i = 0;
while (i < readLocks.length)
{
String key = readLocks[i++];
LockDescription ld = ht.get(key);
if (ld == null)
{
ld = new LockDescription(TYPE_READ,key);
ht.put(key,ld);
}
else
ld.set(TYPE_READ);
}
}
if (writeNonExLocks != null)
{
i = 0;
while (i < writeNonExLocks.length)
{
String key = writeNonExLocks[i++];
LockDescription ld = ht.get(key);
if (ld == null)
{
ld = new LockDescription(TYPE_WRITENONEX,key);
ht.put(key,ld);
}
else
ld.set(TYPE_WRITENONEX);
}
}
if (writeLocks != null)
{
i = 0;
while (i < writeLocks.length)
{
String key = writeLocks[i++];
LockDescription ld = ht.get(key);
if (ld == null)
{
ld = new LockDescription(TYPE_WRITE,key);
ht.put(key,ld);
}
else
ld.set(TYPE_WRITE);
}
}
// Now, sort by key name
LockDescription[] rval = new LockDescription[ht.size()];
String[] sortarray = new String[ht.size()];
i = 0;
for (String key : ht.keySet())
{
sortarray[i++] = key;
}
java.util.Arrays.sort(sortarray);
i = 0;
for (String key : sortarray)
{
rval[i++] = ht.get(key);
}
return rval;
}
protected static class LockDescription
{
protected int lockType;
protected String lockKey;
public LockDescription(int lockType, String lockKey)
{
this.lockType = lockType;
this.lockKey = lockKey;
}
public void set(int lockType)
{
if (lockType > this.lockType)
this.lockType = lockType;
}
public int getType()
{
return lockType;
}
public String getKey()
{
return lockKey;
}
}
}