jsr-299 libs and facets
[idea/community.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / changes / UpdateRequestsQueue.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.vcs.changes;
17
18 import com.intellij.ide.startup.impl.StartupManagerImpl;
19 import com.intellij.lifecycle.ControlledAlarmFactory;
20 import com.intellij.lifecycle.ScheduledSlowlyClosingAlarm;
21 import com.intellij.openapi.application.ApplicationManager;
22 import com.intellij.openapi.application.ModalityState;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.startup.StartupManager;
26 import com.intellij.openapi.vcs.ProjectLevelVcsManager;
27 import com.intellij.util.Consumer;
28
29 import javax.swing.*;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.concurrent.ScheduledExecutorService;
33
34 /**
35  * ChangeListManager updates scheduler.
36  * Tries to zip several update requests into one (if starts and see several requests in the queue)
37  * own inner synchronization
38  */
39 public class UpdateRequestsQueue {
40   private final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.UpdateRequestsQueue");
41   private final Project myProject;
42   private final LocalChangesUpdater myDelegate;
43   private final Object myLock;
44   private volatile boolean myStarted;
45   private volatile boolean myStopped;
46
47   private boolean myRequestSubmitted;
48   private final List<Runnable> myWaitingUpdateCompletionQueue;
49   private final ProjectLevelVcsManager myPlVcsManager;
50   private boolean myUpdateUnversionedRequested;
51   private final ScheduledSlowlyClosingAlarm mySharedExecutor;
52   private final StartupManager myStartupManager;
53
54   public UpdateRequestsQueue(final Project project, final ScheduledExecutorService executor, final LocalChangesUpdater delegate) {
55     mySharedExecutor = ControlledAlarmFactory.createScheduledOnSharedThread(project, "Local changes update", executor);
56
57     myDelegate = delegate;
58     myProject = project;
59     myPlVcsManager = ProjectLevelVcsManager.getInstance(myProject);
60     myStartupManager = StartupManager.getInstance(myProject);
61     myLock = new Object();
62     myWaitingUpdateCompletionQueue = new ArrayList<Runnable>();
63     // not initialized
64     myStarted = false;
65     myStopped = false;
66     myUpdateUnversionedRequested = false;
67   }
68
69   public void initialized() {
70     LOG.debug("Initialized for project: " + myProject.getName());
71     myStarted = true;
72   }
73
74   public boolean isStopped() {
75     return myStopped;
76   }
77
78   public void schedule(final boolean updateUnversionedFiles) {
79     synchronized (myLock) {
80       if (! myStarted && ApplicationManager.getApplication().isUnitTestMode()) return;
81
82       if (! myStopped) {
83         if (! myRequestSubmitted) {
84           final MyRunnable runnable = new MyRunnable();
85           myRequestSubmitted = true;
86           mySharedExecutor.addRequest(runnable, 300);
87           LOG.debug("Scheduled for project: " + myProject.getName() + ", runnable: " + runnable.hashCode());
88           myUpdateUnversionedRequested |= updateUnversionedFiles;
89         } else if (updateUnversionedFiles && (! myUpdateUnversionedRequested)) {
90           myUpdateUnversionedRequested = true;
91         }
92       }
93     }
94   }
95
96   public void stop() {
97     LOG.debug("Calling stop for project: " + myProject.getName());
98     final List<Runnable> waiters = new ArrayList<Runnable>(myWaitingUpdateCompletionQueue.size());
99     synchronized (myLock) {
100       myStopped = true;
101       waiters.addAll(myWaitingUpdateCompletionQueue);
102       myWaitingUpdateCompletionQueue.clear();
103     }
104     LOG.debug("Calling runnables in stop for project: " + myProject.getName());
105     // do not run under lock
106     for (Runnable runnable : waiters) {
107       runnable.run();
108     }
109     LOG.debug("Stop finished for project: " + myProject.getName());
110   }
111
112   public void invokeAfterUpdate(final Runnable afterUpdate, final InvokeAfterUpdateMode mode, final String title,
113                                 final Consumer<VcsDirtyScopeManager> dirtyScopeManagerFiller, final ModalityState state) {
114     LOG.debug("invokeAfterUpdate for project: " + myProject.getName());
115     final CallbackData data = CallbackData.create(afterUpdate, title, state, mode, myProject);
116
117     VcsDirtyScopeManagerProxy managerProxy = null;
118     if (dirtyScopeManagerFiller != null) {
119       managerProxy  = new VcsDirtyScopeManagerProxy();
120       dirtyScopeManagerFiller.consume(managerProxy);
121     }
122
123     synchronized (myLock) {
124       if (! myStopped) {
125         if (managerProxy != null) {
126           managerProxy.callRealManager(VcsDirtyScopeManager.getInstance(myProject));
127         }
128         
129         myWaitingUpdateCompletionQueue.add(data.getCallback());
130         schedule(true);
131       }
132     }
133     // do not run under lock; stopped cannot be switched into not stopped - can check without lock
134     if (myStopped) {
135       LOG.debug("invokeAfterUpdate: stopped, invoke right now for project: " + myProject.getName());
136       SwingUtilities.invokeLater(new Runnable() {
137         public void run() {
138           afterUpdate.run();
139         }
140       });
141       return;
142     } else {
143       // invoke progress if needed
144       if (data.getWrapperStarter() != null) {
145         data.getWrapperStarter().run();
146       }
147     }
148     LOG.debug("invokeAfterUpdate: exit for project: " + myProject.getName());
149   }
150
151   private class MyRunnable implements Runnable {
152     public void run() {
153       boolean updateUnversioned;
154       final List<Runnable> copy = new ArrayList<Runnable>(myWaitingUpdateCompletionQueue.size());
155
156       try {
157         synchronized (myLock) {
158           if ((! myStopped) && ((! myStarted) || myPlVcsManager.isBackgroundVcsOperationRunning()) ||
159             (! ((StartupManagerImpl) myStartupManager).startupActivityPassed())) {
160             LOG.debug("MyRunnable: not started, not stopped, reschedule, project: " + myProject.getName() + ", runnable: " + hashCode());
161             myRequestSubmitted = false;
162             // try again after time
163             schedule(myUpdateUnversionedRequested);
164             return;
165           }
166
167           copy.addAll(myWaitingUpdateCompletionQueue);
168           myRequestSubmitted = false;
169
170           if (myStopped) {
171             LOG.debug("MyRunnable: STOPPED, project: " + myProject.getName() + ", runnable: " + hashCode());
172             return;
173           }
174
175           // take it under lock
176           updateUnversioned = myUpdateUnversionedRequested;
177           // for concurrent schedules to tigger flag correctly
178           myUpdateUnversionedRequested = false;
179         }
180
181         LOG.debug("MyRunnable: INVOKE, project: " + myProject.getName() + ", runnable: " + hashCode());
182         myDelegate.execute(updateUnversioned, mySharedExecutor);
183         LOG.debug("MyRunnable: invokeD, project: " + myProject.getName() + ", runnable: " + hashCode());
184       } finally {
185         synchronized (myLock) {
186           LOG.debug("MyRunnable: delete executed, project: " + myProject.getName() + ", runnable: " + hashCode());
187           if (! copy.isEmpty()) {
188             myWaitingUpdateCompletionQueue.removeAll(copy);
189           }
190
191           if ((! myWaitingUpdateCompletionQueue.isEmpty()) && (! myRequestSubmitted)) {
192             LOG.error("No update task to handle request(s)");
193           }
194         }
195         // do not run under lock
196         for (Runnable runnable : copy) {
197           runnable.run();
198         }
199         LOG.debug("MyRunnable: Runnables executed, project: " + myProject.getName() + ", runnable: " + hashCode());
200       }
201     }
202   }
203 }