PY-75831 Split cache(resolve/type) into library and user part

GitOrigin-RevId: 8dfd0120379c9a34051d66e147ffdc2c69f0db66
This commit is contained in:
Andrey Vokin
2025-08-03 07:11:08 +02:00
committed by intellij-monorepo-bot
parent e986be3990
commit 385d275011
5 changed files with 148 additions and 3 deletions

View File

@@ -492,6 +492,8 @@
description="Require marking namespace packages explicitly, treat regular directories as implicit source roots"/>
<registryKey key="python.type.hints.literal.string" defaultValue="true"
description="When enabled, activates LiteralString inference for Python string literals" />
<registryKey key="python.use.separated.libraries.type.cache" defaultValue="true"
description="It enables the use of a library-level cache for PSI elements from packages."/>
<registryKey key="python.statement.lists.incremental.reparse" defaultValue="false"
description="Enables incremental reparse for statement lists"/>
</extensions>

View File

@@ -23,6 +23,7 @@ import java.util.concurrent.ConcurrentMap;
*/
final class TypeEvalContextCacheImpl implements TypeEvalContextCache, Disposable {
private final @NotNull CachedValue<ConcurrentMap<TypeEvalConstraints, TypeEvalContext>> myCachedMapStorage;
private final @NotNull CachedValue<ConcurrentMap<TypeEvalConstraints, TypeEvalContext>> myLibrariesCachedMapStorage;
private final LowMemoryWatcher myLowMemoryWatcher;
private final SimpleModificationTracker myLowMemoryModificationTracker = new SimpleModificationTracker();
@@ -36,6 +37,15 @@ final class TypeEvalContextCacheImpl implements TypeEvalContextCache, Disposable
return new CachedValueProvider.Result<>(map, PsiModificationTracker.MODIFICATION_COUNT, myLowMemoryModificationTracker);
}
});
myLibrariesCachedMapStorage = CachedValuesManager.getManager(project).createCachedValue(new CachedValueProvider<>() {
@Override
public @NotNull CachedValueProvider.Result<ConcurrentMap<TypeEvalConstraints, TypeEvalContext>> compute() {
// This method is called if cache is empty. Create new map for it.
// Concurrent map allows several threads to call get and put, so it is thread safe but not atomic
final ConcurrentMap<TypeEvalConstraints, TypeEvalContext> map = ContainerUtil.createConcurrentSoftValueMap();
return new CachedValueProvider.Result<>(map, PyLibraryModificationTracker.Companion.getInstance(project));
}
});
myLowMemoryWatcher = LowMemoryWatcher.register(() -> {
myLowMemoryModificationTracker.incModificationCount();
@@ -43,12 +53,12 @@ final class TypeEvalContextCacheImpl implements TypeEvalContextCache, Disposable
});
}
@Override
public @NotNull TypeEvalContext getContext(@NotNull TypeEvalContext standard) {
private static TypeEvalContext retrieveFromStorage(@NotNull TypeEvalContext standard,
CachedValue<ConcurrentMap<TypeEvalConstraints, TypeEvalContext>> storage) {
// map is thread safe but not atomic nor getValue() is, so in worst case several threads may produce same result
// both explicit locking and computeIfAbsent leads to deadlock
final ConcurrentMap<TypeEvalConstraints, TypeEvalContext> map = myCachedMapStorage.getValue();
final TypeEvalConstraints key = standard.getConstraints();
final ConcurrentMap<TypeEvalConstraints, TypeEvalContext> map = storage.getValue();
final TypeEvalContext cachedContext = map.get(key);
if (cachedContext != null) {
return cachedContext;
@@ -58,6 +68,16 @@ final class TypeEvalContextCacheImpl implements TypeEvalContextCache, Disposable
return oldValue == null ? standard : oldValue;
}
@Override
public @NotNull TypeEvalContext getContext(@NotNull TypeEvalContext standard) {
return retrieveFromStorage(standard, myCachedMapStorage);
}
@Override
public @NotNull TypeEvalContext getLibraryContext(@NotNull TypeEvalContext standard) {
return retrieveFromStorage(standard, myLibrariesCachedMapStorage);
}
@Override
public void dispose() {
myLowMemoryWatcher.stop();