ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/CancellableTask.java
Revision: 1.15
Committed: Sun Sep 7 23:28:21 2003 UTC (20 years, 8 months ago) by dl
Branch: MAIN
Changes since 1.14: +7 -0 lines
Log Message:
Allow resets

File Contents

# Content
1 /*
2 * Written by Doug Lea with assistance from members of JCP JSR-166
3 * Expert Group and released to the public domain. Use, modify, and
4 * redistribute this code in any way without acknowledgement.
5 */
6
7 package java.util.concurrent;
8 import java.util.concurrent.atomic.*;
9 import java.util.concurrent.locks.*;
10
11 /**
12 * Base class for {@link Cancellable} {@link java.lang.Runnable}
13 * actions within the {@link Executor} framework. In addition to
14 * serving as a standalone class, this provides <tt>protected</tt>
15 * functionality that may be useful when creating customized task
16 * classes.
17 * @since 1.5
18 * @author Doug Lea
19 */
20
21 public class CancellableTask implements Cancellable, Runnable {
22 /**
23 * Holds the run-state, taking on values:
24 * null = not yet started,
25 * [some thread ref] = running,
26 * DONE = completed normally,
27 * CANCELLED = cancelled (may or may not have ever run).
28 * Transitions use atomic updates.
29 */
30 private volatile Object runner;
31
32 /**
33 * Special value for "runner" indicating task is completed
34 */
35 private static final Object DONE = new Object();
36
37 /**
38 * Special value for "runner" indicating task is cancelled
39 */
40 private static final Object CANCELLED = new Object();
41
42 private static AtomicReferenceFieldUpdater<CancellableTask, Object>
43 runnerUpdater =
44 AtomicReferenceFieldUpdater.newUpdater
45 (CancellableTask.class, Object.class, "runner");
46
47 /**
48 * The runnable underlying this task
49 */
50 private volatile Runnable runnable;
51
52 /**
53 * Creates a new CancellableTask which invokes the given
54 * <tt>Runnable</tt> when executed.
55 * @param r the runnable action
56 */
57 public CancellableTask(Runnable r) {
58 this.runnable = r;
59 }
60
61 /**
62 * Creates a new CancellableTask without a runnable action, which
63 * must be set using <tt>setRunnable</tt> before use. This is
64 * intended for use in subclasses that must complete superclass
65 * construction before establishing the runnable action.
66 */
67 protected CancellableTask() {
68 }
69
70
71 public boolean cancel(boolean mayInterruptIfRunning) {
72 Object r = runner;
73 if (r == DONE || r == CANCELLED)
74 return false;
75
76 if (mayInterruptIfRunning &&
77 r != null &&
78 r instanceof Thread &&
79 runnerUpdater.compareAndSet(this, r, CANCELLED))
80
81 ((Thread)r).interrupt();
82 else
83 runnerUpdater.set(this, CANCELLED);
84 return true;
85 }
86
87 public boolean isCancelled() {
88 return runner == CANCELLED;
89 }
90
91 public boolean isDone() {
92 Object r = runner;
93 return r == DONE || r == CANCELLED;
94 }
95
96 /**
97 * Return the Runnable forming the basis of this task.
98 * @return the runnable action
99 * @see #setRunnable
100 */
101 protected Runnable getRunnable() {
102 return runnable;
103 }
104
105 /**
106 * Set the Runnable forming the basis of this task.
107 * @param r the runnable action
108 * @see #getRunnable
109 */
110 protected void setRunnable(Runnable r) {
111 runnable = r;
112 }
113
114 /**
115 * Set the state of this task to Cancelled.
116 */
117 protected void setCancelled() {
118 runnerUpdater.set(this, CANCELLED);
119 }
120
121 /**
122 * Set the state of this task to Done, unless already
123 * in a Cancelled state, in which Cancelled status is preserved.
124 *
125 */
126 protected void setDone() {
127 for (;;) {
128 Object r = runner;
129 if (r == DONE || r == CANCELLED)
130 return;
131 if (runnerUpdater.compareAndSet(this, r, DONE))
132 return;
133 }
134 }
135
136 /**
137 * Attempt to set the state of this task to Running, succeeding
138 * only if the state is currently NOT Done, Running, or Cancelled.
139 * @return true if successful
140 */
141 protected boolean setRunning() {
142 return runnerUpdater.compareAndSet(this, null, Thread.currentThread());
143 }
144
145 public void run() {
146 if (setRunning()) {
147 try {
148 runnable.run();
149 } finally {
150 setDone();
151 }
152 }
153 }
154
155 /**
156 * Reset the run state of this task to its initial state.
157 */
158 protected void reset() {
159 runnerUpdater.set(this, null);
160 }
161
162 /**
163 * Implementation of Future methods under the control of a current
164 * <tt>CancellableTask</tt>, which it relies on for methods
165 * <tt>isDone</tt>, <tt>isCancelled</tt> and <tt>cancel</tt>. This
166 * class is split into an inner class to permit Future support to
167 * be mixed-in with other flavors of tasks. Normally, such a
168 * class will delegate <tt>Future</tt> <tt>get</tt> methods to the
169 * <tt>InnerCancellableFuture</tt>, and internally arrange that
170 * <tt>set</tt> methods be invoked when computations are ready.
171 *
172 * <p><b>Sample Usage</b>. Here are fragments of an example subclass.
173 * <pre>
174 * class MyFutureTask&lt;V&gt; extends CancellableTask implements Future&lt;
175 V&gt; {
176 *
177 * MyFutureTask(Callable&lt;V&gt; callable) {
178 * setRunnable(new InnerCancellableFuture&lt;V&gt;(callable));
179 * }
180 *
181 * public V get() throws InterruptedException, ExecutionException {
182 * return ((InnerCancellableFuture&lt;V&gt;)getRunnable()).get();
183 * }
184 * // (And similarly for timeout version.)
185 *
186 * void action() { // whatever action causes execution
187 * try {
188 * ((InnerCancellableFuture&lt;V&gt;)getRunnable()).set(compute());
189 * } catch (Exception ex) {
190 * ((InnerCancellableFuture&lt;V&gt;)getRunnable()).setException(ex);
191 * }
192 * }
193 * }
194 *</pre>
195 */
196 protected class InnerCancellableFuture<V> implements Future<V>, Runnable {
197 private final Callable<V> callable;
198 private final ReentrantLock lock = new ReentrantLock();
199 private final Condition accessible = lock.newCondition();
200 private V result;
201 private Throwable exception;
202
203 /**
204 * Create an InnerCancellableFuture that will execute the
205 * given callable.
206 * @param callable the function to execute
207 */
208 protected InnerCancellableFuture(Callable<V> callable) {
209 this.callable = callable;
210 }
211
212 public boolean cancel(boolean mayInterruptIfRunning) {
213 return CancellableTask.this.cancel(mayInterruptIfRunning);
214 }
215
216 public boolean isCancelled() {
217 return CancellableTask.this.isCancelled();
218 }
219
220
221 public boolean isDone() {
222 return CancellableTask.this.isDone();
223 }
224
225
226 /**
227 * Sets this Future to the results of <tt>callable.call</tt>
228 */
229 public void run() {
230 try {
231 set(callable.call());
232 } catch(Throwable ex) {
233 setException(ex);
234 }
235 }
236
237 /**
238 * Waits if necessary for the call to <tt>callable.call</tt> to
239 * complete, and then retrieves its result.
240 *
241 * @return computed result
242 * @throws CancellationException here???
243 * @throws ExecutionException if underlying computation threw an
244 * exception
245 * @throws InterruptedException if current thread was interrupted
246 * while waiting
247 */
248 public V get() throws InterruptedException, ExecutionException {
249 lock.lock();
250 try {
251 while (!isDone())
252 accessible.await();
253 if (isCancelled())
254 throw new CancellationException();
255 else if (exception != null)
256 throw new ExecutionException(exception);
257 else
258 return result;
259 } finally {
260 lock.unlock();
261 }
262 }
263
264 /**
265 * Waits if necessary for at most the given time for the call to
266 * <tt>callable.call</tt> to complete, and then retrieves its
267 * result.
268 *
269 * @param timeout the maximum time to wait
270 * @param unit the time unit of the timeout argument
271 * @return computed result
272 * @throws ExecutionException if underlying computation threw an
273 * exception
274 * @throws InterruptedException if current thread was interrupted
275 * while waiting
276 * @throws TimeoutException if the wait timed out
277 */
278 public V get(long timeout, TimeUnit unit)
279 throws InterruptedException, ExecutionException, TimeoutException {
280 lock.lock();
281 try {
282 if (!isDone()) {
283 long nanos = unit.toNanos(timeout);
284 do {
285 if (nanos <= 0)
286 throw new TimeoutException();
287 nanos = accessible.awaitNanos(nanos);
288 } while (!isDone());
289 }
290 if (isCancelled())
291 throw new CancellationException();
292 else if (exception != null)
293 throw new ExecutionException(exception);
294 else
295 return result;
296 } finally {
297 lock.unlock();
298 }
299 }
300
301 /**
302 * Sets the result of this Future to the given value.
303 * @param v the value
304 */
305 protected void set(V v) {
306 lock.lock();
307 try {
308 result = v;
309 setDone();
310 accessible.signalAll();
311 } finally {
312 lock.unlock();
313 }
314 }
315
316 /**
317 * Causes this futue to report an <tt>ExecutionException</tt>
318 * with the given throwable as its cause.
319 * @param t the cause of failure.
320 */
321 protected void setException(Throwable t) {
322 lock.lock();
323 try {
324 exception = t;
325 setDone();
326 accessible.signalAll();
327 } finally {
328 lock.unlock();
329 }
330 }
331 }
332
333 }