Top Description Fields Constructors Methods
org.eclipse.persistence.internal.helper

public Class WriteLockManager

extends Object
Class Inheritance
Imports
java.util.*, java.util.concurrent.ConcurrentHashMap, .Semaphore, org.eclipse.persistence.descriptors.ClassDescriptor, .FetchGroupManager, org.eclipse.persistence.exceptions.ConcurrencyException, org.eclipse.persistence.internal.helper.linkedlist.ExposedNodeLinkedList, org.eclipse.persistence.internal.identitymaps.CacheKey, org.eclipse.persistence.internal.localization.TraceLocalization, org.eclipse.persistence.internal.queries.ContainerPolicy, org.eclipse.persistence.internal.sessions.AbstractSession, .MergeManager, .ObjectChangeSet, .UnitOfWorkChangeSet, org.eclipse.persistence.logging.SessionLog, org.eclipse.persistence.mappings.DatabaseMapping

INTERNAL:

Purpose: Acquires all required locks for a particular merge process. Implements a deadlock avoidance algorithm to prevent concurrent merge conflicts.

Responsibilities:

Author
Gordon Yorke
Since
10.0.3

Field Summary

Modifier and TypeField and Description
private static final Boolean
ALLOW_INTERRUPTED_EXCEPTION_TO_BE_FIRED_UP_FALSE

The code spots where we use this constant are code spots where we afraid the thread might be trying to run a commit.

private static final Boolean
ALLOW_INTERRUPTED_EXCEPTION_TO_BE_FIRED_UP_TRUE

This flag we use if the write lock manager is stuck building clones of objects.

private static final Map<Thread, Set<Object>>
MAP_WRITE_LOCK_MANAGER_THREAD_TO_OBJECT_IDS_WITH_CHANGE_SET

We want to have traceability of what objects where changed by thread that is in the middle of a commit.

public static final int
public static final int
protected ExposedNodeLinkedList
private static final transient Semaphore
private static final transient int
private static final transient ThreadLocal<Boolean>
SEMAPHORE_THREAD_LOCAL_VAR

Semaphore related properties

private static final Map<Thread, Set<ConcurrencyManager>>
THREAD_TO_FAIL_TO_ACQUIRE_CACHE_KEYS

This a map from a thread to cache keys the thread is finding itself not being able to acquire.

private transient ConcurrencySemaphore

Constructor Summary

AccessConstructor and Description
public

Method Summary

Modifier and TypeMethod and Description
public CacheKey
acquireLockAndRelatedLocks(Object objectForClone, Map<K, V> lockedObjects, Map<K, V> refreshedObjects, CacheKey cacheKey, ClassDescriptor descriptor, AbstractSession cloningSession)

INTERNAL: This is a recursive method used to acquire read locks on all objects that will be cloned.

public Map<K, V>
acquireLocksForClone(Object objectForClone, ClassDescriptor descriptor, CacheKey cacheKey, AbstractSession cloningSession)

INTERNAL: This method will return once the object is locked and all non-indirect related objects are also locked.

public void
acquireRequiredLocks(MergeManager mergeManager, UnitOfWorkChangeSet changeSet)

INTERNAL: This method will be the entry point for threads attempting to acquire locks for all objects that have a changeset.

private void
acquireRequiredLocksInternal(MergeManager mergeManager, UnitOfWorkChangeSet changeSet)

INTERNAL: This method will be the entry point for threads attempting to acquire locks for all objects that have a changeset.

public static void
addCacheKeyToMapWriteLockManagerToCacheKeysThatCouldNotBeAcquired(Thread
thread the thread working on updating the shared cache
thread
,
ConcurrencyManager
the cache key it is not managing to acquire
cacheKeyThatCouldNotBeAcquired
,
long whileStartDate)

The thread was doing its while loop to acquire all required locks to proceed with the commmit and it realized there was one cache key it is unable to acquire

public CacheKey
appendLock(Object primaryKey, Object objectToLock, ClassDescriptor descriptor, MergeManager mergeManager, AbstractSession session)

INTERNAL: This method will be called by a merging thread that is attempting to lock a new object that was not locked previously.

protected CacheKey
attemptToAcquireLock(ClassDescriptor descriptor, Object primaryKey, AbstractSession session)

INTERNAL: This method performs the operations of finding the cacheKey and locking it if possible.

protected CacheKey
checkAndLockObject(Object objectToLock, Map<K, V> lockedObjects, Map<K, V> refreshedObjects, DatabaseMapping mapping, AbstractSession cloningSession)

INTERNAL: Simply check that the object is not already locked then pass it on to the locking method

public static void
clearMapThreadToObjectIdsWithChagenSet(Thread
the thread that is clearing itself out of the map of change sets it needs to merge into the shared cache
thread
)

