/* $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.nio.charset.StandardCharsets; 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; // This is the local thread ID protected final Long threadID; // 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 { threadID = new Long(Thread.currentThread().getId()); } // 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; return new String(bytes, StandardCharsets.UTF_8); } protected void writeServiceName(String resourceName, String serviceName) throws ManifoldCFException { writeData(resourceName, (serviceName==null)?null:serviceName.getBytes(StandardCharsets.UTF_8)); } 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(threadID, lockKey, "lock", localLocks, getGlobalLockPool()); } @Override public final void enterNonExWriteLockNoWait(String lockKey) throws ManifoldCFException, LockException { enterNonExWriteNoWait(threadID, 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(threadID, lockKey, "lock", localLocks, getGlobalLockPool()); } @Override public final void enterWriteLockNoWait(String lockKey) throws ManifoldCFException, LockException { enterWriteNoWait(threadID, 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(threadID, lockKey, "lock", localLocks, getGlobalLockPool()); } @Override public final void enterReadLockNoWait(String lockKey) throws ManifoldCFException, LockException { enterReadNoWait(threadID, 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(threadID, readLocks, nonExWriteLocks, writeLocks, "lock", localLocks, getGlobalLockPool()); } @Override public final void enterLocksNoWait(String[] readLocks, String[] nonExWriteLocks, String[] writeLocks) throws ManifoldCFException, LockException { enterNoWait(threadID, 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(threadID, 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(threadID, 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(threadID, 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(threadID, 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(Long threadID, 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) { LockGate lo = crossLocks.getObject(lockKey); try { lo.enterNonExWriteLock(threadID); 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(Long threadID, 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) { LockGate lo = crossLocks.getObject(lockKey); try { synchronized (lo) { lo.enterNonExWriteLockNoWait(threadID); 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) { LockGate 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(Long threadID, 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) { LockGate lo = crossLocks.getObject(lockKey); try { lo.enterWriteLock(threadID); 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(Long threadID, 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) { LockGate lo = crossLocks.getObject(lockKey); try { synchronized (lo) { lo.enterWriteLockNoWait(threadID); 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) { LockGate 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(Long threadID, 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) { LockGate lo = crossLocks.getObject(lockKey); try { lo.enterReadLock(threadID); 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(Long threadID, 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) { LockGate lo = crossLocks.getObject(lockKey); try { synchronized (lo) { lo.enterReadLockNoWait(threadID); 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) { LockGate 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(Long threadID, 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) { LockGate lo = crossLocks.getObject(lockKey); try { lo.enterWriteLock(threadID); 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) { LockGate lo = crossLocks.getObject(lockKey); try { lo.enterNonExWriteLock(threadID); 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) { LockGate lo = crossLocks.getObject(lockKey); try { lo.enterReadLock(threadID); 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(Long threadID, 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) { LockGate lo = crossLocks.getObject(lockKey); synchronized (lo) { try { lo.enterWriteLockNoWait(threadID); 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) { LockGate lo = crossLocks.getObject(lockKey); synchronized (lo) { try { lo.enterNonExWriteLockNoWait(threadID); 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) { LockGate lo = crossLocks.getObject(lockKey); synchronized (lo) { try { lo.enterReadLockNoWait(threadID); 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; } } }