mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Revert: avoid smart pointer leaks after project closing by not storing them in VFS user data at all
because of leaks when using light files (IDEA-187382)
This commit is contained in:
+18
@@ -9,6 +9,7 @@ import com.intellij.ide.highlighter.HtmlFileType;
|
||||
import com.intellij.ide.highlighter.JavaFileType;
|
||||
import com.intellij.ide.highlighter.XmlFileType;
|
||||
import com.intellij.lang.FileASTNode;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
@@ -23,6 +24,7 @@ import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileTypes.PlainTextFileType;
|
||||
import com.intellij.openapi.fileTypes.StdFileTypes;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
@@ -43,6 +45,7 @@ import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.testFramework.*;
|
||||
import com.intellij.util.FileContentUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ref.GCUtil;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Assert;
|
||||
@@ -1074,4 +1077,19 @@ public class SmartPsiElementPointersTest extends CodeInsightTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testDoNotLeakLightVirtualFileSmartPointersReachableViaDocument() {
|
||||
Key<SmartPointerEx<PsiClass>> key = Key.create("smart pointer test");
|
||||
|
||||
Runnable createFileAndPointer = () -> {
|
||||
PsiFile file = PsiFileFactory.getInstance(myProject).createFileFromText("a.java", JavaLanguage.INSTANCE, "class Foo {}", true, false);
|
||||
SmartPointerEx<PsiClass> pointer = createPointer(((PsiJavaFile)file).getClasses()[0]);
|
||||
file.getViewProvider().getDocument().putUserData(key, pointer);
|
||||
};
|
||||
createFileAndPointer.run();
|
||||
|
||||
GCUtil.tryGcSoftlyReachableObjects();
|
||||
SmartPointerTracker.processQueue();
|
||||
LeakHunter.checkLeak(LeakHunter.allRoots(), Document.class, d -> d.getUserData(key) != null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
-27
@@ -24,7 +24,6 @@ import com.intellij.openapi.editor.impl.FrozenDocument;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.LowMemoryWatcher;
|
||||
import com.intellij.openapi.util.ProperTextRange;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -32,31 +31,26 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiDocumentManagerBase;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.reference.SoftReference;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
public class SmartPointerManagerImpl extends SmartPointerManager {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl");
|
||||
private final Project myProject;
|
||||
private final Key<SmartPointerTracker> POINTERS_KEY;
|
||||
private final PsiDocumentManagerBase myPsiDocManager;
|
||||
private final ReferenceQueue<SmartPsiElementPointerImpl> myQueue = new ReferenceQueue<>();
|
||||
private final ConcurrentMap<VirtualFile, SmartPointerTracker> myTrackerMap = ContainerUtil.newConcurrentMap();
|
||||
|
||||
public SmartPointerManagerImpl(Project project) {
|
||||
myProject = project;
|
||||
myPsiDocManager = (PsiDocumentManagerBase)PsiDocumentManager.getInstance(myProject);
|
||||
LowMemoryWatcher.register(() -> processQueue(), project);
|
||||
POINTERS_KEY = Key.create("SMART_POINTERS for "+project);
|
||||
}
|
||||
|
||||
public void fastenBelts(@NotNull VirtualFile file) {
|
||||
processQueue();
|
||||
SmartPointerTracker pointers = getTracker(file);
|
||||
if (pointers != null) pointers.fastenBelts();
|
||||
}
|
||||
@@ -86,7 +80,7 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
|
||||
PsiUtilCore.ensureValid(element);
|
||||
LOG.error("Invalid element:" + element);
|
||||
}
|
||||
processQueue();
|
||||
SmartPointerTracker.processQueue();
|
||||
SmartPsiElementPointerImpl<E> pointer = getCachedPointer(element);
|
||||
if (pointer != null &&
|
||||
(!(pointer.getElementInfo() instanceof SelfElementInfo) || ((SelfElementInfo)pointer.getElementInfo()).isForInjected() == forInjected) &&
|
||||
@@ -126,7 +120,7 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
|
||||
@NotNull TextRange range,
|
||||
boolean forInjected) {
|
||||
PsiUtilCore.ensureValid(file);
|
||||
processQueue();
|
||||
SmartPointerTracker.processQueue();
|
||||
SmartPsiFileRangePointerImpl pointer = new SmartPsiFileRangePointerImpl(file, ProperTextRange.create(range), forInjected);
|
||||
trackPointer(pointer, file.getViewProvider().getVirtualFile());
|
||||
|
||||
@@ -137,9 +131,12 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
|
||||
SmartPointerElementInfo info = pointer.getElementInfo();
|
||||
if (!(info instanceof SelfElementInfo)) return;
|
||||
|
||||
SmartPointerTracker.PointerReference reference = new SmartPointerTracker.PointerReference(pointer, containingFile, myTrackerMap, myQueue);
|
||||
SmartPointerTracker.PointerReference reference = new SmartPointerTracker.PointerReference(pointer, containingFile, POINTERS_KEY);
|
||||
while (true) {
|
||||
SmartPointerTracker pointers = myTrackerMap.computeIfAbsent(containingFile, __ -> new SmartPointerTracker());
|
||||
SmartPointerTracker pointers = getTracker(containingFile);
|
||||
if (pointers == null) {
|
||||
pointers = containingFile.putUserDataIfAbsent(POINTERS_KEY, new SmartPointerTracker());
|
||||
}
|
||||
if (pointers.addReference(reference, pointer)) {
|
||||
break;
|
||||
}
|
||||
@@ -175,14 +172,14 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
|
||||
SmartPointerTracker pointers = getTracker(vFile);
|
||||
SmartPointerTracker.PointerReference reference = ((SmartPsiElementPointerImpl)pointer).pointerReference;
|
||||
if (pointers != null && reference != null) {
|
||||
pointers.removeReference(reference, myTrackerMap);
|
||||
pointers.removeReference(reference, POINTERS_KEY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
SmartPointerTracker getTracker(@NotNull VirtualFile containingFile) {
|
||||
return myTrackerMap.get(containingFile);
|
||||
return containingFile.getUserData(POINTERS_KEY);
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@@ -208,19 +205,6 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
|
||||
if (list != null) list.updatePointerTargetsAfterReparse();
|
||||
}
|
||||
|
||||
private void processQueue() {
|
||||
while (true) {
|
||||
SmartPointerTracker.PointerReference reference = (SmartPointerTracker.PointerReference)myQueue.poll();
|
||||
if (reference == null) break;
|
||||
|
||||
SmartPointerTracker pointers = myTrackerMap.get(reference.file);
|
||||
if (pointers != null) {
|
||||
pointers.removeReference(reference, reference.trackerMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
+41
-19
@@ -15,8 +15,11 @@
|
||||
*/
|
||||
package com.intellij.psi.impl.smartPointers;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.impl.FrozenDocument;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.LowMemoryWatcher;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
@@ -33,24 +36,29 @@ import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
class SmartPointerTracker {
|
||||
private static final ReferenceQueue<SmartPsiElementPointerImpl> ourQueue = new ReferenceQueue<>();
|
||||
|
||||
private int nextAvailableIndex;
|
||||
private int size;
|
||||
private PointerReference[] references = new PointerReference[10];
|
||||
private final MarkerCache markerCache = new MarkerCache(this);
|
||||
private boolean mySorted;
|
||||
|
||||
static {
|
||||
LowMemoryWatcher.register(() -> processQueue(), ApplicationManager.getApplication());
|
||||
}
|
||||
|
||||
synchronized boolean addReference(@NotNull PointerReference reference, @NotNull SmartPsiElementPointerImpl pointer) {
|
||||
if (!isActual(reference.file, reference.trackerMap)) {
|
||||
if (!isActual(reference.file, reference.key)) {
|
||||
// this pointer list has been removed by another thread; clients should get/create an up-to-date list and try adding to it
|
||||
return false;
|
||||
}
|
||||
|
||||
if (needsExpansion() || isTooSparse()) {
|
||||
resize();
|
||||
assert isActual(reference.file, reference.trackerMap);
|
||||
assert isActual(reference.file, reference.key);
|
||||
}
|
||||
|
||||
assert references[nextAvailableIndex] == null : references[nextAvailableIndex];
|
||||
@@ -63,8 +71,8 @@ class SmartPointerTracker {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean isActual(VirtualFile file, @NotNull Map<VirtualFile, SmartPointerTracker> trackerMap) {
|
||||
return trackerMap.get(file) == this;
|
||||
boolean isActual(VirtualFile file, Key<SmartPointerTracker> key) {
|
||||
return file.getUserData(key) == this;
|
||||
}
|
||||
|
||||
private boolean needsExpansion() {
|
||||
@@ -89,22 +97,24 @@ class SmartPointerTracker {
|
||||
nextAvailableIndex = index;
|
||||
}
|
||||
|
||||
synchronized void removeReference(@NotNull PointerReference reference, @NotNull Map<VirtualFile, SmartPointerTracker> trackerMap) {
|
||||
synchronized void removeReference(@NotNull PointerReference reference, @NotNull Key<SmartPointerTracker> expectedKey) {
|
||||
int index = reference.index;
|
||||
if (index < 0) return;
|
||||
|
||||
assertActual(trackerMap, reference.file);
|
||||
assertActual(expectedKey, reference.file, reference.key);
|
||||
assert references[index] == reference : "At " + index + " expected " + reference + ", found " + references[index];
|
||||
references[index].index = -1;
|
||||
references[index] = null;
|
||||
if (--size == 0) {
|
||||
trackerMap.remove(reference.file, this);
|
||||
reference.file.replace(reference.key, this, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertActual(@NotNull Map<VirtualFile, SmartPointerTracker> trackerMap, VirtualFile file) {
|
||||
assert isActual(file, trackerMap) : "Smart pointer list mismatch mismatch:" +
|
||||
(trackerMap.containsKey(file) ? "; has another pointer list" : "");
|
||||
private void assertActual(Key<SmartPointerTracker> expectedKey, VirtualFile file, Key<SmartPointerTracker> refKey) {
|
||||
assert isActual(file, refKey) : "Smart pointer list mismatch mismatch:" +
|
||||
" ref.key=" + expectedKey +
|
||||
", manager.key=" + refKey +
|
||||
(file.getUserData(refKey) != null ? "; has another pointer list" : "");
|
||||
}
|
||||
|
||||
private void processAlivePointers(@NotNull Processor<SmartPsiElementPointerImpl<?>> processor) {
|
||||
@@ -112,10 +122,10 @@ class SmartPointerTracker {
|
||||
PointerReference ref = references[i];
|
||||
if (ref == null) continue;
|
||||
|
||||
assert isActual(ref.file, ref.trackerMap);
|
||||
assert isActual(ref.file, ref.key);
|
||||
SmartPsiElementPointerImpl pointer = ref.get();
|
||||
if (pointer == null) {
|
||||
removeReference(ref, ref.trackerMap);
|
||||
removeReference(ref, ref.key);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -167,6 +177,7 @@ class SmartPointerTracker {
|
||||
}
|
||||
|
||||
synchronized void fastenBelts() {
|
||||
processQueue();
|
||||
processAlivePointers(pointer -> {
|
||||
pointer.getElementInfo().fastenBelt();
|
||||
return true;
|
||||
@@ -229,19 +240,30 @@ class SmartPointerTracker {
|
||||
}
|
||||
|
||||
static class PointerReference extends WeakReference<SmartPsiElementPointerImpl> {
|
||||
@NotNull final VirtualFile file;
|
||||
@NotNull final Map<VirtualFile, SmartPointerTracker> trackerMap;
|
||||
@NotNull private final VirtualFile file;
|
||||
@NotNull private final Key<SmartPointerTracker> key;
|
||||
private int index = -2;
|
||||
|
||||
PointerReference(@NotNull SmartPsiElementPointerImpl<?> pointer,
|
||||
@NotNull VirtualFile containingFile,
|
||||
@NotNull Map<VirtualFile, SmartPointerTracker> trackerMap,
|
||||
@NotNull ReferenceQueue<SmartPsiElementPointerImpl> queue) {
|
||||
super(pointer, queue);
|
||||
@NotNull Key<SmartPointerTracker> key) {
|
||||
super(pointer, ourQueue);
|
||||
file = containingFile;
|
||||
this.trackerMap = trackerMap;
|
||||
this.key = key;
|
||||
pointer.pointerReference = this;
|
||||
}
|
||||
}
|
||||
|
||||
static void processQueue() {
|
||||
while (true) {
|
||||
PointerReference reference = (PointerReference)ourQueue.poll();
|
||||
if (reference == null) break;
|
||||
|
||||
SmartPointerTracker pointers = reference.file.getUserData(reference.key);
|
||||
if (pointers != null) {
|
||||
pointers.removeReference(reference, reference.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user