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
) {
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
) {
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() {
66 waitAndInterrupt(ProgressManager
.getInstance().getProgressIndicator());
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();
90 Thread
.currentThread().interrupt();
95 synchronized (myLock
) {
96 debug("entering section");
97 if (myDisposeStarted
) {
98 debug("self-interrupting (1)");
101 myInUninterruptibleState
= true;
106 debug("exiting section");
107 synchronized (myLock
) {
108 myInUninterruptibleState
= false;
109 if (myDisposeStarted
) {
110 debug("self-interrupting (2)");
116 public boolean shouldExitAsap() {
117 synchronized (myLock
) {
118 return myDisposeStarted
;
122 public void checkShouldExit() throws ProcessCanceledException
{
123 synchronized (myLock
) {
124 if (myDisposeStarted
) {
130 private void safelyShutdownExecutor() {
131 synchronized (myLock
) {
132 if (! myExecutorIsShared
) {
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;
156 debug("waiting for gets");
157 boolean wasCanceled
= false;
158 for (Future
<?
> future
: copy
) {
159 if (wasCanceled
) break;
162 if (indicator
== null) {
165 future
.get(500, TimeUnit
.MILLISECONDS
);
167 } catch (CancellationException e
) {
169 } catch (InterruptedException e
) {
172 catch (ExecutionException e
) {
175 catch (TimeoutException e
) {
176 if (indicator
!= null) {
177 wasCanceled
|= indicator
.isCanceled();
181 debug("was canceled");
189 debug("finishing " + myInUninterruptibleState
);
190 synchronized (myLock
) {
191 for (Future
<?
> future
: myFutureList
) {
194 myFutureList
.clear();
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
) {
214 debug("wrapper starts runnable");
216 debug("wrapper: runnable succesfully finished");
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
);