Fixed thread leak in multiprocess debugger.
authorDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Mon, 18 Feb 2013 16:31:08 +0000 (17:31 +0100)
committerDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Mon, 18 Feb 2013 16:31:08 +0000 (17:31 +0100)
platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java

index 7ea3e4882cf1fb5ec75e0bfc57cd3df2855d37ca..d5ae6b40b3241085019ac379028d8e5d703f9db0 100644 (file)
@@ -65,7 +65,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
   private volatile long myModificationStamp;
   private final PropertyChangeSupport myPropertyChangeSupport = new PropertyChangeSupport(this);
 
-  private DocumentListener[] myCachedDocumentListeners;
+  private final Ref<DocumentListener[]> myCachedDocumentListeners = Ref.create(null);
   private final List<EditReadOnlyListener> myReadOnlyListeners = ContainerUtil.createLockFreeCopyOnWriteList();
 
   private int myCheckGuardedBlocks = 0;
@@ -647,7 +647,10 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
 
   @Override
   public void addDocumentListener(@NotNull DocumentListener listener) {
-    myCachedDocumentListeners = null;
+    if (myCachedDocumentListeners != null) {
+      myCachedDocumentListeners.set(null);
+    }
+
     LOG.assertTrue(!myDocumentListeners.contains(listener), "Already registered: " + listener);
     boolean added = myDocumentListeners.add(listener);
     LOG.assertTrue(added, listener);
@@ -664,12 +667,38 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
     });
   }
 
+  private static class DocumentListenerDisposable implements Disposable {
+
+    private DocumentListener myListener;
+    private Ref<DocumentListener[]> myCachedDocumentListenersRef;
+    private List<DocumentListener> myDocumentListeners;
+
+    public DocumentListenerDisposable(DocumentListener listener, Ref<DocumentListener[]> cachedDocumentListenersRef, List<DocumentListener> documentListeners) {
+      myListener = listener;
+      myCachedDocumentListenersRef = cachedDocumentListenersRef;
+      myDocumentListeners = documentListeners;
+    }
+
+    @Override
+    public void dispose() {
+      doRemoveDocumentListener(myListener, myCachedDocumentListenersRef, myDocumentListeners);
+    }
+  }
+
   @Override
   public void removeDocumentListener(@NotNull DocumentListener listener) {
-    myCachedDocumentListeners = null;
-    boolean success = myDocumentListeners.remove(listener);
+    doRemoveDocumentListener(listener, myCachedDocumentListeners, myDocumentListeners);
+  }
+
+  private static void doRemoveDocumentListener(DocumentListener listener,
+                                               Ref<DocumentListener[]> cachedDocumentListenersRef,
+                                               List<DocumentListener> documentListeners) {
+    if (cachedDocumentListenersRef != null) {
+      cachedDocumentListenersRef.set(null);
+    }
+    boolean success = documentListeners.remove(listener);
     if (!success) {
-      LOG.error("Can't remove document listener (" + listener + "). Registered listeners: " + myDocumentListeners);
+      LOG.error("Can't remove document listener (" + listener + "). Registered cachedDocumentListenersRef: " + documentListeners);
     }
   }
 
@@ -714,11 +743,12 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
 
   @NotNull
   private DocumentListener[] getCachedListeners() {
-    DocumentListener[] cachedListeners = myCachedDocumentListeners;
+    DocumentListener[] cachedListeners = myCachedDocumentListeners.get();
     if (cachedListeners == null) {
       DocumentListener[] listeners = myDocumentListeners.toArray(new DocumentListener[myDocumentListeners.size()]);
       Arrays.sort(listeners, PrioritizedDocumentListener.COMPARATOR);
-      myCachedDocumentListeners = cachedListeners = listeners;
+      cachedListeners = listeners;
+      myCachedDocumentListeners.set(cachedListeners);
     }
 
     return cachedListeners;