Remove the current thread from the map of object ids with change sets that are about to bec ommited

public static void
clearMapWriteLockManagerToCacheKeysThatCouldNotBeAcquired(Thread
the thread that what clear his set of cache keys it is struggling to acquire.
thread
)

Before the problematic while loop starts we should always clear for this thread the set of cache keys it could not acquire.

private static Set<ConcurrencyManager>

Returns:

the set of cache keys the thrad is struggling to acquire
getCacheKeysThatCouldNotBeAcquiredByThread
(Thread
the thread that wants to get its set of cache keys it is not managing to acquire.
thread
)

If the thread is not yet registered in the map it will get registered with an empty map.

public static Map<Thread, Set<Object>>
public static Map<Thread, Set<ConcurrencyManager>>
public static void
populateMapThreadToObjectIdsWithChagenSet(Thread
the thread that is in the middle of merge to the shared cache trying to acquire write locks to do this merge
thread
,
Collection<ObjectChangeSet>
the object change sets it has in its hands and that it would like to merge into the cache
objectChangeSets
)

Before a thread starts long wait loop to acquire write locks during a commit transaction the thread will record in this map the object ids it holds with chance sets.

public void
releaseAllAcquiredLocks(MergeManager mergeManager)

INTERNAL: This method will release all acquired locks

public static void
removeCacheKeyFromMapWriteLockManagerToCacheKeysThatCouldNotBeAcquired(Thread
the thread that just managed to grab a write lock
thread
,
ConcurrencyManager
the cache key it managed to acquire for writing.
cacheKeyThatCouldNotBeAcquired
)

A cache keys was successfully acquired we want to make sure it is not recorded in the map of cache keys that could not be acquired.

public void
transitionToDeferredLocks(MergeManager mergeManager)

INTERNAL: This method will transition the previously acquired active locks to deferred locks in the case a readlock could not be acquired for a related object.

public CacheKey
traverseRelatedLocks(Object objectForClone, Map<K, V> lockedObjects, Map<K, V> refreshedObjects, ClassDescriptor descriptor, AbstractSession cloningSession)

INTERNAL: Traverse the object and acquire locks on all related objects.

protected CacheKey
waitOnObjectLock(ClassDescriptor descriptor, Object primaryKey, AbstractSession session, int waitTime)

INTERNAL: This method performs the operations of finding the cacheKey and locking it if possible.

Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAlltoStringwaitwaitwait

Field Detail

ALLOW_INTERRUPTED_EXCEPTION_TO_BE_FIRED_UP_FALSEback to summary
private static final Boolean ALLOW_INTERRUPTED_EXCEPTION_TO_BE_FIRED_UP_FALSE

The code spots where we use this constant are code spots where we afraid the thread might be trying to run a commit. Blowing up the thread with an interrupted exception might be too dangerous. We are not certain the eclipselink code is able to cope with it and release all resources appropriately.

ALLOW_INTERRUPTED_EXCEPTION_TO_BE_FIRED_UP_TRUEback to summary
private static final Boolean ALLOW_INTERRUPTED_EXCEPTION_TO_BE_FIRED_UP_TRUE

This flag we use if the write lock manager is stuck building clones of objects. Because we are not in the code area of commit to the db anything.

MAP_WRITE_LOCK_MANAGER_THREAD_TO_OBJECT_IDS_WITH_CHANGE_SETback to summary
private static final Map<Thread, Set<Object>> MAP_WRITE_LOCK_MANAGER_THREAD_TO_OBJECT_IDS_WITH_CHANGE_SET

We want to have traceability of what objects where changed by thread that is in the middle of a commit. This information can be useful when a massive dump is performed to explain the situation of any thread that might eventually be stuck inside of the write lock manager to tells us what exactly are the objects it has changed and wants to commit or merge into the shared cache. Relates to the THREAD_TO_FAIL_TO_ACQUIRE_CACHE_KEYS but this map does not tells us about any specific problem such as a cache key that could not be acquired just tells us what objects were modified.

MAX_WAITback to summary
public static final int MAX_WAIT
MAXTRIESback to summary
public static final int MAXTRIES
prevailingQueueback to summary
protected ExposedNodeLinkedList prevailingQueue
SEMAPHORE_LIMIT_MAX_NUMBER_OF_THREADS_WRITE_LOCK_MANAGERback to summary
private static final transient Semaphore SEMAPHORE_LIMIT_MAX_NUMBER_OF_THREADS_WRITE_LOCK_MANAGER
SEMAPHORE_MAX_NUMBER_THREADSback to summary
private static final transient int SEMAPHORE_MAX_NUMBER_THREADS
SEMAPHORE_THREAD_LOCAL_VARback to summary
private static final transient ThreadLocal<Boolean> SEMAPHORE_THREAD_LOCAL_VAR

