IDEA-51285 (Local Changes Update should not prevent IDEA exit)
authorirengrig <Irina.Chernushina@jetbrains.com>
Wed, 24 Feb 2010 19:51:18 +0000 (22:51 +0300)
committerirengrig <Irina.Chernushina@jetbrains.com>
Wed, 24 Feb 2010 19:51:18 +0000 (22:51 +0300)
platform/vcs-impl/src/com/intellij/lifecycle/PeriodicalTasksCloser.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ExecutorWrapper.java [new file with mode: 0644]
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/RemoteRevisionsStateCache.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/UpdateRequestsQueue.java

index 61777c564b4bd8cafc939f83bb0db929ea7179e0..35595a07c0529888a66783985de7c68d1bc58eeb 100644 (file)
@@ -64,7 +64,7 @@ public class PeriodicalTasksCloser implements ProjectManagerListener {
       myInterrupters.add(new Pair<String, Runnable>(name, runnable));
       return true;
     }
-  }       
+  }
 
   public void projectOpened(Project project) {
     synchronized (ourLock) {
index bc751b107d20c367ff282df239f3219035b163f8..8dd23e8abf91a3055955e62037d87945448b0d67 100644 (file)
@@ -17,12 +17,6 @@ package com.intellij.openapi.vcs.changes;
 
 import com.intellij.ide.highlighter.WorkspaceFileType;
 import com.intellij.lifecycle.AtomicSectionsAware;
-import com.intellij.lifecycle.ControlledAlarmFactory;
-import com.intellij.lifecycle.SlowlyClosingAlarm;
-import com.intellij.notification.Notification;
-import com.intellij.notification.NotificationDisplayType;
-import com.intellij.notification.NotificationType;
-import com.intellij.notification.Notifications;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.components.ProjectComponent;
@@ -249,7 +243,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
     final VcsInvalidated invalidated = dirtyScopeManager.retrieveScopes();
     if (invalidated == null || invalidated.isEmpty()) {
       // a hack here; but otherwise everything here should be refactored ;)
-      if (invalidated.isEmpty() && invalidated.isEverythingDirty()) {
+      if (invalidated != null && invalidated.isEmpty() && invalidated.isEverythingDirty()) {
         VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty();
       }
       return;
@@ -976,27 +970,41 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
   }
 
   private static class MyChangesDeltaForwarder implements PlusMinus<Pair<String, AbstractVcs>> {
-    private SlowlyClosingAlarm myAlarm;
+    //private SlowlyClosingAlarm myAlarm;
     private RemoteRevisionsCache myRevisionsCache;
     private final ProjectLevelVcsManager myVcsManager;
+    private ExecutorWrapper myExecutorWrapper;
+    private final ExecutorService myService;
+
 
     public MyChangesDeltaForwarder(final Project project, final ExecutorService service) {
-      myAlarm = ControlledAlarmFactory.createOnSharedThread(project, "changes delta consumer forwarder", service);
+      myService = service;
+      //myAlarm = ControlledAlarmFactory.createOnSharedThread(project, UpdateRequestsQueue.LOCAL_CHANGES_UPDATE, service);
+      myExecutorWrapper = new ExecutorWrapper(project, UpdateRequestsQueue.LOCAL_CHANGES_UPDATE);
       myRevisionsCache = RemoteRevisionsCache.getInstance(project);
       myVcsManager = ProjectLevelVcsManager.getInstance(project);
     }
 
     public void plus(final Pair<String, AbstractVcs> stringAbstractVcsPair) {
-      myAlarm.addRequest(new Runnable() {
+      myService.submit(new Runnable() {
         public void run() {
-          myRevisionsCache.plus(getCorrectedPair(stringAbstractVcsPair));
+          myExecutorWrapper.submit(new Consumer<AtomicSectionsAware>() {
+            public void consume(AtomicSectionsAware atomicSectionsAware) {
+              myRevisionsCache.plus(getCorrectedPair(stringAbstractVcsPair));
+            }
+          });
         }
       });
     }
 
     public void minus(final Pair<String, AbstractVcs> stringAbstractVcsPair) {
-      myAlarm.addRequest(new Runnable() {
+      myService.submit(new Runnable() {
         public void run() {
+          myExecutorWrapper.submit(new Consumer<AtomicSectionsAware>() {
+            public void consume(AtomicSectionsAware atomicSectionsAware) {
+              myRevisionsCache.minus(getCorrectedPair(stringAbstractVcsPair));
+            }
+          });
           myRevisionsCache.minus(getCorrectedPair(stringAbstractVcsPair));
         }
       });
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ExecutorWrapper.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ExecutorWrapper.java
new file mode 100644 (file)
index 0000000..ceb7567
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2000-2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.vcs.changes;
+
+import com.intellij.lifecycle.AtomicSectionsAware;
+import com.intellij.lifecycle.PeriodicalTasksCloser;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.Consumer;
+import com.intellij.util.concurrency.Semaphore;
+
+/**
+ * for restarted single threaded executor
+ */
+public class ExecutorWrapper {
+  private final Project myProject;
+  private final String myName;
+  private final Semaphore mySemaphore;
+  private volatile boolean myDisposeStarted;
+  private final Object myLock;
+  private boolean myInProgress;
+  private Runnable myStopper;
+  private AtomicSectionsAware myAtomicSectionsAware;
+
+  protected ExecutorWrapper(final Project project, final String name) {
+    myProject = project;
+    myName = name;
+    myLock = new Object();
+    mySemaphore = new Semaphore();
+
+    myStopper = new Runnable() {
+      public void run() {
+        synchronized (myLock) {
+          myDisposeStarted = true;
+          taskFinished();
+        }
+      }
+    };
+
+    myAtomicSectionsAware = new AtomicSectionsAware() {
+      public void checkShouldExit() throws ProcessCanceledException {
+        if (myDisposeStarted) {
+          throw new ProcessCanceledException();
+        }
+      }
+      public void enter() {
+        // we won't kill the thread, so we can ignore information
+      }
+      public void exit() {
+        //the same
+      }
+      public boolean shouldExitAsap() {
+        return myDisposeStarted;
+      }
+    };
+    myDisposeStarted = ! PeriodicalTasksCloser.getInstance(myProject).register(myName, myStopper);
+  }
+
+  private void taskFinished() {
+    synchronized (myLock) {
+      if (myInProgress) {
+        myInProgress = false;
+        mySemaphore.up();
+      }
+    }
+  }
+
+  /**
+   * executed on separate thread, that can be interrupted immediately, while "the main thread" gets control back
+   * and can continue to serve other requests
+   */
+  public void submit(final Consumer<AtomicSectionsAware> runnable) {
+    try {
+      synchronized (myLock) {
+        if (myDisposeStarted) return;
+        assert ! myInProgress;
+        myInProgress = true;
+        mySemaphore.down();
+      }
+      ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
+        public void run() {
+          try {
+            runnable.consume(myAtomicSectionsAware);
+          } finally {
+            taskFinished();
+          }
+        }
+      });
+      mySemaphore.waitFor();
+    } finally {
+      taskFinished();
+    }
+  }
+}
index b920a082832f63561a0dbe5c105b792365846688..9b10f6255383136b3158cd89e8685cdfd8550488 100644 (file)
@@ -83,7 +83,10 @@ public class RemoteRevisionsStateCache implements ChangesOnServerTracker {
     final VirtualFile root = getRootForPath(pair.getFirst());
     if (root == null) return;
     synchronized (myLock) {
-      myQueries.removeValue(new VcsRoot(pair.getSecond(), root), pair.getFirst());
+      final VcsRoot key = new VcsRoot(pair.getSecond(), root);
+      if (myQueries.containsKey(key)) {
+        myQueries.removeValue(key, pair.getFirst());
+      }
       myChanged.remove(pair.getFirst());
     }
   }
index 29cbfd9429106085a8da7c389ab0177f7a987595..817253f3635b091a3649de0bb24597b643729747 100644 (file)
@@ -16,8 +16,7 @@
 package com.intellij.openapi.vcs.changes;
 
 import com.intellij.ide.startup.impl.StartupManagerImpl;
-import com.intellij.lifecycle.ControlledAlarmFactory;
-import com.intellij.lifecycle.ScheduledSlowlyClosingAlarm;
+import com.intellij.lifecycle.AtomicSectionsAware;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.diagnostic.Logger;
@@ -26,11 +25,13 @@ import com.intellij.openapi.project.Project;
 import com.intellij.openapi.startup.StartupManager;
 import com.intellij.openapi.vcs.ProjectLevelVcsManager;
 import com.intellij.util.Consumer;
+import org.jetbrains.annotations.NonNls;
 
 import javax.swing.*;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 /**
  * ChangeListManager updates scheduler.
@@ -41,6 +42,7 @@ import java.util.concurrent.ScheduledExecutorService;
 public class UpdateRequestsQueue {
   private final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.UpdateRequestsQueue");
   private final Project myProject;
+  private final ScheduledExecutorService myExecutor;
   private final LocalChangesUpdater myDelegate;
   private final Object myLock;
   private volatile boolean myStarted;
@@ -50,14 +52,19 @@ public class UpdateRequestsQueue {
   private final List<Runnable> myWaitingUpdateCompletionQueue;
   private final ProjectLevelVcsManager myPlVcsManager;
   private boolean myUpdateUnversionedRequested;
-  private final ScheduledSlowlyClosingAlarm mySharedExecutor;
+  //private final ScheduledSlowlyClosingAlarm mySharedExecutor;
   private final StartupManager myStartupManager;
+  private final ExecutorWrapper myExecutorWrapper;
+  @NonNls public static final String LOCAL_CHANGES_UPDATE = "Local changes update";
 
   public UpdateRequestsQueue(final Project project, final ScheduledExecutorService executor, final LocalChangesUpdater delegate) {
-    mySharedExecutor = ControlledAlarmFactory.createScheduledOnSharedThread(project, "Local changes update", executor);
+    myProject = project;
+    myExecutor = executor;
+
+    //mySharedExecutor = ControlledAlarmFactory.createScheduledOnSharedThread(project, LOCAL_CHANGES_UPDATE, executor);
+    myExecutorWrapper = new ExecutorWrapper(myProject, LOCAL_CHANGES_UPDATE);
 
     myDelegate = delegate;
-    myProject = project;
     myPlVcsManager = ProjectLevelVcsManager.getInstance(myProject);
     myStartupManager = StartupManager.getInstance(myProject);
     myLock = new Object();
@@ -85,7 +92,7 @@ public class UpdateRequestsQueue {
         if (! myRequestSubmitted) {
           final MyRunnable runnable = new MyRunnable();
           myRequestSubmitted = true;
-          mySharedExecutor.addRequest(runnable, 300);
+          myExecutor.schedule(runnable, 300, TimeUnit.MILLISECONDS);
           LOG.debug("Scheduled for project: " + myProject.getName() + ", runnable: " + runnable.hashCode());
           myUpdateUnversionedRequested |= updateUnversionedFiles;
         } else if (updateUnversionedFiles && (! myUpdateUnversionedRequested)) {
@@ -152,7 +159,7 @@ public class UpdateRequestsQueue {
 
   private class MyRunnable implements Runnable {
     public void run() {
-      boolean updateUnversioned;
+      final boolean updateUnversioned;
       final List<Runnable> copy = new ArrayList<Runnable>(myWaitingUpdateCompletionQueue.size());
 
       try {
@@ -181,7 +188,11 @@ public class UpdateRequestsQueue {
         }
 
         LOG.debug("MyRunnable: INVOKE, project: " + myProject.getName() + ", runnable: " + hashCode());
-        myDelegate.execute(updateUnversioned, mySharedExecutor);
+        myExecutorWrapper.submit(new Consumer<AtomicSectionsAware>() {
+          public void consume(AtomicSectionsAware atomicSectionsAware) {
+            myDelegate.execute(updateUnversioned, atomicSectionsAware);
+          }
+        });
         LOG.debug("MyRunnable: invokeD, project: " + myProject.getName() + ", runnable: " + hashCode());
       } finally {
         synchronized (myLock) {