mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-10 13:17:09 +07:00
[python] Remove TypeEvalContextBasedCache
GitOrigin-RevId: 74d87561c3cc857ede78e60f19a7edbad35f29ee
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9f862f5b7e
commit
b619e21989
-71
@@ -1,71 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.psi.types;
|
||||
|
||||
import com.intellij.psi.util.CachedValue;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* Engine to cache something in map, where {@link TypeEvalContext} is used as key.
|
||||
* This cache is weak-based (no memory leaks), thread safe and purges on any PSI change.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class TypeEvalContextBasedCache<T> {
|
||||
private final @NotNull CachedValue<ConcurrentMap<TypeEvalConstraints, T>> myCachedMapStorage;
|
||||
|
||||
private final @NotNull Function<? super TypeEvalContext, ? extends T> myProvider;
|
||||
|
||||
/**
|
||||
* @param manager Cache manager to be used to store cache
|
||||
* @param valueProvider engine to create value based on context.
|
||||
*/
|
||||
public TypeEvalContextBasedCache(final @NotNull CachedValuesManager manager,
|
||||
final @NotNull Function<? super TypeEvalContext, ? extends T> valueProvider) {
|
||||
myCachedMapStorage = manager.createCachedValue(new MapCreator<>(), false);
|
||||
myProvider = valueProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value (executes provider to obtain new if no any and stores it in cache).
|
||||
* It is better to run this method under read action to make sure PSI not modified in the middle of its execution
|
||||
*
|
||||
* @param context to be used as key
|
||||
* @return value
|
||||
*/
|
||||
public @NotNull T getValue(final @NotNull TypeEvalContext context) {
|
||||
|
||||
// map is thread safe but not atomic nor getValue() is, so in worst case several threads may produce same result
|
||||
// myProvider.fun should never be launched under lock to prevent deadlocks like PY-24300 and PY-24625
|
||||
// both explicit locking and computeIfAbsent leads to deadlock
|
||||
final ConcurrentMap<TypeEvalConstraints, T> map = myCachedMapStorage.getValue();
|
||||
final TypeEvalConstraints key = context.getConstraints();
|
||||
final T value = map.get(key);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
final T newValue = myProvider.fun(context);
|
||||
T oldValue =
|
||||
map.putIfAbsent(key, newValue);// ConcurrentMap guarantees happens-before so from this moment get() should work in other threads
|
||||
return oldValue == null ? newValue : oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider that creates map to store cache. Map depends on PSI modification
|
||||
*/
|
||||
private static final class MapCreator<T> implements CachedValueProvider<ConcurrentMap<TypeEvalConstraints, T>> {
|
||||
@Override
|
||||
public @NotNull Result<ConcurrentMap<TypeEvalConstraints, T>> 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, T> map = ContainerUtil.createConcurrentSoftValueMap();
|
||||
return new Result<>(map, PsiModificationTracker.MODIFICATION_COUNT);
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
-13
@@ -2,10 +2,15 @@
|
||||
package com.jetbrains.python.psi.types;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.util.CachedValue;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* Caches context by their constraints (to prevent context cache loss). Flushes cache every PSI change or low memory conditions.
|
||||
* Class is thread safe.
|
||||
@@ -14,23 +19,32 @@ import org.jetbrains.annotations.NotNull;
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class TypeEvalContextCacheImpl implements TypeEvalContextCache {
|
||||
private static final @NotNull Function<TypeEvalContext, TypeEvalContext> VALUE_PROVIDER = new MyValueProvider();
|
||||
private final @NotNull TypeEvalContextBasedCache<TypeEvalContext> myCache;
|
||||
private final @NotNull CachedValue<ConcurrentMap<TypeEvalConstraints, TypeEvalContext>> myCachedMapStorage;
|
||||
|
||||
TypeEvalContextCacheImpl(@NotNull Project project) {
|
||||
myCache = new TypeEvalContextBasedCache<>(CachedValuesManager.getManager(project), VALUE_PROVIDER);
|
||||
myCachedMapStorage = 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, PsiModificationTracker.MODIFICATION_COUNT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TypeEvalContext getContext(final @NotNull TypeEvalContext standard) {
|
||||
return myCache.getValue(standard);
|
||||
}
|
||||
|
||||
private static class MyValueProvider implements Function<TypeEvalContext, TypeEvalContext> {
|
||||
@Override
|
||||
public TypeEvalContext fun(final TypeEvalContext param) {
|
||||
// key and value are both context here. If no context stored, then key is stored. Old one is returned otherwise to cache.
|
||||
return param;
|
||||
public @NotNull TypeEvalContext getContext(@NotNull TypeEvalContext standard) {
|
||||
// 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 TypeEvalContext cachedContext = map.get(key);
|
||||
if (cachedContext != null) {
|
||||
return cachedContext;
|
||||
}
|
||||
TypeEvalContext oldValue =
|
||||
map.putIfAbsent(key, standard);// ConcurrentMap guarantees happens-before so from this moment get() should work in other threads
|
||||
return oldValue == null ? standard : oldValue;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user