ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/CancellableTask.java
Revision: 1.14
Committed: Sun Aug 31 13:33:13 2003 UTC (20 years, 9 months ago) by dl
Branch: MAIN
Changes since 1.13: +7 -6 lines
Log Message:
Removed non-standard tags and misc javadoc cleanup

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 * Implementation of Future methods under the control of a current
157 * <tt>CancellableTask</tt>, which it relies on for methods
158 * <tt>isDone</tt>, <tt>isCancelled</tt> and <tt>cancel</tt>. This
159 * class is split into an inner class to permit Future support to
160 * be mixed-in with other flavors of tasks. Normally, such a
161 * class will delegate <tt>Future</tt> <tt>get</tt> methods to the
162 * <tt>InnerCancellableFuture</tt>, and internally arrange that
163 * <tt>set</tt> methods be invoked when computations are ready.
164 *
165 * <p><b>Sample Usage</b>. Here are fragments of an example subclass.
166 * <pre>
167 * class MyFutureTask&lt;V&gt; extends CancellableTask implements Future&lt;
168 V&gt; {
169 *
170 * MyFutureTask(Callable&lt;V&gt; callable) {
171 * setRunnable(new InnerCancellableFuture&lt;V&gt;(callable));
172 * }
173 *
174 * public V get() throws InterruptedException, ExecutionException {
175 * return ((InnerCancellableFuture&lt;V&gt;)getRunnable()).get();
176 * }
177 * // (And similarly for timeout version.)
178 *
179 * void action() { // whatever action causes execution
180 * try {
181 * ((InnerCancellableFuture&lt;V&gt;)getRunnable()).set(compute());
182 * } catch (Exception ex) {
183 * ((InnerCancellableFuture&lt;V&gt;)getRunnable()).setException(ex);
184 * }
185 * }
186 * }
187 *</pre>
188 */
189 protected class InnerCancellableFuture<V> implements Future<V>, Runnable {
190 private final Callable<V> callable;
191 private final ReentrantLock lock = new ReentrantLock();
192 private final Condition accessible = lock.newCondition();
193 private V result;
194 private Throwable exception;
195
196 /**
197 * Create an InnerCancellableFuture that will execute the
198 * given callable.
199 * @param callable the function to execute
200 */
201 protected InnerCancellableFuture(Callable<V> callable) {
202 this.callable = callable;
203 }
204
205 public boolean cancel(boolean mayInterruptIfRunning) {
206 return CancellableTask.this.cancel(mayInterruptIfRunning);
207 }
208
209 public boolean isCancelled() {
210 return CancellableTask.this.isCancelled();
211 }
212
213
214 public boolean isDone() {
215 return CancellableTask.this.isDone();
216 }
217
218
219 /**
220 * Sets this Future to the results of <tt>callable.call</tt>
221 */
222 public void run() {
223 try {
224 set(callable.call());
225 } catch(Throwable ex) {
226 setException(ex);
227 }
228 }
229
230 /**
231 * Waits if necessary for the call to <tt>callable.call</tt> to
232 * complete, and then retrieves its result.
233 *
234 * @return computed result
235 * @throws CancellationException here???
236 * @throws ExecutionException if underlying computation threw an
237 * exception
238 * @throws InterruptedException if current thread was interrupted
239 * while waiting
240 */
241 public V get() throws InterruptedException, ExecutionException {
242 lock.lock();
243 try {
244 while (!isDone())
245 accessible.await();
246 if (isCancelled())
247 throw new CancellationException();
248 else if (exception != null)
249 throw new ExecutionException(exception);
250 else
251 return result;
252 } finally {
253 lock.unlock();
254 }
255 }
256
257 /**
258 * Waits if necessary for at most the given time for the call to
259 * <tt>callable.call</tt> to complete, and then retrieves its
260 * result.
261 *
262 * @param timeout the maximum time to wait
263 * @param unit the time unit of the timeout argument
264 * @return computed result
265 * @throws ExecutionException if underlying computation threw an
266 * exception
267 * @throws InterruptedException if current thread was interrupted
268 * while waiting
269 * @throws TimeoutException if the wait timed out
270 */
271 public V get(long timeout, TimeUnit unit)
272 throws InterruptedException, ExecutionException, TimeoutException {
273 lock.lock();
274 try {
275 if (!isDone()) {
276 long nanos = unit.toNanos(timeout);
277 do {
278 if (nanos <= 0)
279 throw new TimeoutException();
280 nanos = accessible.awaitNanos(nanos);
281 } while (!isDone());
282 }
283 if (isCancelled())
284 throw new CancellationException();
285 else if (exception != null)
286 throw new ExecutionException(exception);
287 else
288 return result;
289 } finally {
290 lock.unlock();
291 }
292 }
293
294 /**
295 * Sets the result of this Future to the given value.
296 * @param v the value
297 */
298 protected void set(V v) {
299 lock.lock();
300 try {
301 result = v;
302 setDone();
303 accessible.signalAll();
304 } finally {
305 lock.unlock();
306 }
307 }
308
309 /**
310 * Causes this futue to report an <tt>ExecutionException</tt>
311 * with the given throwable as its cause.
312 * @param t the cause of failure.
313 */
314 protected void setException(Throwable t) {
315 lock.lock();
316 try {
317 exception = t;
318 setDone();
319 accessible.signalAll();
320 } finally {
321 lock.unlock();
322 }
323 }
324 }
325
326 }