Skip to content

Commit

Permalink
Performance improvement: synchronize to java.util.concurrent.locks sw…
Browse files Browse the repository at this point in the history
…itch to improve performance with VirtualThreads (#2119)

This change improves EclipseLink performance with higher versions of the JDK (21 and above).
It's about replacement of synchronized keyword and wait(), notify() methods by objects from java.util.concurrent.locks.* package.
Additionally, there are some new performance tests to verify it and performance tests refresh.


(cherry picked from commit 1f0cf75)

Signed-off-by: Radek Felcman <radek.felcman@oracle.com>
  • Loading branch information
rfelcman committed Apr 29, 2024
1 parent 36e9af7 commit bee608e
Show file tree
Hide file tree
Showing 22 changed files with 1,109 additions and 278 deletions.
15 changes: 14 additions & 1 deletion antbuild.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!--
Copyright (c) 1998, 2023 Oracle and/or its affiliates. All rights reserved.
Copyright (c) 1998, 2024 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
Expand Down Expand Up @@ -71,6 +71,10 @@
- test-oxm : runs MOXY oxm tests
- test-sdo : runs SDO tests
- test-sdo-srg : runs SDO SRG
- test-core-perf: runs the core performance tests
- test-moxy-perf: run the MOXy performance tests
- test-jpa-metadata-perf: run the JPA metadata performance tests
- test-jpa-perf: run the JPA performance tests
It may require some configuration of the build.properties to run.
-->
Expand Down Expand Up @@ -1968,6 +1972,15 @@
<target name="test-perf" description="run the performance tests">
<ant antfile="antbuild.xml" dir="${eclipselink.perf.test}" target="test"/>
</target>
<target name="test-core-perf" description="run the core performance tests">
<ant antfile="antbuild.xml" dir="${eclipselink.perf.test}" target="test-core"/>
</target>
<target name="test-moxy-perf" description="run the MOXy performance tests">
<ant antfile="antbuild.xml" dir="${eclipselink.perf.test}" target="test-moxy"/>
</target>
<target name="test-jpa-metadata-perf" description="run the performance tests">
<ant antfile="antbuild.xml" dir="${eclipselink.perf.test}" target="test-jpa-metadata"/>
</target>
<target name="test-jpa-perf" description="run the performance tests">
<ant antfile="antbuild.xml" dir="${eclipselink.perf.test}" target="test-jpa"/>
</target>
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2024 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
Expand All @@ -17,6 +17,8 @@
import org.eclipse.persistence.internal.helper.type.ReadLockAcquisitionMetadata;

import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReadLockManager {

Expand All @@ -42,21 +44,28 @@ public class ReadLockManager {
*/
private final List<String> removeReadLockProblemsDetected = new ArrayList<>();

private final Lock instanceLock = new ReentrantLock();

/**
* add a concurrency manager as deferred locks to the DLM
*/
public synchronized void addReadLock(ConcurrencyManager concurrencyManager) {
final Thread currentThread = Thread.currentThread();
final long currentThreadId = currentThread.getId();
ReadLockAcquisitionMetadata readLockAcquisitionMetadata = ConcurrencyUtil.SINGLETON.createReadLockAcquisitionMetadata(concurrencyManager);

this.readLocks.add(FIRST_INDEX_OF_COLLECTION, concurrencyManager);
if(!mapThreadToReadLockAcquisitionMetadata.containsKey(currentThreadId)) {
List<ReadLockAcquisitionMetadata> newList = Collections.synchronizedList(new ArrayList<ReadLockAcquisitionMetadata>());
mapThreadToReadLockAcquisitionMetadata.put(currentThreadId, newList );
public void addReadLock(ConcurrencyManager concurrencyManager) {
instanceLock.lock();
try {
final Thread currentThread = Thread.currentThread();
final long currentThreadId = currentThread.getId();
ReadLockAcquisitionMetadata readLockAcquisitionMetadata = ConcurrencyUtil.SINGLETON.createReadLockAcquisitionMetadata(concurrencyManager);

this.readLocks.add(FIRST_INDEX_OF_COLLECTION, concurrencyManager);
if (!mapThreadToReadLockAcquisitionMetadata.containsKey(currentThreadId)) {
List<ReadLockAcquisitionMetadata> newList = Collections.synchronizedList(new ArrayList<ReadLockAcquisitionMetadata>());
mapThreadToReadLockAcquisitionMetadata.put(currentThreadId, newList);
}
List<ReadLockAcquisitionMetadata> acquiredReadLocksInCurrentTransactionList = mapThreadToReadLockAcquisitionMetadata.get(currentThreadId);
acquiredReadLocksInCurrentTransactionList.add(FIRST_INDEX_OF_COLLECTION, readLockAcquisitionMetadata);
} finally {
instanceLock.unlock();
}
List<ReadLockAcquisitionMetadata> acquiredReadLocksInCurrentTransactionList = mapThreadToReadLockAcquisitionMetadata.get(currentThreadId);
acquiredReadLocksInCurrentTransactionList.add(FIRST_INDEX_OF_COLLECTION, readLockAcquisitionMetadata);
}

/**
Expand All @@ -67,46 +76,56 @@ public synchronized void addReadLock(ConcurrencyManager concurrencyManager) {
* @param concurrencyManager
* the concurrency cache key that is about to be decrement in number of readers.
*/
public synchronized void removeReadLock(ConcurrencyManager concurrencyManager) {
final Thread currentThread = Thread.currentThread();
final long currentThreadId = currentThread.getId();
boolean readLockManagerHasTracingAboutAddedReadLocksForCurrentThread = mapThreadToReadLockAcquisitionMetadata.containsKey(currentThreadId);

if (!readLockManagerHasTracingAboutAddedReadLocksForCurrentThread) {
String errorMessage = ConcurrencyUtil.SINGLETON.readLockManagerProblem02ReadLockManageHasNoEntriesForThread(concurrencyManager, currentThreadId);
removeReadLockProblemsDetected.add(errorMessage);
return;
}
public void removeReadLock(ConcurrencyManager concurrencyManager) {
instanceLock.lock();
try {
final Thread currentThread = Thread.currentThread();
final long currentThreadId = currentThread.getId();
boolean readLockManagerHasTracingAboutAddedReadLocksForCurrentThread = mapThreadToReadLockAcquisitionMetadata.containsKey(currentThreadId);

if (!readLockManagerHasTracingAboutAddedReadLocksForCurrentThread) {
String errorMessage = ConcurrencyUtil.SINGLETON.readLockManagerProblem02ReadLockManageHasNoEntriesForThread(concurrencyManager, currentThreadId);
removeReadLockProblemsDetected.add(errorMessage);
return;
}

List<ReadLockAcquisitionMetadata> readLocksAcquiredDuringCurrentThread = mapThreadToReadLockAcquisitionMetadata.get(currentThreadId);
ReadLockAcquisitionMetadata readLockAquisitionMetadataToRemove = null;
for (ReadLockAcquisitionMetadata currentReadLockAcquisitionMetadata : readLocksAcquiredDuringCurrentThread) {
ConcurrencyManager currentCacheKeyObjectToCheck = currentReadLockAcquisitionMetadata.getCacheKeyWhoseNumberOfReadersThreadIsIncrementing();
boolean dtoToRemoveFound = concurrencyManager.getConcurrencyManagerId() == currentCacheKeyObjectToCheck.getConcurrencyManagerId();
if (dtoToRemoveFound) {
readLockAquisitionMetadataToRemove = currentReadLockAcquisitionMetadata;
break;
List<ReadLockAcquisitionMetadata> readLocksAcquiredDuringCurrentThread = mapThreadToReadLockAcquisitionMetadata.get(currentThreadId);
ReadLockAcquisitionMetadata readLockAquisitionMetadataToRemove = null;
for (ReadLockAcquisitionMetadata currentReadLockAcquisitionMetadata : readLocksAcquiredDuringCurrentThread) {
ConcurrencyManager currentCacheKeyObjectToCheck = currentReadLockAcquisitionMetadata.getCacheKeyWhoseNumberOfReadersThreadIsIncrementing();
boolean dtoToRemoveFound = concurrencyManager.getConcurrencyManagerId() == currentCacheKeyObjectToCheck.getConcurrencyManagerId();
if (dtoToRemoveFound) {
readLockAquisitionMetadataToRemove = currentReadLockAcquisitionMetadata;
break;
}
}
}

if (readLockAquisitionMetadataToRemove == null) {
String errorMessage = ConcurrencyUtil.SINGLETON.readLockManagerProblem03ReadLockManageHasNoEntriesForThread(concurrencyManager, currentThreadId);
removeReadLockProblemsDetected.add(errorMessage);
return;
}
this.readLocks.remove(concurrencyManager);
readLocksAcquiredDuringCurrentThread.remove(readLockAquisitionMetadataToRemove);
if (readLockAquisitionMetadataToRemove == null) {
String errorMessage = ConcurrencyUtil.SINGLETON.readLockManagerProblem03ReadLockManageHasNoEntriesForThread(concurrencyManager, currentThreadId);
removeReadLockProblemsDetected.add(errorMessage);
return;
}
this.readLocks.remove(concurrencyManager);
readLocksAcquiredDuringCurrentThread.remove(readLockAquisitionMetadataToRemove);

if (readLocksAcquiredDuringCurrentThread.isEmpty()) {
mapThreadToReadLockAcquisitionMetadata.remove(currentThreadId);
if (readLocksAcquiredDuringCurrentThread.isEmpty()) {
mapThreadToReadLockAcquisitionMetadata.remove(currentThreadId);
}
} finally {
instanceLock.unlock();
}
}

/**
* Return a set of the deferred locks
*/
public synchronized List<ConcurrencyManager> getReadLocks() {
return Collections.unmodifiableList(readLocks);
public List<ConcurrencyManager> getReadLocks() {
instanceLock.lock();
try {
return Collections.unmodifiableList(readLocks);
} finally {
instanceLock.unlock();
}
}

/**
Expand All @@ -116,8 +135,13 @@ public synchronized List<ConcurrencyManager> getReadLocks() {
* @param problemDetected
* the detected problem
*/
public synchronized void addRemoveReadLockProblemsDetected(String problemDetected) {
removeReadLockProblemsDetected.add(problemDetected);
public void addRemoveReadLockProblemsDetected(String problemDetected) {
instanceLock.lock();
try {
removeReadLockProblemsDetected.add(problemDetected);
} finally {
instanceLock.unlock();
}
}

/** Getter for {@link #mapThreadToReadLockAcquisitionMetadata} */
Expand All @@ -139,8 +163,13 @@ public List<String> getRemoveReadLockProblemsDetected() {
* any read lock acquired in the tracing we definitely do not want this object instance to be thrown out
* from our main tracing. It is probably revealing problems in read lock acquisition and released.
*/
public synchronized boolean isEmpty() {
return readLocks.isEmpty() && removeReadLockProblemsDetected.isEmpty();
public boolean isEmpty() {
instanceLock.lock();
try {
return readLocks.isEmpty() && removeReadLockProblemsDetected.isEmpty();
} finally {
instanceLock.unlock();
}
}

/**
Expand All @@ -154,16 +183,21 @@ public synchronized boolean isEmpty() {
*/
@SuppressWarnings("unchecked")
@Override
public synchronized ReadLockManager clone() {
ReadLockManager clone = new ReadLockManager();
clone.readLocks.addAll(this.readLocks);
for (Map.Entry<Long, List<ReadLockAcquisitionMetadata>> currentEntry : this.mapThreadToReadLockAcquisitionMetadata.entrySet()) {
Long key = currentEntry.getKey();
List<ReadLockAcquisitionMetadata> value = currentEntry.getValue();
clone.mapThreadToReadLockAcquisitionMetadata.put(key, new ArrayList<>(value));
public ReadLockManager clone() {
instanceLock.lock();
try {
ReadLockManager clone = new ReadLockManager();
clone.readLocks.addAll(this.readLocks);
for (Map.Entry<Long, List<ReadLockAcquisitionMetadata>> currentEntry : this.mapThreadToReadLockAcquisitionMetadata.entrySet()) {
Long key = currentEntry.getKey();
List<ReadLockAcquisitionMetadata> value = currentEntry.getValue();
clone.mapThreadToReadLockAcquisitionMetadata.put(key, new ArrayList<>(value));
}
clone.removeReadLockProblemsDetected.addAll(this.removeReadLockProblemsDetected);
return clone;
} finally {
instanceLock.unlock();
}
clone.removeReadLockProblemsDetected.addAll(this.removeReadLockProblemsDetected);
return clone;
}

}
Loading

0 comments on commit bee608e

Please sign in to comment.