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