Semaphore related properties

THREAD_TO_FAIL_TO_ACQUIRE_CACHE_KEYSback to summary
private static final Map<Thread, Set<ConcurrencyManager>> THREAD_TO_FAIL_TO_ACQUIRE_CACHE_KEYS

This a map from a thread to cache keys the thread is finding itself not being able to acquire. This map is important to explain why a thread might be stuck in a stack trace of the form: at java.lang.Class.getEnclosingMethod0(Native Method) at java.lang.Class.getEnclosingMethodInfo(Class.java:1072) at java.lang.Class.getEnclosingClass(Class.java:1272) at java.lang.Class.getSimpleBinaryName(Class.java:1443) at java.lang.Class.getSimpleName(Class.java:1309) at org.eclipse.persistence.internal.identitymaps.IdentityMapManager.acquireLockNoWait(IdentityMapManager.java:205) at org.eclipse.persistence.internal.sessions.IdentityMapAccessor.acquireLockNoWait(IdentityMapAccessor.java:108) at org.eclipse.persistence.internal.helper.WriteLockManager.attemptToAcquireLock(WriteLockManager.java:431) at org.eclipse.persistence.internal.helper.WriteLockManager.acquireRequiredLocks(WriteLockManager.java:280) We want to be able to trace these dead lock situations. To put them our on the massive log dump as to do the dead lock detection.

writeLockManagerSemaphoreback to summary
private transient ConcurrencySemaphore writeLockManagerSemaphore

Constructor Detail

WriteLockManagerback to summary
public WriteLockManager()

Method Detail

acquireLockAndRelatedLocksback to summary
public CacheKey acquireLockAndRelatedLocks(Object objectForClone, Map<K, V> lockedObjects, Map<K, V> refreshedObjects, CacheKey cacheKey, ClassDescriptor descriptor, AbstractSession cloningSession)

INTERNAL: This is a recursive method used to acquire read locks on all objects that will be cloned. These include all related objects for which there is no indirection. The returned object is the first object that the lock could not be acquired for. The caller must try for exceptions and release locked objects in the case of an exception.

acquireLocksForCloneback to summary
public Map<K, V> acquireLocksForClone(Object objectForClone, ClassDescriptor descriptor, CacheKey cacheKey, AbstractSession cloningSession)

INTERNAL: This method will return once the object is locked and all non-indirect related objects are also locked.

acquireRequiredLocksback to summary
public void acquireRequiredLocks(MergeManager mergeManager, UnitOfWorkChangeSet changeSet)

INTERNAL: This method will be the entry point for threads attempting to acquire locks for all objects that have a changeset. This method will hand off the processing of the deadlock algorithm to other member methods. The mergeManager must be the active mergemanager for the calling thread. Returns true if all required locks were acquired This is wrapper method with semaphore logic.

acquireRequiredLocksInternalback to summary
private void acquireRequiredLocksInternal(MergeManager mergeManager, UnitOfWorkChangeSet changeSet)

INTERNAL: This method will be the entry point for threads attempting to acquire locks for all objects that have a changeset. This method will hand off the processing of the deadlock algorithm to other member methods. The mergeManager must be the active mergemanager for the calling thread. Returns true if all required locks were acquired

addCacheKeyToMapWriteLockManagerToCacheKeysThatCouldNotBeAcquiredback to summary
public static void addCacheKeyToMapWriteLockManagerToCacheKeysThatCouldNotBeAcquired(Thread thread, ConcurrencyManager cacheKeyThatCouldNotBeAcquired, long whileStartDate) throws InterruptedException

The thread was doing its while loop to acquire all required locks to proceed with the commmit and it realized there was one cache key it is unable to acquire

Parameters
thread:Thread

thread the thread working on updating the shared cache

cacheKeyThatCouldNotBeAcquired:ConcurrencyManager

the cache key it is not managing to acquire

Exceptions
InterruptedException:
Should be fired because we are passing a flag into the determineIfReleaseDeferredLockAppearsToBeDeadLocked to say we do not want the thread to be blown up (e.g. we are afraid of breaking threads in the middle of a commit process could be quite dangerous). See ALLOW_INTERRUPTED_EXCEPTION_TO_BE_FIRED_UP_FALSE
appendLockback to summary
public CacheKey appendLock(Object primaryKey, Object objectToLock, ClassDescriptor descriptor, MergeManager mergeManager, AbstractSession session)

INTERNAL: This method will be called by a merging thread that is attempting to lock a new object that was not locked previously. Unlike the other methods within this class this method will lock only this object.

