1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-06 17:26:01 +02:00

Implement a thread safe solution for computeIfAbsent

This commit is contained in:
Ionut Matei 2025-02-09 13:14:37 +02:00 committed by Jonah Graham
parent 971204426e
commit 8bc7a14a0b

View file

@ -19,13 +19,14 @@ import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -95,12 +96,16 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
private static final int FIND_RESOURCES_CACHE_SIZE = 100; private static final int FIND_RESOURCES_CACHE_SIZE = 100;
private Map<URI, IResource[]> workspaceRootFindContainersForLocationURICache = Collections private LRUCache<URI, IResource[]> workspaceRootFindContainersForLocationURICache = new LRUCache<>(
.synchronizedMap(new LRUCache<>(FIND_RESOURCES_CACHE_SIZE)); FIND_RESOURCES_CACHE_SIZE);
private Map<URI, IResource[]> workspaceRootFindFilesForLocationURICache = Collections private LRUCache<URI, IResource[]> workspaceRootFindFilesForLocationURICache = new LRUCache<>(
.synchronizedMap(new LRUCache<>(FIND_RESOURCES_CACHE_SIZE)); FIND_RESOURCES_CACHE_SIZE);
private Map<IProject, LRUCache<IPath, List<IResource>>> findPathInProjectCache = Collections private HashMap<IProject, LRUCache<IPath, List<IResource>>> findPathInProjectCache = new HashMap<>();
.synchronizedMap(new HashMap<>());
private final ReentrantReadWriteLock findContainersForLocationURICacheLock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock findFilesForLocationURICacheLock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock findPathInProjectCacheLock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock findPathInFoldertCacheLock = new ReentrantReadWriteLock();
//String pathStr, URI baseURI -> URI //String pathStr, URI baseURI -> URI
private static class MappedURIKey { private static class MappedURIKey {
@ -146,14 +151,14 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
} }
// Caches the result of determineMappedURI // Caches the result of determineMappedURI
private Map<MappedURIKey, URI> mappedURICache = Collections private LRUCache<MappedURIKey, URI> mappedURICache = new LRUCache<>(FIND_RESOURCES_CACHE_SIZE);
.synchronizedMap(new LRUCache<>(FIND_RESOURCES_CACHE_SIZE));
// Caches the result of getFilesystemLocation // Caches the result of getFilesystemLocation
private Map<URI, IPath> fileSystemLocationCache = Collections private LRUCache<URI, IPath> fileSystemLocationCache = new LRUCache<>(FIND_RESOURCES_CACHE_SIZE);
.synchronizedMap(new LRUCache<>(FIND_RESOURCES_CACHE_SIZE));
// Caches the result of new File(pathname).exists() // Caches the result of new File(pathname).exists()
private Map<IPath, Boolean> pathExistsCache = Collections private LRUCache<IPath, Boolean> pathExistsCache = new LRUCache<>(FIND_RESOURCES_CACHE_SIZE);
.synchronizedMap(new LRUCache<>(FIND_RESOURCES_CACHE_SIZE)); private final ReentrantReadWriteLock mappedURICacheLock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock fileSystemLocationCacheLock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock pathExistsCacheLock = new ReentrantReadWriteLock();
/** @since 8.2 */ /** @since 8.2 */
protected EFSExtensionProvider efsProvider = null; protected EFSExtensionProvider efsProvider = null;
@ -797,8 +802,9 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
} }
IResource sourceFile = null; IResource sourceFile = null;
IResource[] resources = workspaceRootFindFilesForLocationURICache.computeIfAbsent(uri, IResource[] resources = threadSafeComputeIfAbsent(uri, workspaceRootFindFilesForLocationURICache,
key -> ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(key)); key -> ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(key),
findFilesForLocationURICacheLock);
for (IResource rc : resources) { for (IResource rc : resources) {
if (!checkExistence || rc.isAccessible()) { if (!checkExistence || rc.isAccessible()) {
if (rc.getProject().equals(preferredProject)) { if (rc.getProject().equals(preferredProject)) {
@ -820,8 +826,9 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
private IResource findContainerForLocationURI(URI uri, IProject preferredProject, boolean checkExistence) { private IResource findContainerForLocationURI(URI uri, IProject preferredProject, boolean checkExistence) {
IResource resource = null; IResource resource = null;
IResource[] resources = workspaceRootFindContainersForLocationURICache.computeIfAbsent(uri, IResource[] resources = threadSafeComputeIfAbsent(uri, workspaceRootFindContainersForLocationURICache,
key -> ResourcesPlugin.getWorkspace().getRoot().findContainersForLocationURI(key)); (key) -> ResourcesPlugin.getWorkspace().getRoot().findContainersForLocationURI(key),
findContainersForLocationURICacheLock);
for (IResource rc : resources) { for (IResource rc : resources) {
if ((rc instanceof IProject || rc instanceof IFolder) && (!checkExistence || rc.isAccessible())) { // treat IWorkspaceRoot as non-workspace path if ((rc instanceof IProject || rc instanceof IFolder) && (!checkExistence || rc.isAccessible())) { // treat IWorkspaceRoot as non-workspace path
if (rc.getProject().equals(preferredProject)) { if (rc.getProject().equals(preferredProject)) {
@ -1016,7 +1023,7 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
* @return {@link URI} of the resource * @return {@link URI} of the resource
*/ */
private URI determineMappedURI(String pathStr, URI baseURI) { private URI determineMappedURI(String pathStr, URI baseURI) {
return mappedURICache.computeIfAbsent(new MappedURIKey(baseURI, pathStr), key -> { return threadSafeComputeIfAbsent(new MappedURIKey(baseURI, pathStr), mappedURICache, key -> {
URI uri = null; URI uri = null;
if (baseURI == null) { if (baseURI == null) {
@ -1045,16 +1052,16 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
uri = org.eclipse.core.filesystem.URIUtil.toURI(pathStr); uri = org.eclipse.core.filesystem.URIUtil.toURI(pathStr);
} }
return uri; return uri;
}); }, mappedURICacheLock);
} }
/** /**
* Find all resources in the project which might be represented by relative path passed. * Find all resources in the project which might be represented by relative path passed.
*/ */
private List<IResource> findPathInProject(IPath path, IProject project) { private List<IResource> findPathInProject(IPath path, IProject project) {
LRUCache<IPath, List<IResource>> cache = findPathInProjectCache.computeIfAbsent(project, LRUCache<IPath, List<IResource>> cache = threadSafeComputeIfAbsent(project, findPathInProjectCache,
key -> new LRUCache<>(FIND_RESOURCES_CACHE_SIZE)); key -> new LRUCache<IPath, List<IResource>>(FIND_RESOURCES_CACHE_SIZE), findPathInProjectCacheLock);
return cache.computeIfAbsent(path, key -> findPathInFolder(path, project)); return threadSafeComputeIfAbsent(path, cache, key -> findPathInFolder(path, project), findPathInFoldertCacheLock);
} }
/** /**
@ -1162,8 +1169,7 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
private IPath getFilesystemLocation(URI uri) { private IPath getFilesystemLocation(URI uri) {
if (uri == null) if (uri == null)
return null; return null;
return threadSafeComputeIfAbsent(uri, fileSystemLocationCache, (k) -> {
return fileSystemLocationCache.computeIfAbsent(uri, (k) -> {
String pathStr = efsProvider.getMappedPath(uri); String pathStr = efsProvider.getMappedPath(uri);
URI resUri = org.eclipse.core.filesystem.URIUtil.toURI(pathStr); URI resUri = org.eclipse.core.filesystem.URIUtil.toURI(pathStr);
@ -1180,7 +1186,7 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
} }
} }
return null; return null;
}); }, fileSystemLocationCacheLock);
} }
/** /**
@ -1261,9 +1267,9 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
IPath location = getFilesystemLocation(uri); IPath location = getFilesystemLocation(uri);
if (location != null) { if (location != null) {
String loc = location.toString(); String loc = location.toString();
boolean exists = pathExistsCache.computeIfAbsent(location, (s) -> { boolean exists = threadSafeComputeIfAbsent(location, pathExistsCache, (s) -> {
return new File(loc).exists(); return new File(loc).exists();
}); }, pathExistsCacheLock);
if (exists) { if (exists) {
return optionParser.createEntry(loc, loc, flag); return optionParser.createEntry(loc, loc, flag);
} }
@ -1420,4 +1426,22 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
return true; return true;
} }
private static <K, V> V threadSafeComputeIfAbsent(K key, HashMap<K, V> cacheMap,
Function<? super K, ? extends V> mappingFunction, ReentrantReadWriteLock rwLock) {
rwLock.readLock().lock();
V value = cacheMap.get(key);
rwLock.readLock().unlock();
if (value != null) {
return value;
}
rwLock.writeLock().lock();
value = cacheMap.get(key);
if (value == null) {
value = cacheMap.computeIfAbsent(key, mappingFunction);
}
rwLock.writeLock().unlock();
return value;
}
} }