VCS: file changed remotely hint
[fedora-idea.git] / vcs-impl / src / com / intellij / lifecycle / SlowlyClosingAlarm.java
blob21f6030c0cea19ca880002baf28fd3275e9073ba
1 package com.intellij.lifecycle;
3 import com.intellij.openapi.Disposable;
4 import com.intellij.openapi.diagnostic.Logger;
5 import com.intellij.openapi.progress.ProcessCanceledException;
6 import com.intellij.openapi.progress.ProgressIndicator;
7 import com.intellij.openapi.progress.ProgressManager;
8 import com.intellij.openapi.project.Project;
9 import com.intellij.openapi.util.Disposer;
10 import org.jetbrains.annotations.NonNls;
11 import org.jetbrains.annotations.NotNull;
12 import org.jetbrains.annotations.Nullable;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.concurrent.*;
18 public class SlowlyClosingAlarm implements AtomicSectionsAware, Disposable {
19 private final static Logger LOG = Logger.getInstance("#com.intellij.lifecycle.SlowlyClosingAlarm");
21 protected final ControlledAlarmFactory.MyExecutorWrapper myExecutorService;
22 // single threaded executor, so we have "shared" state here
23 private boolean myInUninterruptibleState;
24 protected boolean myDisposeStarted;
25 private boolean myFinished;
26 // for own threads only
27 protected final List<Future<?>> myFutureList;
28 protected final Object myLock;
30 private static final ThreadFactory THREAD_FACTORY_OWN = threadFactory("SlowlyClosingAlarm pool");
31 private final String myName;
32 private final boolean myExecutorIsShared;
34 private static ThreadFactory threadFactory(@NonNls final String threadsName) {
35 return new ThreadFactory() {
36 public Thread newThread(final Runnable r) {
37 return new Thread(r, threadsName);
42 public void dispose() {
43 synchronized (myLock) {
44 safelyShutdownExecutor();
45 for (Future<?> future : myFutureList) {
46 future.cancel(true);
48 myFutureList.clear();
52 protected SlowlyClosingAlarm(@NotNull final Project project, @NotNull final String name) {
53 this(project, name, ControlledAlarmFactory.createExecutorWrapper(Executors.newSingleThreadExecutor(THREAD_FACTORY_OWN)), false);
56 protected SlowlyClosingAlarm(@NotNull final Project project, @NotNull final String name, final ControlledAlarmFactory.MyExecutorWrapper executor,
57 final boolean executorIsShared) {
58 myName = name;
59 myExecutorIsShared = executorIsShared;
60 myExecutorService = executor;
61 myLock = new Object();
62 myFutureList = new ArrayList<Future<?>>();
63 Disposer.register(project, this);
64 PeriodicalTasksCloser.getInstance(project).register(name, new Runnable() {
65 public void run() {
66 waitAndInterrupt(ProgressManager.getInstance().getProgressIndicator());
68 });
71 protected void debug(final String s) {
72 LOG.debug(myName + " " + s);
75 public void addRequest(final Runnable runnable) {
76 synchronized (myLock) {
77 if (myDisposeStarted) return;
78 final MyWrapper wrapper = new MyWrapper(runnable);
79 final Future<?> future = myExecutorService.submit(wrapper);
80 wrapper.setFuture(future);
81 myFutureList.add(future);
82 debug("request added");
86 private void stopSelf() {
87 if (myExecutorIsShared) {
88 throw new ProcessCanceledException();
89 } else {
90 Thread.currentThread().interrupt();
94 public void enter() {
95 synchronized (myLock) {
96 debug("entering section");
97 if (myDisposeStarted) {
98 debug("self-interrupting (1)");
99 stopSelf();
101 myInUninterruptibleState = true;
105 public void exit() {
106 debug("exiting section");
107 synchronized (myLock) {
108 myInUninterruptibleState = false;
109 if (myDisposeStarted) {
110 debug("self-interrupting (2)");
111 stopSelf();
116 public boolean shouldExitAsap() {
117 synchronized (myLock) {
118 return myDisposeStarted;
122 public void checkShouldExit() throws ProcessCanceledException {
123 synchronized (myLock) {
124 if (myDisposeStarted) {
125 stopSelf();
130 private void safelyShutdownExecutor() {
131 synchronized (myLock) {
132 if (! myExecutorIsShared) {
133 try {
134 myExecutorService.shutdown();
135 } catch (SecurityException e) {
142 public void waitAndInterrupt(@Nullable final ProgressIndicator indicator) {
143 final List<Future<?>> copy;
144 synchronized (myLock) {
145 debug("starting shutdown: " + myFutureList.size());
146 myDisposeStarted = true;
147 safelyShutdownExecutor();
149 copy = new ArrayList<Future<?>>(myFutureList.size());
150 for (Future<?> future : myFutureList) {
151 if (future.isDone()) continue;
152 copy.add(future);
156 debug("waiting for gets");
157 boolean wasCanceled = false;
158 for (Future<?> future : copy) {
159 if (wasCanceled) break;
160 while (true) {
161 try {
162 if (indicator == null) {
163 future.get();
164 } else {
165 future.get(500, TimeUnit.MILLISECONDS);
167 } catch (CancellationException e) {
168 break;
169 } catch (InterruptedException e) {
170 break;
172 catch (ExecutionException e) {
173 break;
175 catch (TimeoutException e) {
176 if (indicator != null) {
177 wasCanceled |= indicator.isCanceled();
178 if (wasCanceled) {
179 break;
181 debug("was canceled");
183 continue;
185 break;
189 debug("finishing " + myInUninterruptibleState);
190 synchronized (myLock) {
191 for (Future<?> future : myFutureList) {
192 future.cancel(true);
194 myFutureList.clear();
195 myFinished = true;
197 debug("done");
200 protected class MyWrapper implements Runnable {
201 private final Runnable myDelegate;
202 private Future myFuture;
204 protected MyWrapper(final Runnable delegate) {
205 myDelegate = delegate;
208 public void setFuture(final Future future) {
209 myFuture = future;
212 public void run() {
213 try {
214 debug("wrapper starts runnable");
215 myDelegate.run();
216 debug("wrapper: runnable succesfully finished");
217 } finally {
218 // anyway, the owner Future is no more interesting for us: its task is finished and does not require "anti-closing" defence
219 if (myFuture != null) {
220 debug("removing future");
221 synchronized (myLock) {
222 myFutureList.remove(myFuture);