Single mirror lock replaced with per-file ones
authorRoman Shevchenko <roman.shevchenko@jetbrains.com>
Mon, 20 May 2013 08:48:31 +0000 (10:48 +0200)
committerRoman Shevchenko <roman.shevchenko@jetbrains.com>
Mon, 20 May 2013 08:48:31 +0000 (10:48 +0200)
java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsFileImpl.java
platform/util-rt/src/com/intellij/reference/SoftReference.java

index 1920043b2fe483c168c9b11390c2e0acecd8ea2f..b5b1077087681c3f7643b2d55293a422ddfca042 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * Copyright 2000-2013 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,7 +31,10 @@ import com.intellij.openapi.ui.Queryable;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.pom.java.LanguageLevel;
 import com.intellij.psi.*;
-import com.intellij.psi.impl.*;
+import com.intellij.psi.impl.JavaPsiImplementationHelper;
+import com.intellij.psi.impl.PsiFileEx;
+import com.intellij.psi.impl.PsiManagerEx;
+import com.intellij.psi.impl.PsiManagerImpl;
 import com.intellij.psi.impl.java.stubs.PsiClassStub;
 import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl;
 import com.intellij.psi.impl.source.PsiFileImpl;
@@ -42,22 +45,26 @@ import com.intellij.psi.impl.source.tree.JavaElementType;
 import com.intellij.psi.impl.source.tree.TreeElement;
 import com.intellij.psi.search.PsiElementProcessor;
 import com.intellij.psi.stubs.*;
