ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/CancellableTask.java
Revision: 1.18
Committed: Fri Sep 26 11:37:10 2003 UTC (20 years, 8 months ago) by dl
Branch: MAIN
Changes since 1.17: +1 -1 lines
Log Message:
Spellcheck

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 * @throws NullPointerException if runnable is null
57 */
58 public CancellableTask(Runnable r) {
59 if (r == null)
60 throw new NullPointerException();
61 this.runnable = r;
62 }
63
64 /**
65 * Creates a new CancellableTask without a runnable action, which
66 * must be set using <tt>setRunnable</tt> before use. This is
67 * intended for use in subclasses that must complete superclass
68 * construction before establishing the runnable action.
69 */
70 protected CancellableTask() {
71 }
72
73
74 public boolean cancel(boolean mayInterruptIfRunning) {
75 Object r = runner;
76 if (r == DONE || r == CANCELLED)
77 return false;
78
79 if (mayInterruptIfRunning &&
80 r != null &&
81 r instanceof Thread &&
82 runnerUpdater.compareAndSet(this, r, CANCELLED))
83
84 ((Thread)r).interrupt();
85 else
86 runnerUpdater.set(this, CANCELLED);
87 return true;
88 }
89
90 public boolean isCancelled() {
91 return runner == CANCELLED;
92 }
93
94 public boolean isDone() {
95 Object r = runner;
96 return r == DONE || r == CANCELLED;
97 }
98
99 /**
100 * Return the Runnable forming the basis of this task.
101 * @return the runnable action
102 * @see #setRunnable
103 */
104 protected Runnable getRunnable() {
105 return runnable;
106 }
107
108 /**
109 * Set the Runnable forming the basis of this task.
110 * @param r the runnable action
111 * @see #getRunnable
112 */
113 protected void setRunnable(Runnable r) {
114 runnable = r;
115 }
116
117 /**
118 * Set the state of this task to Cancelled.
119 */
120 protected void setCancelled() {
121 runnerUpdater.set(this, CANCELLED);
122 }
123
124 /**
125 * Set the state of this task to Done, unless already
126 * in a Cancelled state, in which Cancelled status is preserved.
127 *
128 */
129 protected void setDone() {
130 for (;;) {
131 Object r = runner;
132 if (r == DONE || r == CANCELLED)
133 return;
134 if (runnerUpdater.compareAndSet(this, r, DONE))
135 return;
136 }
137 }
138
139 /**
140 * Attempt to set the state of this task to Running, succeeding
141 * only if the state is currently NOT Done, Running, or Cancelled.
142 * @return true if successful
143 */
144 protected boolean setRunning() {
145 return runnerUpdater.compareAndSet(this, null, Thread.currentThread());
146 }
147
148 /**
149 * Runs the runnable if not cancelled, maintaining run status.
150 */
151 public void run() {
152 if (setRunning()) {
153 try {
154 runnable.run();
155 } finally {
156 setDone();
157 }
158 }
159 }
160
161 /**
162 * Reset the run state of this task to its initial state unless
163 * it has been cancelled. (Note that a cancelled task cannot be
164 * reset.)
165 * @return true if successful
166 */
167 protected boolean reset() {
168 for (;;) {
169 Object r = runner;
170 if (r == CANCELLED)
171 return false;
172 if (runnerUpdater.compareAndSet(this, r, null))
173 return true;
174 }
175 }
176
177 /**
178 * Implementation of Future methods under the control of a current
179 * <tt>CancellableTask</tt>, which it relies on for methods
180 * <tt>isDone</tt>, <tt>isCancelled</tt> and <tt>cancel</tt>. This
181 * class is split into an inner class to permit Future support to
182 * be mixed-in with other flavors of tasks. Normally, such a
183 * class will delegate <tt>Future</tt> <tt>get</tt> methods to the
184 * <tt>InnerCancellableFuture</tt>, and internally arrange that
185 * <tt>set</tt> methods be invoked when computations are ready.
186 *
187 * <p><b>Sample Usage</b>. Here are fragments of an example subclass.
188 * <pre>
189 * class MyFutureTask&lt;V&gt; extends CancellableTask implements Future&lt;
190 V&gt; {
191 *
192 * MyFutureTask(Callable&lt;V&gt; callable) {
193 * setRunnable(new InnerCancellableFuture&lt;V&gt;(callable));
194 * }
195 *
196 * public V get() throws InterruptedException, ExecutionException {
197 * return ((InnerCancellableFuture&lt;V&gt;)getRunnable()).get();
198 * }
199 * // (And similarly for timeout version.)
200 *
201 * void action() { // whatever action causes execution
202 * try {
203 * ((InnerCancellableFuture&lt;V&gt;)getRunnable()).set(compute());
204 * } catch (Exception ex) {
205 * ((InnerCancellableFuture&lt;V&gt;)getRunnable()).setException(ex);
206 * }
207 * }
208 * }
209 *</pre>
210 */
211 protected class InnerCancellableFuture<V> implements Future<V>, Runnable {
212 private final Callable<V> callable;
213 private final ReentrantLock lock = new ReentrantLock();
214 private final Condition accessible = lock.newCondition();
215 private V result;
216 private Throwable exception;
217
218 /**
219 * Create an InnerCancellableFuture that will execute the
220 * given callable.
221 * @param callable the function to execute
222 */
223 protected InnerCancellableFuture(Callable<V> callable) {
224 this.callable = callable;
225 }
226
227 public boolean cancel(boolean mayInterruptIfRunning) {
228 return CancellableTask.this.cancel(mayInterruptIfRunning);
229 }
230
231 public boolean isCancelled() {
232 return CancellableTask.this.isCancelled();
233 }
234
235
236 public boolean isDone() {
237 return CancellableTask.this.isDone();
238 }
239
240
241 /**
242 * Sets this Future to the results of <tt>callable.call</tt>
243 */
244 public void run() {
245 try {
246 set(callable.call());
247 } catch(Throwable ex) {
248 setException(ex);
249 }
250 }
251
252 /**
253 * Waits if necessary for the call to <tt>callable.call</tt> to
254 * complete, and then retrieves its result.
255 *
256 * @return computed result
257 * @throws CancellationException here???
258 * @throws ExecutionException if underlying computation threw an
259 * exception
260 * @throws InterruptedException if current thread was interrupted
261 * while waiting
262 */
263 public V get() throws InterruptedException, ExecutionException {
264 lock.lock();
265 try {
266 while (!isDone())
267 accessible.await();
268 if (isCancelled())
269 throw new CancellationException();
270 else if (exception != null)
271 throw new ExecutionException(exception);
272 else
273 return result;
274 } finally {
275 lock.unlock();
276 }
277 }
278
279 /**
280 * Waits if necessary for at most the given time for the call to
281 * <tt>callable.call</tt> to complete, and then retrieves its
282 * result.
283 *
284 * @param timeout the maximum time to wait
285 * @param unit the time unit of the timeout argument
286 * @return computed result
287 * @throws ExecutionException if underlying computation threw an
288 * exception
289 * @throws InterruptedException if current thread was interrupted
290 * while waiting
291 * @throws TimeoutException if the wait timed out
292 */
293 public V get(long timeout, TimeUnit unit)
294 throws InterruptedException, ExecutionException, TimeoutException {
295 lock.lock();
296 try {
297 if (!isDone()) {
298 long nanos = unit.toNanos(timeout);
299 do {
300 if (nanos <= 0)
301 throw new TimeoutException();
302 nanos = accessible.awaitNanos(nanos);
303 } while (!isDone());
304 }
305 if (isCancelled())
306 throw new CancellationException();
307 else if (exception != null)
308 throw new ExecutionException(exception);
309 else
310 return result;
311 } finally {
312 lock.unlock();
313 }
314 }
315
316 /**
317 * Sets the result of this Future to the given value.
318 * @param v the value
319 */
320 protected void set(V v) {
321 lock.lock();
322 try {
323 result = v;
324 setDone();
325 accessible.signalAll();
326 } finally {
327 lock.unlock();
328 }
329 }
330
331 /**
332 * Causes this future to report an <tt>ExecutionException</tt>
333 * with the given throwable as its cause.
334 * @param t the cause of failure.
335 */
336 protected void setException(Throwable t) {
337 lock.lock();
338 try {
339 exception = t;
340 setDone();
341 accessible.signalAll();
342 } finally {
343 lock.unlock();
344 }
345 }
346 }
347
348 }