IDEA-25467 (CVS is set to Offline mode on project open) - integration to trunk
[fedora-idea.git] / platform / vcs-impl / src / com / intellij / lifecycle / SlowlyClosingAlarm.java
blob164c181955fe5d7edde8643bf0186ffb2d901e7c
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.lifecycle;
18 import com.intellij.openapi.Disposable;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.progress.ProcessCanceledException;
21 import com.intellij.openapi.progress.ProgressIndicator;
22 import com.intellij.openapi.progress.ProgressManager;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.util.Disposer;
25 import org.jetbrains.annotations.NonNls;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.concurrent.*;
33 public class SlowlyClosingAlarm implements AtomicSectionsAware, Disposable {
34 private final static Logger LOG = Logger.getInstance("#com.intellij.lifecycle.SlowlyClosingAlarm");
36 protected final ControlledAlarmFactory.MyExecutorWrapper myExecutorService;
37 // single threaded executor, so we have "shared" state here
38 private boolean myInUninterruptibleState;
39 protected boolean myDisposeStarted;
40 private boolean myFinished;
41 // for own threads only
42 protected final List<Future<?>> myFutureList;
43 protected final Object myLock;
45 private static final ThreadFactory THREAD_FACTORY_OWN = threadFactory("SlowlyClosingAlarm pool");
46 private final String myName;
47 private final boolean myExecutorIsShared;
48 private boolean myDisposed;
50 private static ThreadFactory threadFactory(@NonNls final String threadsName) {
51 return new ThreadFactory() {
52 public Thread newThread(final Runnable r) {
53 final Thread thread = new Thread(r, threadsName);
54 thread.setPriority(Thread.MIN_PRIORITY);
55 return thread;
60 public void dispose() {
61 synchronized (myLock) {
62 safelyShutdownExecutor();
63 for (Future<?> future : myFutureList) {
64 future.cancel(true);
66 myFutureList.clear();
67 myDisposed = true;
71 protected SlowlyClosingAlarm(@NotNull final Project project, @NotNull final String name) {
72 this(project, name, ControlledAlarmFactory.createExecutorWrapper(Executors.newSingleThreadExecutor(THREAD_FACTORY_OWN)), false);
75 protected SlowlyClosingAlarm(@NotNull final Project project, @NotNull final String name, final ControlledAlarmFactory.MyExecutorWrapper executor,
76 final boolean executorIsShared) {
77 myName = name;
78 myExecutorIsShared = executorIsShared;
79 myExecutorService = executor;
80 myLock = new Object();
81 myFutureList = new ArrayList<Future<?>>();
82 Disposer.register(project, this);
83 myDisposeStarted = ! PeriodicalTasksCloser.getInstance(project).register(name, new Runnable() {
84 public void run() {
85 waitAndInterrupt(ProgressManager.getInstance().getProgressIndicator());
87 });
90 protected void debug(final String s) {
91 LOG.debug(myName + " " + s);
94 public void addRequest(final Runnable runnable) {
95 synchronized (myLock) {
96 if (myDisposed || myDisposeStarted) return;
97 final MyWrapper wrapper = new MyWrapper(runnable);
98 final Future<?> future = myExecutorService.submit(wrapper);
99 wrapper.setFuture(future);
100 myFutureList.add(future);
101 debug("request added");
105 private void stopSelf() {
106 if (myExecutorIsShared) {
107 throw new ProcessCanceledException();
108 } else {
109 Thread.currentThread().interrupt();
113 public void enter() {
114 synchronized (myLock) {
115 debug("entering section");
116 if (myDisposeStarted) {
117 debug("self-interrupting (1)");
118 stopSelf();
120 myInUninterruptibleState = true;
124 public void exit() {
125 debug("exiting section");
126 synchronized (myLock) {
127 myInUninterruptibleState = false;
128 if (myDisposeStarted) {
129 debug("self-interrupting (2)");
130 stopSelf();
135 public boolean shouldExitAsap() {
136 synchronized (myLock) {
137 return myDisposeStarted;
141 public void checkShouldExit() throws ProcessCanceledException {
142 synchronized (myLock) {
143 if (myDisposeStarted) {
144 stopSelf();
149 private void safelyShutdownExecutor() {
150 synchronized (myLock) {
151 if (! myExecutorIsShared) {
152 try {
153 myExecutorService.shutdown();
154 } catch (SecurityException e) {
161 public void waitAndInterrupt(@Nullable final ProgressIndicator indicator) {
162 final List<Future<?>> copy;
163 synchronized (myLock) {
164 debug("starting shutdown: " + myFutureList.size());
165 myDisposeStarted = true;
166 safelyShutdownExecutor();
168 copy = new ArrayList<Future<?>>(myFutureList.size());
169 for (Future<?> future : myFutureList) {
170 if (future.isDone()) continue;
171 copy.add(future);
175 debug("waiting for gets");
176 boolean wasCanceled = false;
177 for (Future<?> future : copy) {
178 if (wasCanceled) break;
179 while (true) {
180 try {
181 if (indicator == null) {
182 future.get();
183 } else {
184 future.get(500, TimeUnit.MILLISECONDS);
186 } catch (CancellationException e) {
187 break;
188 } catch (InterruptedException e) {
189 break;
191 catch (ExecutionException e) {
192 break;
194 catch (TimeoutException e) {
195 if (indicator != null) {
196 wasCanceled |= indicator.isCanceled();
197 if (wasCanceled) {
198 break;
200 debug("was canceled");
202 continue;
204 break;
208 debug("finishing " + myInUninterruptibleState);
209 synchronized (myLock) {
210 for (Future<?> future : myFutureList) {
211 future.cancel(true);
213 myFutureList.clear();
214 myFinished = true;
216 debug("done");
219 protected class MyWrapper implements Runnable {
220 private final Runnable myDelegate;
221 private Future myFuture;
223 protected MyWrapper(final Runnable delegate) {
224 myDelegate = delegate;
227 public void setFuture(final Future future) {
228 myFuture = future;
231 public void run() {
232 try {
233 debug("wrapper starts runnable");
234 myDelegate.run();
235 debug("wrapper: runnable succesfully finished");
236 } finally {
237 // anyway, the owner Future is no more interesting for us: its task is finished and does not require "anti-closing" defence
238 if (myFuture != null) {
239 debug("removing future");
240 synchronized (myLock) {
241 myFutureList.remove(myFuture);