+import com.intellij.reference.SoftReference;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.IncorrectOperationException;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
-import java.lang.ref.SoftReference;
-import java.util.*;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
-public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub> implements PsiJavaFile, PsiFileWithStubSupport, PsiFileEx,
-                                                                                            Queryable, PsiClassOwnerEx, PsiCompiledFile {
+import static com.intellij.reference.SoftReference.dereference;
+
+public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
+                         implements PsiJavaFile, PsiFileWithStubSupport, PsiFileEx, Queryable, PsiClassOwnerEx, PsiCompiledFile {
   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.compiled.ClsFileImpl");
 
-  /** YOU absolutely MUST NOT hold PsiLock under the MIRROR_LOCK */
-  private static final MirrorLock MIRROR_LOCK = new MirrorLock();
-  private static class MirrorLock {}
+  /** NOTE: you absolutely MUST NOT hold PsiLock under the mirror lock */
+  private final Object myMirrorLock = new Object();
+  private final Object myStubLock = new Object();
 
   private final PsiManagerImpl myManager;
   private final boolean myIsForDecompiling;
@@ -73,7 +80,7 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
     myManager = manager;
     myIsForDecompiling = forDecompiling;
     myViewProvider = viewProvider;
-    JavaElementType.CLASS.getIndex(); // Initialize java stubs...
+    JavaElementType.CLASS.getIndex();  // initialize Java stubs
   }
 
   public ClsFileImpl(PsiManagerImpl manager, FileViewProvider viewProvider) {
@@ -270,43 +277,38 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
 
   @Override
   public PsiElement getMirror() {
-    String mirrorText = null;
-    VirtualFile file = null;
-    if (myMirrorFileElement == null) {
-      file = getVirtualFile();
-      // avoid decompiling under MIRROR_LOCK as decompiling can need other locks
-      // (e.g. nice parameter names in mirror need resolve and FQN stub index lock) and stub indices (under own lock) need to access stub
-      // tree with MIRROR_LOCK, see IDEA-98468
-      mirrorText = decompile(getManager(), file);
-    }
-
-    synchronized (MIRROR_LOCK) {
-      if (myMirrorFileElement == null) {
-        String ext = JavaFileType.INSTANCE.getDefaultExtension();
-        PsiClass[] classes = getClasses();
-        String fileName = (classes.length > 0 ? classes[0].getName() : file.getNameWithoutExtension()) + "." + ext;
-        PsiFileFactory factory = PsiFileFactory.getInstance(getManager().getProject());
-        PsiFile mirror = factory.createFileFromText(fileName, JavaLanguage.INSTANCE, mirrorText, false, false);
-        TreeElement mirrorTreeElement = SourceTreeToPsiMap.psiToTreeNotNull(mirror);
-
-        //IMPORTANT: do not take lock too early - FileDocumentManager.getInstance().saveToString() can run write action...
-        final NonCancelableSection section = ProgressIndicatorProvider.startNonCancelableSectionIfSupported();
-        try {
-          setMirror(mirrorTreeElement);
-        }
-        catch (InvalidMirrorException e) {
-          // todo[r.sh] use logging API once available (to attach .class file)
-          LOG.error(file.getPath(), e);
+    TreeElement mirrorTreeElement = myMirrorFileElement;
+    if (mirrorTreeElement == null) {
+      synchronized (myMirrorLock) {
+        mirrorTreeElement = myMirrorFileElement;
+        if (mirrorTreeElement == null) {
+          VirtualFile file = getVirtualFile();
+          String mirrorText = decompile(getManager(), file);
+
+          String ext = JavaFileType.INSTANCE.getDefaultExtension();
+          PsiClass[] classes = getClasses();
+          String fileName = (classes.length > 0 ? classes[0].getName() : file.getNameWithoutExtension()) + "." + ext;
+          PsiFileFactory factory = PsiFileFactory.getInstance(getManager().getProject());
+          PsiFile mirror = factory.createFileFromText(fileName, JavaLanguage.INSTANCE, mirrorText, false, false);
+          mirrorTreeElement = SourceTreeToPsiMap.psiToTreeNotNull(mirror);
+
+          // IMPORTANT: do not take lock too early - FileDocumentManager.saveToString() can run write action
+          NonCancelableSection section = ProgressIndicatorProvider.startNonCancelableSectionIfSupported();
+          try {
+            setMirror(mirrorTreeElement);
+          }
+          catch (InvalidMirrorException e) {
+            LOG.error(file.getPath(), e);
+          }
+          finally {
+            section.done();
+          }
+
+          myMirrorFileElement = mirrorTreeElement;
         }
-        finally {
-          section.done();
-        }
-
-        myMirrorFileElement = mirrorTreeElement;
       }
-
-      return myMirrorFileElement.getPsi();
     }
+    return mirrorTreeElement.getPsi();
   }
 
   @Override
@@ -398,58 +400,30 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
     return (PsiClassHolderFileStub)getStubTree().getRoot();
   }
 
-  private final Object lock = new Object();
-
   @Override
   @NotNull
   public StubTree getStubTree() {
     ApplicationManager.getApplication().assertReadAccessAllowed();
 
-    final StubTree derefd = derefStub();
-    if (derefd != null) return derefd;
-
-    StubTree stubHolder = (StubTree)StubTreeLoader.getInstance().readOrBuild(getProject(), getVirtualFile(), this);
-    if (stubHolder == null) {
-      // Must be corrupted .class file
-      LOG.info("Class file is corrupted: " + getVirtualFile().getPresentableUrl());
-
-      StubTree emptyTree = new StubTree(new PsiJavaFileStubImpl("corrupted.classfiles", true));
-      setStubTree(emptyTree);
-      resetMirror();
-      return emptyTree;
-    }
-
-    synchronized (lock) {
-      final StubTree derefdOnLock = derefStub();
-      if (derefdOnLock != null) return derefdOnLock;
-
-      setStubTree(stubHolder);
-    }
+    StubTree stubTree = dereference(myStub);
+    if (stubTree != null) return stubTree;
 
-    resetMirror();
-    return stubHolder;
-  }
+    synchronized (myStubLock) {
+      stubTree = dereference(myStub);
+      if (stubTree != null) return stubTree;
 
-  private void setStubTree(StubTree tree) {
-    synchronized (lock) {
-      myStub = new SoftReference<StubTree>(tree);
-      ((PsiFileStubImpl)tree.getRoot()).setPsi(this);
-    }
-  }
+      stubTree = (StubTree)StubTreeLoader.getInstance().readOrBuild(getProject(), getVirtualFile(), this);
+      if (stubTree == null) {
+        LOG.warn("Class file is corrupted: " + getVirtualFile().getPresentableUrl());
+        stubTree = new StubTree(new PsiJavaFileStubImpl("corrupted.classfiles", true));
+      }
 
-  private void resetMirror() {
-    ClsPackageStatementImpl clsPackageStatement = new ClsPackageStatementImpl(this);
-    synchronized (MIRROR_LOCK) {
-      myMirrorFileElement = null;
-      myPackageStatement = clsPackageStatement;
+      myStub = new SoftReference<StubTree>(stubTree);
+      //noinspection unchecked
+      ((PsiFileStubImpl)stubTree.getRoot()).setPsi(this);
     }
-  }
 
-  @Nullable
-  private StubTree derefStub() {
-    synchronized (lock) {
-      return myStub != null ? myStub.get() : null;
-    }
+    return stubTree;
   }
 
   @Override
@@ -464,18 +438,21 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
 
   @Override
   public void onContentReload() {
-    SoftReference<StubTree> stub = myStub;
-    StubTree stubHolder = stub == null ? null : stub.get();
-    if (stubHolder != null) {
-      ((StubBase<?>)stubHolder.getRoot()).setPsi(null);
-    }
-    myStub = null;
-
     ApplicationManager.getApplication().assertWriteAccessAllowed();
 
-    synchronized (MIRROR_LOCK) {
+    synchronized (myStubLock) {
+      StubTree stubTree = dereference(myStub);
+      myStub = null;
+      if (stubTree != null) {
+        //noinspection unchecked
+        ((PsiFileStubImpl)stubTree.getRoot()).setPsi(null);
+      }
+    }
+
+    ClsPackageStatementImpl packageStatement = new ClsPackageStatementImpl(this);
+    synchronized (myMirrorLock) {
       myMirrorFileElement = null;
-      myPackageStatement = null;
+      myPackageStatement = packageStatement;
     }
   }
 
index ce74a7499b6c131ab8d2404d7471cc8d3be6c011..f28f94834690d1ac9e65031234ab19e9f1919aec 100644 (file)
@@ -15,6 +15,8 @@
  */
 package com.intellij.reference;
 
+import org.jetbrains.annotations.Nullable;
+
 import java.lang.ref.ReferenceQueue;
 
 /**
@@ -42,4 +44,9 @@ public class SoftReference<T> extends java.lang.ref.SoftReference<T> {
   //public T get() {
   //  return myReferent;
   //}
+
+  @Nullable
+  public static <T> T dereference(@Nullable java.lang.ref.SoftReference<T> ref) {
+    return ref != null ? ref.get() : null;
+  }
 }