diff --git a/antbuild.properties b/antbuild.properties
index 36f53a3d80f..d37c43a757d 100644
--- a/antbuild.properties
+++ b/antbuild.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2008, 2024 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2008, 2025 Oracle and/or its affiliates. All rights reserved.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
@@ -111,6 +111,7 @@ eclipselink.jpa.spring.test=${eclipselink.jpa.base}/eclipselink.jpa.spring.test
eclipselink.jpa.wdf.test=${eclipselink.jpa.base}/eclipselink.jpa.wdf.test
eclipselink.jpa.xsds=${eclipselink.jpa}/resource/org/eclipse/persistence/jpa
eclipselink.jpa.plugins=${eclipselink.jpa.base}/${plugins.dir}
+eclipselink.deadlock.test=${eclipselink.jpa.base}/eclipselink.jpa.test.deadlock
eclipselink.modelgen=${eclipselink.jpa.base}/org.eclipse.persistence.jpa.modelgen
eclipselink.jpars=${eclipselink.jpa.base}/org.eclipse.persistence.jpars
eclipselink.jpars.test=${eclipselink.jpa.base}/eclipselink.jpars.test
diff --git a/antbuild.xml b/antbuild.xml
index 8a440521bff..7cc780a8548 100644
--- a/antbuild.xml
+++ b/antbuild.xml
@@ -1,6 +1,6 @@
+
+
+
@@ -1855,7 +1860,7 @@
depends="clear-db, test-core-srg, test-jpa22-srg, test-jpars, test-moxy-srg, test-sdo-srg, test-dbws-srg, test-dbws-builder-srg, generate-report"
/>
+
+
+
+
diff --git a/foundation/eclipselink.core.test/src/org/eclipse/persistence/testing/tests/junit/helper/HelperTest.java b/foundation/eclipselink.core.test/src/org/eclipse/persistence/testing/tests/junit/helper/HelperTest.java
index 46278cdf7c6..4c2785b19d9 100644
--- a/foundation/eclipselink.core.test/src/org/eclipse/persistence/testing/tests/junit/helper/HelperTest.java
+++ b/foundation/eclipselink.core.test/src/org/eclipse/persistence/testing/tests/junit/helper/HelperTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -20,6 +20,8 @@
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
import java.util.Vector;
import org.eclipse.persistence.internal.helper.Helper;
@@ -315,4 +317,22 @@ public void timestampFromStringTest() {
Helper.setShouldOptimizeDates(optimizedDatesState);
}
}
+
+ @Test
+ public void copyMapTest() {
+ Map inputMap = new HashMap<>();
+ inputMap.put(1L, "one");
+ inputMap.put(2L, "two");
+ Map copyMap = Helper.copyMap(inputMap);
+ Assert.assertEquals(inputMap, copyMap);
+ }
+
+ @Test
+ public void copySetTest() {
+ Set inputSet = new HashSet<>();
+ inputSet.add("one");
+ inputSet.add("two");
+ Set copySet = Helper.copySet(inputSet);
+ Assert.assertEquals(inputSet, copySet);
+ }
}
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/MergeManagerOperationMode.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/MergeManagerOperationMode.java
new file mode 100644
index 00000000000..25572032c0d
--- /dev/null
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/MergeManagerOperationMode.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+// Oracle - initial API and implementation
+package org.eclipse.persistence.config;
+
+/**
+ * INTERNAL:
+ *
+ * Purpose: It is data model behind {@linkplain org.eclipse.persistence.config.SystemProperties#CONCURRENCY_MANAGER_ALLOW_GET_CACHE_KEY_FOR_MERGE_MODE}
+ * or {@linkplain org.eclipse.persistence.config.PersistenceUnitProperties#CONCURRENCY_MANAGER_ALLOW_GET_CACHE_KEY_FOR_MERGE_MODE}.
+ */
+public final class MergeManagerOperationMode {
+
+ /**
+ * {@code ORIGIN} (DEFAULT) - There is infinite {@linkplain java.lang.Object#wait()} call in case of some conditions during time when object/entity referred from
+ * {@code org.eclipse.persistence.internal.identitymaps.CacheKey} is locked and modified by another thread. In some cases it should leads into deadlock.
+ */
+ public static final String ORIGIN = "ORIGIN";
+
+ /**
+ * {@code WAITLOOP} - Merge manager will try in the loop with timeout wait {@code cacheKey.wait(ConcurrencyUtil.SINGLETON.getAcquireWaitTime());}
+ * fetch object/entity from {@linkplain org.eclipse.persistence.internal.identitymaps.CacheKey}. If fetch will be successful object/entity loop finish and continue
+ * with remaining code. If not @{code java.lang.InterruptedException} is thrown and caught and used {@linkplain org.eclipse.persistence.internal.identitymaps.CacheKey} instance
+ * status is set into invalidation state. This strategy avoid deadlock issue, but there should be impact to the performance.
+ */
+ public static final String WAITLOOP = "WAITLOOP";
+
+ private MergeManagerOperationMode() {
+ // no instance please
+ }
+}
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/PersistenceUnitProperties.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/PersistenceUnitProperties.java
index 27ae1f4008b..4153eda456d 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/PersistenceUnitProperties.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/PersistenceUnitProperties.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2025 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2022 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -4051,6 +4051,23 @@ public class PersistenceUnitProperties {
*/
public static final String CONCURRENCY_MANAGER_ALLOW_INTERRUPTED_EXCEPTION = "eclipselink.concurrency.manager.allow.interruptedexception";
+ /**
+ *
+ * This property control in {@link org.eclipse.persistence.internal.sessions.AbstractSession#getCacheKeyFromTargetSessionForMerge(java.lang.Object, org.eclipse.persistence.internal.descriptors.ObjectBuilder, org.eclipse.persistence.descriptors.ClassDescriptor, org.eclipse.persistence.internal.sessions.MergeManager)}
+ * strategy how {@code org.eclipse.persistence.internal.identitymaps.CacheKey} will be fetched from shared cache.
+ *
+ * Allowed Values (case-sensitive String):
+ *
+ *
{@code ORIGIN} (DEFAULT) - There is infinite {@linkplain java.lang.Object#wait()} call in case of some conditions during time when object/entity referred from
+ * {@code org.eclipse.persistence.internal.identitymaps.CacheKey} is locked and modified by another thread. In some cases it should leads into deadlock.
+ *
{@code WAITLOOP} - Merge manager will try in the loop with timeout wait {@code cacheKey.wait(ConcurrencyUtil.SINGLETON.getAcquireWaitTime());}
+ * fetch object/entity from {@linkplain org.eclipse.persistence.internal.identitymaps.CacheKey}. If fetch will be successful object/entity loop finish and continue
+ * with remaining code. If not @{code java.lang.InterruptedException} is thrown and caught and used {@linkplain org.eclipse.persistence.internal.identitymaps.CacheKey} instance
+ * status is set into invalidation state. This strategy avoid deadlock issue, but there should be impact to the performance.
+ *
+ */
+ public static final String CONCURRENCY_MANAGER_ALLOW_GET_CACHE_KEY_FOR_MERGE_MODE = "eclipselink.concurrency.manager.allow.getcachekeyformerge.mode";
+
/**
*
* This property control (enable/disable) if ConcurrencyException fired when dead-lock diagnostic is enabled.
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/SystemProperties.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/SystemProperties.java
index eee92c9543f..b84017cde45 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/SystemProperties.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/SystemProperties.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2025 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -155,6 +155,23 @@ public class SystemProperties {
*/
public static final String CONCURRENCY_MANAGER_ALLOW_INTERRUPTED_EXCEPTION = "eclipselink.concurrency.manager.allow.interruptedexception";
+ /**
+ *
+ * This property control in {@link org.eclipse.persistence.internal.sessions.AbstractSession#getCacheKeyFromTargetSessionForMerge(java.lang.Object, org.eclipse.persistence.internal.descriptors.ObjectBuilder, org.eclipse.persistence.descriptors.ClassDescriptor, org.eclipse.persistence.internal.sessions.MergeManager)}
+ * strategy how {@code org.eclipse.persistence.internal.identitymaps.CacheKey} will be fetched from shared cache.
+ *
+ * Allowed Values (case-sensitive String):
+ *
+ *
{@code ORIGIN} (DEFAULT) - There is infinite {@linkplain java.lang.Object#wait()} call in case of some conditions during time when object/entity referred from
+ * {@code org.eclipse.persistence.internal.identitymaps.CacheKey} is locked and modified by another thread. In some cases it should leads into deadlock.
+ *
{@code WAITLOOP} - Merge manager will try in the loop with timeout wait {@code cacheKey.wait(ConcurrencyUtil.SINGLETON.getAcquireWaitTime());}
+ * fetch object/entity from {@linkplain org.eclipse.persistence.internal.identitymaps.CacheKey}. If fetch will be successful object/entity loop finish and continue
+ * with remaining code. If not @{code java.lang.InterruptedException} is thrown and caught and used {@linkplain org.eclipse.persistence.internal.identitymaps.CacheKey} instance
+ * status is set into invalidation state. This strategy avoid deadlock issue, but there should be impact to the performance.
+ *
+ */
+ public static final String CONCURRENCY_MANAGER_ALLOW_GET_CACHE_KEY_FOR_MERGE_MODE = "eclipselink.concurrency.manager.allow.getcachekeyformerge.mode";
+
/**
*
* This property control (enable/disable) if ConcurrencyException fired when dead-lock diagnostic is enabled.
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/helper/ConcurrencyManager.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/helper/ConcurrencyManager.java
index 5d163dbd216..537cea1700c 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/helper/ConcurrencyManager.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/helper/ConcurrencyManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2025 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -429,6 +429,13 @@ protected static Map getDeferredLockManagers() {
return DEFERRED_LOCK_MANAGERS;
}
+ /**
+ * Return snapshot of the deferred lock manager hashtable (thread - DeferredLockManager).
+ */
+ protected static Map getDeferredLockManagersSnapshot() {
+ return Helper.copyMap(DEFERRED_LOCK_MANAGERS);
+ }
+
/**
* Init the deferred lock managers (thread - DeferredLockManager).
*/
@@ -853,7 +860,7 @@ public void releaseAllLocksAcquiredByThread(DeferredLockManager lockManager) {
* @return Never null if the read lock manager does not yet exist for the current thread. otherwise its read log
* manager is returned.
*/
- protected static ReadLockManager getReadLockManager(Thread thread) {
+ public static ReadLockManager getReadLockManager(Thread thread) {
Map readLockManagers = getReadLockManagers();
return readLockManagers.get(thread);
}
@@ -865,6 +872,13 @@ protected static Map getReadLockManagers() {
return READ_LOCK_MANAGERS;
}
+ /**
+ * Return snapshot of the deferred lock manager hashtable (thread - DeferredLockManager).
+ */
+ protected static Map getReadLockManagersSnapshot() {
+ return Helper.copyMap(READ_LOCK_MANAGERS);
+ }
+
/**
* Print the nested depth.
*/
@@ -969,33 +983,33 @@ public long getTotalNumberOfKeysReleasedForReadingBlewUpExceptionDueToCacheKeyHa
}
/** Getter for {@link #THREADS_TO_WAIT_ON_ACQUIRE} */
- public static Map getThreadsToWaitOnAcquire() {
- return new HashMap<>(THREADS_TO_WAIT_ON_ACQUIRE);
+ public static Map getThreadsToWaitOnAcquireSnapshot() {
+ return Helper.copyMap(THREADS_TO_WAIT_ON_ACQUIRE);
}
/** Getter for {@link #THREADS_TO_WAIT_ON_ACQUIRE_NAME_OF_METHOD_CREATING_TRACE} */
- public static Map getThreadsToWaitOnAcquireMethodName() {
- return new HashMap<>(THREADS_TO_WAIT_ON_ACQUIRE_NAME_OF_METHOD_CREATING_TRACE);
+ public static Map getThreadsToWaitOnAcquireMethodNameSnapshot() {
+ return Helper.copyMap(THREADS_TO_WAIT_ON_ACQUIRE_NAME_OF_METHOD_CREATING_TRACE);
}
/** Getter for {@link #THREADS_TO_WAIT_ON_ACQUIRE_READ_LOCK} */
- public static Map getThreadsToWaitOnAcquireReadLock() {
- return THREADS_TO_WAIT_ON_ACQUIRE_READ_LOCK;
+ public static Map getThreadsToWaitOnAcquireReadLockSnapshot() {
+ return Helper.copyMap(THREADS_TO_WAIT_ON_ACQUIRE_READ_LOCK);
}
/** Getter for {@link #THREADS_TO_WAIT_ON_ACQUIRE_READ_LOCK_NAME_OF_METHOD_CREATING_TRACE} */
- public static Map getThreadsToWaitOnAcquireReadLockMethodName() {
- return THREADS_TO_WAIT_ON_ACQUIRE_READ_LOCK_NAME_OF_METHOD_CREATING_TRACE;
+ public static Map getThreadsToWaitOnAcquireReadLockMethodNameSnapshot() {
+ return Helper.copyMap(THREADS_TO_WAIT_ON_ACQUIRE_READ_LOCK_NAME_OF_METHOD_CREATING_TRACE);
}
/** Getter for {@link #THREADS_WAITING_TO_RELEASE_DEFERRED_LOCKS} */
- public static Set getThreadsWaitingToReleaseDeferredLocks() {
- return new HashSet<>(THREADS_WAITING_TO_RELEASE_DEFERRED_LOCKS);
+ public static Set getThreadsWaitingToReleaseDeferredLocksSnapshot() {
+ return Helper.copySet(THREADS_WAITING_TO_RELEASE_DEFERRED_LOCKS);
}
/** Getter for {@link #THREADS_WAITING_TO_RELEASE_DEFERRED_LOCKS_BUILD_OBJECT_COMPLETE_GOES_NOWHERE} */
- public static Map getThreadsWaitingToReleaseDeferredLocksJustification() {
- return new HashMap<>(THREADS_WAITING_TO_RELEASE_DEFERRED_LOCKS_BUILD_OBJECT_COMPLETE_GOES_NOWHERE);
+ public static Map getThreadsWaitingToReleaseDeferredLocksJustificationSnapshot() {
+ return Helper.copyMap(THREADS_WAITING_TO_RELEASE_DEFERRED_LOCKS_BUILD_OBJECT_COMPLETE_GOES_NOWHERE);
}
/**
@@ -1107,4 +1121,32 @@ public Lock getInstanceLock() {
public Condition getInstanceLockCondition() {
return this.instanceLockCondition;
}
+
+ public boolean isCacheKey() {
+ return false;
+ }
+
+ /**
+ * Check if {@code org.eclipse.persistence.internal.helper.ConcurrencyManager} or child like {@code org.eclipse.persistence.internal.identitymaps.CacheKey} is currently being owned for writing
+ * and if that owning thread happens to be the current thread doing the check.
+ *
+ * @return {@code false} means either the thread is currently not owned by any other thread for writing purposes. Or otherwise if is owned by some thread
+ * but the thread is not the current thread. {@code false} is returned if and only if instance is being owned by some thread
+ * and that thread is not the current thread, it is some other competing thread.
+ */
+ public boolean isAcquiredForWritingAndOwnedByDifferentThread() {
+ instanceLock.lock();
+ try {
+ if (!this.isAcquired()) {
+ return false;
+ }
+ if (this.activeThread == null) {
+ return false;
+ }
+ Thread currentThread = Thread.currentThread();
+ return this.activeThread != currentThread;
+ } finally {
+ instanceLock.unlock();
+ }
+ }
}
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/helper/ConcurrencyUtil.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/helper/ConcurrencyUtil.java
index 1e93eac323e..b931396f76c 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/helper/ConcurrencyUtil.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/helper/ConcurrencyUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2024 Oracle, IBM and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2025 Oracle, IBM and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -15,6 +15,7 @@
// IBM - ConcurrencyUtil call of ThreadMXBean.getThreadInfo() needs doPriv
package org.eclipse.persistence.internal.helper;
+import org.eclipse.persistence.config.MergeManagerOperationMode;
import org.eclipse.persistence.config.SystemProperties;
import org.eclipse.persistence.internal.helper.type.CacheKeyToThreadRelationships;
import org.eclipse.persistence.internal.helper.type.ConcurrencyManagerState;
@@ -25,6 +26,7 @@
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedGetSystemProperty;
import org.eclipse.persistence.internal.security.PrivilegedGetThreadInfo;
+import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.eclipse.persistence.logging.SessionLog;
@@ -68,6 +70,7 @@ public class ConcurrencyUtil {
private boolean useSemaphoreToLimitConcurrencyOnWriteLockManagerAcquireRequiredLocks = getBooleanProperty(SystemProperties.CONCURRENCY_MANAGER_USE_SEMAPHORE_TO_SLOW_DOWN_WRITE_LOCK_MANAGER_ACQUIRE_REQUIRED_LOCKS, DEFAULT_USE_SEMAPHORE_TO_SLOW_DOWN_WRITE_LOCK_MANAGER_ACQUIRE_REQUIRED_LOCKS);
private int noOfThreadsAllowedToObjectBuildInParallel = getIntProperty(SystemProperties.CONCURRENCY_MANAGER_OBJECT_BUILDING_NO_THREADS, DEFAULT_CONCURRENCY_MANAGER_OBJECT_BUILDING_NO_THREADS);
private int noOfThreadsAllowedToDoWriteLockManagerAcquireRequiredLocksInParallel = getIntProperty(SystemProperties.CONCURRENCY_MANAGER_WRITE_LOCK_MANAGER_ACQUIRE_REQUIRED_LOCKS_NO_THREADS, DEFAULT_CONCURRENCY_MANAGER_WRITE_LOCK_MANAGER_ACQUIRE_REQUIRED_LOCKS_NO_THREADS);
+ private String concurrencyManagerAllowGetCacheKeyForMergeMode = getStringProperty(SystemProperties.CONCURRENCY_MANAGER_ALLOW_GET_CACHE_KEY_FOR_MERGE_MODE, MergeManagerOperationMode.ORIGIN);
private long concurrencySemaphoreMaxTimePermit = getLongProperty(SystemProperties.CONCURRENCY_SEMAPHORE_MAX_TIME_PERMIT, DEFAULT_CONCURRENCY_SEMAPHORE_MAX_TIME_PERMIT);
private long concurrencySemaphoreLogTimeout = getLongProperty(SystemProperties.CONCURRENCY_SEMAPHORE_LOG_TIMEOUT, DEFAULT_CONCURRENCY_SEMAPHORE_LOG_TIMEOUT);
@@ -121,11 +124,15 @@ private ConcurrencyUtil() {
* is most likely too dangerous and possibly the eclipselink code is not robust enough to code with such
* scenarios We do not care so much about blowing up exception during object building but during
* committing of transactions we are very afraid
+ * @return Returns a boolean value if the code believes that it is stuck. {@code true} means the code ended up either logging
+ * a tiny Dump message or the massive dump message.
+ * {@code false} means that the code did not do any logging and just quickly returned. This boolean flag is especially
+ * meaningful if we have the interrupt exceptions disabled and so the method is not being caused to blow up.
+ * In that case there is still a way to know if we believe to be stuck. *
* @throws InterruptedException
- * we fire an interrupted exception to ensure that the code blows up and releases all of the locks it
- * had.
+ * it fires an interrupted exception to ensure that the code blows up and releases all the locks it had.
*/
- public void determineIfReleaseDeferredLockAppearsToBeDeadLocked(ConcurrencyManager concurrencyManager,
+ public boolean determineIfReleaseDeferredLockAppearsToBeDeadLocked(ConcurrencyManager concurrencyManager,
final long whileStartTimeMillis, DeferredLockManager lockManager, ReadLockManager readLockManager,
boolean callerIsWillingToAllowInterruptedExceptionToBeFiredUpIfNecessary)
throws InterruptedException {
@@ -138,7 +145,7 @@ public void determineIfReleaseDeferredLockAppearsToBeDeadLocked(ConcurrencyManag
if (!tooMuchTimeHasElapsed) {
// this thread is not stuck for that long let us allow the code to continue waiting for the lock to be acquired
// or for the deferred locks to be considered as finished
- return;
+ return false;
}
// (b) We believe this is a dead lock
@@ -155,7 +162,7 @@ public void determineIfReleaseDeferredLockAppearsToBeDeadLocked(ConcurrencyManag
// this thread has recently logged a small message about the fact that it is stuck
// no point in logging another message like that for some time
// let us allow for this thread to silently continue stuck without logging anything
- return ;
+ return true;
}
// (c) This thread it is dead lock since the whileStartDate indicates a dead lock and
@@ -197,6 +204,7 @@ public void determineIfReleaseDeferredLockAppearsToBeDeadLocked(ConcurrencyManag
} else {
AbstractSessionLog.getLog().log(SessionLog.SEVERE, SessionLog.CACHE,"concurrency_manager_allow_concurrency_exception_fired_up");
}
+ return true;
}
/**
@@ -327,6 +335,14 @@ public void setNoOfThreadsAllowedToDoWriteLockManagerAcquireRequiredLocksInParal
this.noOfThreadsAllowedToDoWriteLockManagerAcquireRequiredLocksInParallel = noOfThreadsAllowedToDoWriteLockManagerAcquireRequiredLocksInParallel;
}
+ public String getConcurrencyManagerAllowGetCacheKeyForMergeMode() {
+ return concurrencyManagerAllowGetCacheKeyForMergeMode;
+ }
+
+ public void setConcurrencyManagerAllowGetCacheKeyForMergeMode(String concurrencyManagerAllowGetCacheKeyForMergeMode) {
+ this.concurrencyManagerAllowGetCacheKeyForMergeMode = concurrencyManagerAllowGetCacheKeyForMergeMode;
+ }
+
public long getConcurrencySemaphoreMaxTimePermit() {
return concurrencySemaphoreMaxTimePermit;
}
@@ -432,7 +448,7 @@ public boolean tooMuchTimeHasElapsed(final long whileStartTimeMillis, final long
}
/**
- * Invoke the {@link #dumpConcurrencyManagerInformationStep01(Map, Map, Map, Map, Map, Map, Map, Set, Map, Map)} if sufficient time has passed.
+ * Invoke the {@link #dumpConcurrencyManagerInformationStep01(Map, Map, Map, Map, Map, Map, Map, Set, Map, Map, Map)} if sufficient time has passed.
* This log message will potentially create a massive dump in the server log file. So we need to check when was the
* last time that the masive dump was produced and decide if we can log again the state of the concurrency manager.
*
@@ -464,16 +480,17 @@ public void dumpConcurrencyManagerInformationIfAppropriate() {
}
// do the "MassiveDump" logging if enough time has passed since the previous massive dump logging
- Map deferredLockManagers = ConcurrencyManager.getDeferredLockManagers();
- Map readLockManagersOriginal = ConcurrencyManager.getReadLockManagers();
- Map mapThreadToWaitOnAcquireOriginal = ConcurrencyManager.getThreadsToWaitOnAcquire();
- Map mapThreadToWaitOnAcquireMethodNameOriginal = ConcurrencyManager.getThreadsToWaitOnAcquireMethodName();
- Map mapThreadToWaitOnAcquireReadLockOriginal = ConcurrencyManager.getThreadsToWaitOnAcquireReadLock();
- Map mapThreadToWaitOnAcquireReadLockMethodNameOriginal = ConcurrencyManager.getThreadsToWaitOnAcquireReadLockMethodName();
- Map> mapThreadToWaitOnAcquireInsideWriteLockManagerOriginal = WriteLockManager.getThreadToFailToAcquireCacheKeys();
- Set setThreadWaitingToReleaseDeferredLocksOriginal = ConcurrencyManager.getThreadsWaitingToReleaseDeferredLocks();
- Map mapThreadsThatAreCurrentlyWaitingToReleaseDeferredLocksJustificationClone = ConcurrencyManager.getThreadsWaitingToReleaseDeferredLocksJustification();
- Map> mapThreadToObjectIdWithWriteLockManagerChangesOriginal = WriteLockManager.getMapWriteLockManagerThreadToObjectIdsWithChangeSet();
+ Map deferredLockManagers = ConcurrencyManager.getDeferredLockManagersSnapshot();
+ Map readLockManagersOriginal = ConcurrencyManager.getReadLockManagersSnapshot();
+ Map mapThreadToWaitOnAcquireOriginal = ConcurrencyManager.getThreadsToWaitOnAcquireSnapshot();
+ Map mapThreadToWaitOnAcquireMethodNameOriginal = ConcurrencyManager.getThreadsToWaitOnAcquireMethodNameSnapshot();
+ Map mapThreadToWaitOnAcquireReadLockOriginal = ConcurrencyManager.getThreadsToWaitOnAcquireReadLockSnapshot();
+ Map mapThreadToWaitOnAcquireReadLockMethodNameOriginal = ConcurrencyManager.getThreadsToWaitOnAcquireReadLockMethodNameSnapshot();
+ Map> mapThreadToWaitOnAcquireInsideWriteLockManagerOriginal = WriteLockManager.getThreadToFailToAcquireCacheKeysSnapshot();
+ Set setThreadWaitingToReleaseDeferredLocksOriginal = ConcurrencyManager.getThreadsWaitingToReleaseDeferredLocksSnapshot();
+ Map mapThreadsThatAreCurrentlyWaitingToReleaseDeferredLocksJustificationClone = ConcurrencyManager.getThreadsWaitingToReleaseDeferredLocksJustificationSnapshot();
+ Map> mapThreadToObjectIdWithWriteLockManagerChangesOriginal = WriteLockManager.getMapWriteLockManagerThreadToObjectIdsWithChangeSetSnapshot();
+ Map mapThreadsToWaitMergeManagerWaitingDeferredCacheKeys = AbstractSession.getThreadsToWaitMergeManagerWaitingDeferredCacheKeysSnapshot();
dumpConcurrencyManagerInformationStep01(
deferredLockManagers,
readLockManagersOriginal,
@@ -484,7 +501,8 @@ public void dumpConcurrencyManagerInformationIfAppropriate() {
mapThreadToWaitOnAcquireReadLockMethodNameOriginal,
setThreadWaitingToReleaseDeferredLocksOriginal,
mapThreadsThatAreCurrentlyWaitingToReleaseDeferredLocksJustificationClone,
- mapThreadToObjectIdWithWriteLockManagerChangesOriginal);
+ mapThreadToObjectIdWithWriteLockManagerChangesOriginal,
+ mapThreadsToWaitMergeManagerWaitingDeferredCacheKeys);
}
/**
@@ -538,7 +556,8 @@ protected void dumpConcurrencyManagerInformationStep01(Map mapThreadToWaitOnAcquireReadLockMethodNameOriginal,
Set setThreadWaitingToReleaseDeferredLocksOriginal,
Map mapThreadsThatAreCurrentlyWaitingToReleaseDeferredLocksJustificationClone,
- Map> mapThreadToObjectIdWithWriteLockManagerChangesOriginal) {
+ Map> mapThreadToObjectIdWithWriteLockManagerChangesOriginal,
+ Map mapThreadsToWaitMergeManagerWaitingDeferredCacheKeys) {
// (a) create object to represent our cache state.
ConcurrencyManagerState concurrencyManagerState = createConcurrencyManagerState(
deferredLockManagers,
@@ -550,7 +569,8 @@ protected void dumpConcurrencyManagerInformationStep01(Mapissue 2094.
+ * @param mapThreadsToWaitMergeManagerWaitingDeferredCacheKeys
+ * This parameter is related to the
+ * {@link AbstractSession#THREADS_TO_WAIT_MERGE_MANAGER_WAITING_DEFERRED_CACHE_KEYS}
+ * and to the issue 2094 it allows check
+ * threads that are at post-commit phase and are trying to merge their change set into the original
+ * objects in the cache. When this is taking place some of the cache keys that the merge manager is
+ * needing might be locked by other threads. This can lead to deadlocks, if our merge manager thread
+ * happens to be the owner of cache keys that matter to the owner of the cache keys the merge manager
+ * will need to acquire.
+ * @return A string describing all threads that are stuck in the
+ * {@code org.eclipse.persistence.internal.sessions.AbstractSession.getCacheKeyFromTargetSessionForMergeScenarioMergeManagerNotNullAndCacheKeyOriginalStillNull(CacheKey, Object, ObjectBuilder, ClassDescriptor, MergeManager) }
+ * logic. There is thread that want to return to the merge manage a cacheKey where the cacheKey object is no
+ * longer null. The threads are stuck because the cache key object is still null and the cache key is
+ * acquired most likely by some random thread doing object building. Deadlocks may be occurring between the
+ * merge manager and the object building threads.
+ */
+ private String createInformationAboutThreadsHavingDifficultyGettingCacheKeysWithObjectDifferentThanNullDuringMergeClonesToCacheAfterTransactionCommit(
+ Map mapThreadsToWaitMergeManagerWaitingDeferredCacheKeys) {
+ // (a) Create a header string of information
+ StringWriter writer = new StringWriter();
+ writer.write(TraceLocalization.buildMessage("concurrency_util_threads_having_difficulty_getting_cache_keys_with_object_different_than_null_during_merge_clones_to_cache_after_transaction_commit_page_header"
+ , new Object[] {mapThreadsToWaitMergeManagerWaitingDeferredCacheKeys.size()}));
+ int currentThreadNumber = 0;
+ for (Map.Entry currentEntry : mapThreadsToWaitMergeManagerWaitingDeferredCacheKeys.entrySet()) {
+ currentThreadNumber++;
+ Thread thread = currentEntry.getKey();
+ String justification = currentEntry.getValue();
+ writer.write(TraceLocalization.buildMessage("concurrency_util_threads_having_difficulty_getting_cache_keys_with_object_different_than_null_during_merge_clones_to_cache_after_transaction_commit_body",
+ new Object[] {currentThreadNumber, thread.getName(), justification}));
+ }
+ writer.write(TraceLocalization.buildMessage("concurrency_util_threads_having_difficulty_getting_cache_keys_with_object_different_than_null_during_merge_clones_to_cache_after_transaction_commit_page_end"));
+ return writer.toString();
+ }
+
/**
* create a DTO that tries to represent the current snapshot of the concurrency manager and write lock manager cache
* state
@@ -683,7 +746,8 @@ public ConcurrencyManagerState createConcurrencyManagerState(
Map mapThreadToWaitOnAcquireReadLockMethodNameOriginal,
Set setThreadWaitingToReleaseDeferredLocksOriginal,
Map mapThreadsThatAreCurrentlyWaitingToReleaseDeferredLocksJustificationClone,
- Map> mapThreadToObjectIdWithWriteLockManagerChangesOriginal) {
+ Map> mapThreadToObjectIdWithWriteLockManagerChangesOriginal,
+ Map mapThreadsToWaitMergeManagerWaitingDeferredCacheKeys) {
// (a) As a first step we want to clone-copy the two maps
// once we start working with the maps and using them to do dead lock detection
// or simply print the state of the system we do not want the maps to continue changing as the threads referenced in the maps
@@ -740,7 +804,8 @@ public ConcurrencyManagerState createConcurrencyManagerState(
readLockManagerMapClone, deferredLockManagerMapClone, unifiedMapOfThreadsStuckTryingToAcquireWriteLock,
mapThreadToWaitOnAcquireMethodNameClone, mapThreadToWaitOnAcquireReadLockClone, mapThreadToWaitOnAcquireReadLockMethodNameClone,
setThreadWaitingToReleaseDeferredLocksClone, mapThreadsThatAreCurrentlyWaitingToReleaseDeferredLocksJustificationClone,
- mapOfCacheKeyToDtosExplainingThreadExpectationsOnCacheKey, mapThreadToObjectIdWithWriteLockManagerChangesClone);
+ mapOfCacheKeyToDtosExplainingThreadExpectationsOnCacheKey, mapThreadToObjectIdWithWriteLockManagerChangesClone,
+ mapThreadsToWaitMergeManagerWaitingDeferredCacheKeys);
}
/**
@@ -1689,4 +1754,18 @@ private boolean getBooleanProperty(final String key, final boolean defaultValue)
}
return defaultValue;
}
+
+ private String getStringProperty(final String key, final String defaultValue) {
+ final String value = PrivilegedAccessHelper.callDoPrivileged(
+ () -> System.getProperty(key, String.valueOf(defaultValue))
+ );
+ if (value != null) {
+ try {
+ return value.trim();
+ } catch (Exception ignoreE) {
+ return defaultValue;
+ }
+ }
+ return defaultValue;
+ }
}
\ No newline at end of file
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/helper/Helper.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/helper/Helper.java
index b331dc7ba58..e65b83a0b7b 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/helper/Helper.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/helper/Helper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2025 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2022 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -44,6 +44,7 @@
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
+import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
@@ -52,10 +53,12 @@
import java.util.List;
import java.util.Map;
import java.util.Queue;
+import java.util.Set;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.Vector;
import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.stream.Collectors;
import org.eclipse.persistence.config.SystemProperties;
import org.eclipse.persistence.exceptions.ConversionException;
@@ -800,6 +803,27 @@ public static Vector copyVector(List originalVector, int startIndex, int stopInd
return newVector;
}
+ /** Return a copy of the map. Returns an unmodifiable map.
+ * @param originalMap - original map
+ * @return Unmodifiable copy of originalMap
+ */
+ public static Map copyMap(Map originalMap) {
+ Set> entries = originalMap.entrySet();
+ Map