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
);
60 public void dispose() {
61 synchronized (myLock
) {
62 safelyShutdownExecutor();
63 for (Future
<?
> future
: myFutureList
) {
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
) {
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() {
85 waitAndInterrupt(ProgressManager
.getInstance().getProgressIndicator());
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();
109 Thread
.currentThread().interrupt();
113 public void enter() {
114 synchronized (myLock
) {
115 debug("entering section");
116 if (myDisposeStarted
) {
117 debug("self-interrupting (1)");
120 myInUninterruptibleState
= true;
125 debug("exiting section");
126 synchronized (myLock
) {
127 myInUninterruptibleState
= false;
128 if (myDisposeStarted
) {
129 debug("self-interrupting (2)");
135 public boolean shouldExitAsap() {
136 synchronized (myLock
) {
137 return myDisposeStarted
;
141 public void checkShouldExit() throws ProcessCanceledException
{
142 synchronized (myLock
) {
143 if (myDisposeStarted
) {
149 private void safelyShutdownExecutor() {
150 synchronized (myLock
) {
151 if (! myExecutorIsShared
) {
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;
175 debug("waiting for gets");
176 boolean wasCanceled
= false;
177 for (Future
<?
> future
: copy
) {
178 if (wasCanceled
) break;
181 if (indicator
== null) {
184 future
.get(500, TimeUnit
.MILLISECONDS
);
186 } catch (CancellationException e
) {
188 } catch (InterruptedException e
) {
191 catch (ExecutionException e
) {
194 catch (TimeoutException e
) {
195 if (indicator
!= null) {
196 wasCanceled
|= indicator
.isCanceled();
200 debug("was canceled");
208 debug("finishing " + myInUninterruptibleState
);
209 synchronized (myLock
) {
210 for (Future
<?
> future
: myFutureList
) {
213 myFutureList
.clear();
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
) {
233 debug("wrapper starts runnable");
235 debug("wrapper: runnable succesfully finished");
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
);