358586a116988f343b8640b94a6e589ddc06d3b0
[idea/community.git] / platform / platform-api / src / com / intellij / util / concurrency / SwingWorker.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.util.concurrency;
17
18 import com.intellij.openapi.application.ApplicationManager;
19 import com.intellij.openapi.application.ModalityState;
20 import com.intellij.openapi.diagnostic.Logger;
21
22 /**
23  * This is the 3rd version of SwingWorker (also known as
24  * SwingWorker 3), an abstract class that you subclass to
25  * perform GUI-related work in a dedicated thread.  For
26  * instructions on using this class, see:
27  *
28  * http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html
29  *
30  * Note that the API changed slightly in the 3rd version:
31  * You must now invoke start() on the SwingWorker after
32  * creating it.
33  */
34 public abstract class SwingWorker {
35   private static final Logger LOG = Logger.getInstance("#com.intellij.util.concurrency.SwingWorker");
36   private Object value;
37   // see getValue(), setValue()
38
39   private final ModalityState myModalityState;
40
41   /**
42    * Class to maintain reference to current worker thread
43    * under separate synchronization control.
44    */
45   private static class ThreadVar {
46     private Thread myThread;
47
48     ThreadVar(Thread t) {
49       myThread = t;
50     }
51
52     synchronized Thread get() {
53       return myThread;
54     }
55
56     synchronized void clear() {
57       myThread = null;
58     }
59   }
60
61   private final ThreadVar myThreadVar;
62   /**
63    * Get the value produced by the worker thread, or null if it
64    * hasn't been constructed yet.
65    */
66
67   protected synchronized Object getValue() {
68     return value;
69   }
70
71   /**
72    * Set the value produced by worker thread
73    */
74
75   private synchronized void setValue(Object x) {
76     value = x;
77   }
78
79   /**
80    * Compute the value to be returned by the <code>get</code> method.
81    */
82
83   public abstract Object construct();
84
85   /**
86    * Called on the event dispatching thread (not on the worker thread)
87    * after the <code>construct</code> method has returned.
88    */
89
90   public void finished() {
91   }
92
93   /**
94    * Called in the worker thread in case a RuntimeException or Error occurred
95    * if the <code>construct</code> method has thrown an uncaught Throwable.
96    */
97   public void onThrowable() {
98   }
99
100   /**
101    * A new method that interrupts the worker thread.  Call this method
102    * to force the worker to stop what it's doing.
103    */
104
105   public void interrupt() {
106     Thread t = myThreadVar.get();
107     if (t != null){
108       t.interrupt();
109     }
110     myThreadVar.clear();
111   }
112
113   /**
114    * Return the value created by the <code>construct</code> method.
115    * Returns null if either the constructing thread or the current
116    * thread was interrupted before a value was produced.
117    *
118    * @return the value created by the <code>construct</code> method
119    */
120
121   public Object get() {
122     while(true){
123       Thread t = myThreadVar.get();
124       if (t == null){
125         return getValue();
126       }
127       try{
128         t.join();
129       }
130       catch(InterruptedException e){
131         Thread.currentThread().interrupt();
132         // propagate
133         return null;
134       }
135     }
136   }
137
138   /**
139    * Start a thread that will call the <code>construct</code> method
140    * and then exit.
141    */
142
143   public SwingWorker() {
144     myModalityState = ModalityState.current();
145
146     final Runnable doFinished = () -> finished();
147
148     Runnable doConstruct = new Runnable() {
149       public void run() {
150         try{
151           setValue(construct());
152           if (LOG.isDebugEnabled()) {
153             LOG.debug("construct() terminated");
154           }
155         }
156         catch (Throwable e) {
157           LOG.error(e);
158           onThrowable();
159           throw new RuntimeException(e);
160         }
161         finally{
162           myThreadVar.clear();
163         }
164         if (LOG.isDebugEnabled()) {
165           LOG.debug("invoking 'finished' action");
166         }
167         ApplicationManager.getApplication().invokeLater(doFinished, myModalityState);
168       }
169     };
170
171     final Thread workerThread = new Thread(doConstruct, "SwingWorker work thread");
172     workerThread.setPriority(Thread.NORM_PRIORITY);
173     myThreadVar = new ThreadVar(workerThread);
174   }
175
176   /**
177    * Start the worker thread.
178    */
179
180   public void start() {
181     Thread t = myThreadVar.get();
182     if (t != null){
183       t.start();
184     }
185   }
186 }