PHP reuse nice existing implementation of PsiElement per thread locks for type resolution
authorAlexey Gopachenko <alexey.gopachenko@jetbrains.com>
Thu, 26 Nov 2009 11:52:32 +0000 (14:52 +0300)
committerAlexey Gopachenko <alexey.gopachenko@jetbrains.com>
Thu, 26 Nov 2009 12:47:49 +0000 (15:47 +0300)
platform/lang-impl/src/com/intellij/psi/impl/source/resolve/ResolveCache.java

index 199678c6a676650838d3b0eaeea1d432cfe2a817..c0f4485ef8dcbb7883069ce75aa13cbe24068c7a 100644 (file)
@@ -152,16 +152,26 @@ public class ResolveCache {
   }
 
   private static final Key<Thread[]> IS_BEING_RESOLVED_KEY = Key.create("ResolveCache.IS_BEING_RESOLVED_KEY");
+
   private static boolean lockElement(PsiReference ref) {
-    PsiElement element = ref.getElement();
+    return lockElement(ref.getElement(), IS_BEING_RESOLVED_KEY);
+  }
 
+  /**
+   * Implementation of per thread lock to prevent element-analysing recursive algorithms from infinite looping.
+   * @see #unlockElement
+   * @param element
+   * @param key
+   * @return lock status
+   */
+  public static boolean lockElement(PsiElement element, Key<Thread[]> key) {
     final Thread currentThread = Thread.currentThread();
     while (true) {
-      Thread[] lockingThreads = element.getUserData(IS_BEING_RESOLVED_KEY);
+      Thread[] lockingThreads = element.getUserData(key);
       Thread[] newThreads;
       if (lockingThreads == null) {
         newThreads = new Thread[]{currentThread};
-        if (((UserDataHolderEx)element).putUserDataIfAbsent(IS_BEING_RESOLVED_KEY, newThreads) == newThreads) {
+        if (((UserDataHolderEx)element).putUserDataIfAbsent(key, newThreads) == newThreads) {
           break;
         }
       }
@@ -170,12 +180,12 @@ public class ResolveCache {
           return false;
         }
         newThreads = ArrayUtil.append(lockingThreads, currentThread);
-        if (((UserDataHolderEx)element).replace(IS_BEING_RESOLVED_KEY, lockingThreads, newThreads)) {
+        if (((UserDataHolderEx)element).replace(key, lockingThreads, newThreads)) {
           break;
         }
       }
     }
-    Thread[] data = element.getUserData(IS_BEING_RESOLVED_KEY);
+    Thread[] data = element.getUserData(key);
     int i = ArrayUtil.find(data, currentThread);
     assert i != -1;
     assert i == ArrayUtil.lastIndexOf(data, currentThread);
@@ -184,10 +194,18 @@ public class ResolveCache {
   }
 
   private static void unlockElement(PsiReference ref) {
-    PsiElement element = ref.getElement();
+    unlockElement(ref.getElement(), IS_BEING_RESOLVED_KEY);
+  }
+
+  /**
+   * @see #lockElement
+   * @param element
+   * @param key
+   */
+  public static void unlockElement(PsiElement element , Key<Thread[]> key) {
     final Thread currentThread = Thread.currentThread();
     while (true) {
-      Thread[] lockingThreads = element.getUserData(IS_BEING_RESOLVED_KEY);
+      Thread[] lockingThreads = element.getUserData(key);
       Thread[] newThreads;
       if (lockingThreads.length == 1) {
         assert lockingThreads[0] == currentThread : "Locking thread = " + lockingThreads[0] + "; current=" + currentThread;
@@ -202,11 +220,11 @@ public class ResolveCache {
         assert newThreads.length == lockingThreads.length - 1 : "Locking threads = " + Arrays.asList(lockingThreads) + "; newThreads=" + Arrays.asList(newThreads);
         assert ArrayUtil.find(newThreads, currentThread) == -1;
       }
-      if (((UserDataHolderEx)element).replace(IS_BEING_RESOLVED_KEY, lockingThreads, newThreads)) {
+      if (((UserDataHolderEx)element).replace(key, lockingThreads, newThreads)) {
         break;
       }
     }
-    Thread[] data = element.getUserData(IS_BEING_RESOLVED_KEY);
+    Thread[] data = element.getUserData(key);
     assert data == null || ArrayUtil.find(data, currentThread) == -1;
   }