attemptToAcquireLockback to summary
protected CacheKey attemptToAcquireLock(ClassDescriptor descriptor, Object primaryKey, AbstractSession session)

INTERNAL: This method performs the operations of finding the cacheKey and locking it if possible. Returns True if the lock was acquired, false otherwise

checkAndLockObjectback to summary
protected CacheKey checkAndLockObject(Object objectToLock, Map<K, V> lockedObjects, Map<K, V> refreshedObjects, DatabaseMapping mapping, AbstractSession cloningSession)

INTERNAL: Simply check that the object is not already locked then pass it on to the locking method

clearMapThreadToObjectIdsWithChagenSetback to summary
public static void clearMapThreadToObjectIdsWithChagenSet(Thread thread)

Remove the current thread from the map of object ids with change sets that are about to bec ommited

Parameters
thread:Thread

the thread that is clearing itself out of the map of change sets it needs to merge into the shared cache

clearMapWriteLockManagerToCacheKeysThatCouldNotBeAcquiredback to summary
public static void clearMapWriteLockManagerToCacheKeysThatCouldNotBeAcquired(Thread thread)

Before the problematic while loop starts we should always clear for this thread the set of cache keys it could not acquire.

Parameters
thread:Thread

the thread that what clear his set of cache keys it is struggling to acquire.

getCacheKeysThatCouldNotBeAcquiredByThreadback to summary
private static Set<ConcurrencyManager> getCacheKeysThatCouldNotBeAcquiredByThread(Thread thread)

If the thread is not yet registered in the map it will get registered with an empty map.

Parameters
thread:Thread

the thread that wants to get its set of cache keys it is not managing to acquire.

Returns:Set<ConcurrencyManager>

the set of cache keys the thrad is struggling to acquire

getMapWriteLockManagerThreadToObjectIdsWithChangeSetback to summary
public static Map<Thread, Set<Object>> getMapWriteLockManagerThreadToObjectIdsWithChangeSet()

Getter for MAP_WRITE_LOCK_MANAGER_THREAD_TO_OBJECT_IDS_WITH_CHANGE_SET

getThreadToFailToAcquireCacheKeysback to summary
public static Map<Thread, Set<ConcurrencyManager>> getThreadToFailToAcquireCacheKeys()

Getter for THREAD_TO_FAIL_TO_ACQUIRE_CACHE_KEYS

populateMapThreadToObjectIdsWithChagenSetback to summary
public static void populateMapThreadToObjectIdsWithChagenSet(Thread thread, Collection<ObjectChangeSet> objectChangeSets)

Before a thread starts long wait loop to acquire write locks during a commit transaction the thread will record in this map the object ids it holds with chance sets. It will be useful information if a dead lock is taking place.

Parameters
thread:Thread

the thread that is in the middle of merge to the shared cache trying to acquire write locks to do this merge

objectChangeSets:Collection<ObjectChangeSet>

the object change sets it has in its hands and that it would like to merge into the cache

releaseAllAcquiredLocksback to summary
public void releaseAllAcquiredLocks(MergeManager mergeManager)

INTERNAL: This method will release all acquired locks

removeCacheKeyFromMapWriteLockManagerToCacheKeysThatCouldNotBeAcquiredback to summary
public static void removeCacheKeyFromMapWriteLockManagerToCacheKeysThatCouldNotBeAcquired(Thread thread, ConcurrencyManager cacheKeyThatCouldNotBeAcquired)

A cache keys was successfully acquired we want to make sure it is not recorded in the map of cache keys that could not be acquired. The situation theoretically can change. Failing to acquire a write lock can be a temporary situation. The lock might become available eventually. Otherwise there would be no point for the while loop that is trying to acquire these locks.

Parameters
thread:Thread

the thread that just managed to grab a write lock

cacheKeyThatCouldNotBeAcquired:ConcurrencyManager

the cache key it managed to acquire for writing.

transitionToDeferredLocksback to summary
public void transitionToDeferredLocks(MergeManager mergeManager)

INTERNAL: This method will transition the previously acquired active locks to deferred locks in the case a readlock could not be acquired for a related object. Deferred locks must be employed to prevent deadlock when waiting for the readlock while still protecting readers from incomplete data.

traverseRelatedLocksback to summary
public CacheKey traverseRelatedLocks(Object objectForClone, Map<K, V> lockedObjects, Map<K, V> refreshedObjects, ClassDescriptor descriptor, AbstractSession cloningSession)

INTERNAL: Traverse the object and acquire locks on all related objects.

waitOnObjectLockback to summary
protected CacheKey waitOnObjectLock(ClassDescriptor descriptor, Object primaryKey, AbstractSession session, int waitTime)

INTERNAL: This method performs the operations of finding the cacheKey and locking it if possible. Waits until the lock can be acquired