1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.concurrency.FutureTask;
13 
14 import hunt.concurrency.atomic.AtomicHelper;
15 import hunt.concurrency.Executors;
16 import hunt.concurrency.Future;
17 import hunt.concurrency.thread;
18 import hunt.Exceptions;
19 import hunt.util.Common;
20 
21 import core.thread;
22 import core.time;
23 
24 import hunt.concurrency.thread;
25 import hunt.logging.ConsoleLogger;
26 
27 
28 /**
29  * A cancellable asynchronous computation.  This class provides a base
30  * implementation of {@link Future}, with methods to start and cancel
31  * a computation, query to see if the computation is complete, and
32  * retrieve the result of the computation.  The result can only be
33  * retrieved when the computation has completed; the {@code get}
34  * methods will block if the computation has not yet completed.  Once
35  * the computation has completed, the computation cannot be restarted
36  * or cancelled (unless the computation is invoked using
37  * {@link #runAndReset}).
38  *
39  * <p>A {@code FutureTask} can be used to wrap a {@link Callable} or
40  * {@link Runnable} object.  Because {@code FutureTask} implements
41  * {@code Runnable}, a {@code FutureTask} can be submitted to an
42  * {@link Executor} for execution.
43  *
44  * <p>In addition to serving as a standalone class, this class provides
45  * {@code protected} functionality that may be useful when creating
46  * customized task classes.
47  *
48  * @author Doug Lea
49  * @param (V) The result type returned by this FutureTask's {@code get} methods
50  */
51 class FutureTask(V) : RunnableFuture!(V) {
52     /*
53      * Revision notes: This differs from previous versions of this
54      * class that relied on AbstractQueuedSynchronizer, mainly to
55      * avoid surprising users about retaining interrupt status during
56      * cancellation races. Sync control in the current design relies
57      * on a "state" field updated via CAS to track completion, along
58      * with a simple Treiber stack to hold waiting threads.
59      */
60 
61     /**
62      * The run state of this task, initially NEW.  The run state
63      * transitions to a terminal state only in methods set,
64      * setException, and cancel.  During completion, state may take on
65      * values of COMPLETING (while outcome is being set) or
66      * INTERRUPTING (only while interrupting the runner to satisfy a
67      * cancel(true)). Transitions from these intermediate to final
68      * states use cheaper ordered/lazy writes because values are unique
69      * and cannot be further modified.
70      *
71      * Possible state transitions:
72      * NEW -> COMPLETING -> NORMAL
73      * NEW -> COMPLETING -> EXCEPTIONAL
74      * NEW -> CANCELLED
75      * NEW -> INTERRUPTING -> INTERRUPTED
76      */
77     private shared(int) state;
78     private enum int NEW          = 0;
79     private enum int COMPLETING   = 1;
80     private enum int NORMAL       = 2;
81     private enum int EXCEPTIONAL  = 3;
82     private enum int CANCELLED    = 4;
83     private enum int INTERRUPTING = 5;
84     private enum int INTERRUPTED  = 6;
85 
86     /** The underlying callable; nulled out after running */
87     private Callable!(V) callable;
88     /** The result to return or exception to throw from get() */
89     static if(!is(V == void)) {
90         private V outcome; // non-volatile, protected by state reads/writes
91     }
92     private Throwable exception;
93     /** The thread running the callable; CASed during run() */
94     private Thread runner;
95     /** Treiber stack of waiting threads */
96     private WaitNode waiters;
97 
98     /**
99      * Returns result or throws exception for completed task.
100      *
101      * @param s completed state value
102      */
103 
104     private V report(int s) {
105         // Object x = outcome;
106         if (s == NORMAL) {
107             static if(!is(V == void)) {
108                 return outcome; // cast(V)
109             } else {
110                 return ; // cast(V)
111             }
112         }
113             
114         if (s >= CANCELLED)
115             throw new CancellationException();
116         throw new ExecutionException(exception);
117     }
118 
119     /**
120      * Creates a {@code FutureTask} that will, upon running, execute the
121      * given {@code Callable}.
122      *
123      * @param  callable the callable task
124      * @throws NullPointerException if the callable is null
125      */
126     this(Callable!(V) callable) {
127         if (callable is null)
128             throw new NullPointerException();
129         this.callable = callable;
130         this.state = NEW;       // ensure visibility of callable
131     }
132 
133     /**
134      * Creates a {@code FutureTask} that will, upon running, execute the
135      * given {@code Runnable}, and arrange that {@code get} will return the
136      * given result on successful completion.
137      *
138      * @param runnable the runnable task
139      * @param result the result to return on successful completion. If
140      * you don't need a particular result, consider using
141      * constructions of the form:
142      * {@code Future<?> f = new FutureTask!(void)(runnable, null)}
143      * @throws NullPointerException if the runnable is null
144      */
145 static if(is(V == void)) {
146     this(Runnable runnable) {
147         this.callable = Executors.callable(runnable);
148         this.state = NEW;       // ensure visibility of callable
149     }
150 } else {
151     this(Runnable runnable, V result) {
152         this.callable = Executors.callable(runnable, result);
153         this.state = NEW;       // ensure visibility of callable
154     }
155 }
156 
157     bool isCancelled() {
158         return state >= CANCELLED;
159     }
160 
161     bool isDone() {
162         return state != NEW;
163     }
164 
165     bool cancel(bool mayInterruptIfRunning) {
166         if (!(state == NEW && AtomicHelper.compareAndSet(state, NEW,
167             mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
168             return false;
169         try {    // in case call to interrupt throws exception
170             if (mayInterruptIfRunning) {
171                 try {
172                     ThreadEx t = cast(ThreadEx) runner;
173                     if (t !is null)
174                         t.interrupt();
175                 } finally { // final state
176                     AtomicHelper.store(state, INTERRUPTED);
177                 }
178             }
179         } finally {
180             finishCompletion();
181         }
182         return true;
183     }
184 
185     /**
186      * @throws CancellationException {@inheritDoc}
187      */
188     V get() {
189         int s = state;
190         if (s <= COMPLETING)
191             s = awaitDone(false, Duration.zero);
192         return report(s);
193     }
194 
195     /**
196      * @throws CancellationException {@inheritDoc}
197      */
198     V get(Duration timeout) {
199         int s = state;
200         if (s <= COMPLETING &&
201             (s = awaitDone(true, timeout)) <= COMPLETING)
202             throw new TimeoutException();
203         return report(s);
204     }
205 
206     /**
207      * Protected method invoked when this task transitions to state
208      * {@code isDone} (whether normally or via cancellation). The
209      * default implementation does nothing.  Subclasses may override
210      * this method to invoke completion callbacks or perform
211      * bookkeeping. Note that you can query status inside the
212      * implementation of this method to determine whether this task
213      * has been cancelled.
214      */
215     protected void done() { }
216 
217     /**
218      * Sets the result of this future to the given value unless
219      * this future has already been set or has been cancelled.
220      *
221      * <p>This method is invoked internally by the {@link #run} method
222      * upon successful completion of the computation.
223      *
224      * @param v the value
225      */
226 
227 static if(is(V == void)) {
228     protected void set() {
229         if (AtomicHelper.compareAndSet(state, NEW, COMPLETING)) {
230             // outcome = v;
231             AtomicHelper.store(state, NORMAL);  // final state
232             finishCompletion();
233         }
234     }
235 
236     void run() {
237         if (state != NEW ||
238             !AtomicHelper.compareAndSet(runner, null, Thread.getThis()))
239             return;
240         try {
241             Callable!(V) c = callable;
242             if (c !is null && state == NEW) {
243                 bool ran;
244                 try {
245                     c.call();
246                     ran = true;
247                 } catch (Throwable ex) {
248                     ran = false;
249                     setException(ex);
250                 }
251                 if (ran)
252                     set();
253             }
254         } finally {
255             // runner must be non-null until state is settled to
256             // prevent concurrent calls to run()
257             runner = null;
258             // state must be re-read after nulling runner to prevent
259             // leaked interrupts
260             int s = state;
261             if (s >= INTERRUPTING)
262                 handlePossibleCancellationInterrupt(s);
263         }
264     }    
265 } else {
266     protected void set(V v) {
267         if (AtomicHelper.compareAndSet(state, NEW, COMPLETING)) {
268             outcome = v;
269             AtomicHelper.store(state, NORMAL);  // final state
270             finishCompletion();
271         }
272     }
273 
274     void run() {
275         if (state != NEW ||
276             !AtomicHelper.compareAndSet(runner, null, Thread.getThis()))
277             return;
278         try {
279             Callable!(V) c = callable;
280             if (c !is null && state == NEW) {
281                 V result;
282                 bool ran;
283                 try {
284                     result = c.call();
285                     ran = true;
286                 } catch (Throwable ex) {
287                     result = V.init;
288                     ran = false;
289                     setException(ex);
290                 }
291                 if (ran)
292                     set(result);
293             }
294         } finally {
295             // runner must be non-null until state is settled to
296             // prevent concurrent calls to run()
297             runner = null;
298             // state must be re-read after nulling runner to prevent
299             // leaked interrupts
300             int s = state;
301             if (s >= INTERRUPTING)
302                 handlePossibleCancellationInterrupt(s);
303         }
304     }    
305 }
306 
307     /**
308      * Causes this future to report an {@link ExecutionException}
309      * with the given throwable as its cause, unless this future has
310      * already been set or has been cancelled.
311      *
312      * <p>This method is invoked internally by the {@link #run} method
313      * upon failure of the computation.
314      *
315      * @param t the cause of failure
316      */
317     protected void setException(Throwable t) {
318         if (AtomicHelper.compareAndSet(state, NEW, COMPLETING)) {
319             exception = t;
320             AtomicHelper.store(state, EXCEPTIONAL); // final state
321             finishCompletion();
322         }
323     }
324 
325     /**
326      * Executes the computation without setting its result, and then
327      * resets this future to initial state, failing to do so if the
328      * computation encounters an exception or is cancelled.  This is
329      * designed for use with tasks that intrinsically execute more
330      * than once.
331      *
332      * @return {@code true} if successfully run and reset
333      */
334     protected bool runAndReset() {
335         if (state != NEW ||
336             !AtomicHelper.compareAndSet(runner, null, Thread.getThis()))
337             return false;
338         bool ran = false;
339         int s = state;
340         try {
341             Callable!(V) c = callable;
342             if (c !is null && s == NEW) {
343                 try {
344                     c.call(); // don't set result
345                     ran = true;
346                 } catch (Throwable ex) {
347                     setException(ex);
348                 }
349             }
350         } finally {
351             // runner must be non-null until state is settled to
352             // prevent concurrent calls to run()
353             runner = null;
354             // state must be re-read after nulling runner to prevent
355             // leaked interrupts
356             s = state;
357             if (s >= INTERRUPTING)
358                 handlePossibleCancellationInterrupt(s);
359         }
360         return ran && s == NEW;
361     }
362 
363     /**
364      * Ensures that any interrupt from a possible cancel(true) is only
365      * delivered to a task while in run or runAndReset.
366      */
367     private void handlePossibleCancellationInterrupt(int s) {
368         // It is possible for our interrupter to stall before getting a
369         // chance to interrupt us.  Let's spin-wait patiently.
370         if (s == INTERRUPTING)
371             while (state == INTERRUPTING)
372                 Thread.yield(); // wait out pending interrupt
373 
374         assert(state == INTERRUPTED);
375 
376         // We want to clear any interrupt we may have received from
377         // cancel(true).  However, it is permissible to use interrupts
378         // as an independent mechanism for a task to communicate with
379         // its caller, and there is no way to clear only the
380         // cancellation interrupt.
381         //
382         ThreadEx.interrupted();
383     }
384 
385     /**
386      * Simple linked list nodes to record waiting threads in a Treiber
387      * stack.  See other classes such as Phaser and SynchronousQueue
388      * for more detailed explanation.
389      */
390     static final class WaitNode {
391         Thread thread;
392         WaitNode next;
393         this() { thread = Thread.getThis(); }
394     }
395 
396     /**
397      * Removes and signals all waiting threads, invokes done(), and
398      * nulls out callable.
399      */
400     private void finishCompletion() {
401         // assert state > COMPLETING;
402         for (WaitNode q; (q = waiters) !is null;) {
403             if (AtomicHelper.compareAndSet(waiters, q, null)) {
404                 for (;;) {
405                     Thread t = q.thread;
406                     if (t !is null) {
407                         q.thread = null;
408                         LockSupport.unpark(t);
409                     }
410                     WaitNode next = q.next;
411                     if (next is null)
412                         break;
413                     q.next = null; // unlink to help gc
414                     q = next;
415                 }
416                 break;
417             }
418         }
419 
420         done();
421 
422         callable = null;        // to reduce footprint
423     }
424 
425     /**
426      * Awaits completion or aborts on interrupt or timeout.
427      *
428      * @param timed true if use timed waits
429      * @param duration time to wait, if timed
430      * @return state upon completion or at timeout
431      */
432     private int awaitDone(bool timed, Duration timeout) {
433         // The code below is very delicate, to achieve these goals:
434         // - call nanoTime exactly once for each call to park
435         // - if nanos <= 0L, return promptly without allocation or nanoTime
436         // - if nanos == Long.MIN_VALUE, don't underflow
437         // - if nanos == Long.MAX_VALUE, and nanoTime is non-monotonic
438         //   and we suffer a spurious wakeup, we will do no worse than
439         //   to park-spin for a while
440         MonoTime startTime = MonoTime.zero;    // Special value 0L means not yet parked
441         WaitNode q = null;
442         bool queued = false;
443         for (;;) {
444             int s = state;
445             if (s > COMPLETING) {
446                 if (q !is null)
447                     q.thread = null;
448                 return s;
449             } else if (s == COMPLETING) {
450                 // We may have already promised (via isDone) that we are done
451                 // so never return empty-handed or throw InterruptedException
452                 Thread.yield();
453             } else if (ThreadEx.interrupted()) {
454                 removeWaiter(q);
455                 throw new InterruptedException();
456             } else if (q is null) {
457                 if (timed && timeout <= Duration.zero)
458                     return s;
459                 q = new WaitNode();
460             } else if (!queued) {
461                 queued = AtomicHelper.compareAndSet!(WaitNode)(waiters, q.next = waiters, q);
462             } else if (timed) {
463                 Duration parkDuration;
464                 if (startTime == MonoTime.zero) { // first time
465                     startTime = MonoTime.currTime;
466                     if (startTime == MonoTime.zero)
467                         startTime = MonoTime(1);
468                     parkDuration = timeout;
469                 } else {                    
470                     Duration elapsed = MonoTime.currTime - startTime;
471                     if (elapsed >= timeout) {
472                         removeWaiter(q);
473                         return state;
474                     }
475                     parkDuration = timeout - elapsed;
476                 }
477                 // nanoTime may be slow; recheck before parking
478                 if (state < COMPLETING) {
479                     LockSupport.park(this, parkDuration);
480                 }
481             } else {
482                 LockSupport.park(this);
483             }
484         }
485     }
486 
487     /**
488      * Tries to unlink a timed-out or interrupted wait node to avoid
489      * accumulating garbage.  Internal nodes are simply unspliced
490      * without CAS since it is harmless if they are traversed anyway
491      * by releasers.  To avoid effects of unsplicing from already
492      * removed nodes, the list is retraversed in case of an apparent
493      * race.  This is slow when there are a lot of nodes, but we don't
494      * expect lists to be long enough to outweigh higher-overhead
495      * schemes.
496      */
497     private void removeWaiter(WaitNode node) {
498         if (node !is null) {
499             node.thread = null;
500             retry:
501             for (;;) {          // restart on removeWaiter race
502                 for (WaitNode pred = null, q = waiters, s; q !is null; q = s) {
503                     s = q.next;
504                     if (q.thread !is null)
505                         pred = q;
506                     else if (pred !is null) {
507                         pred.next = s;
508                         if (pred.thread is null) // check for race
509                             continue retry;
510                     }
511                     else if (!AtomicHelper.compareAndSet(waiters, q, s))
512                         continue retry;
513                 }
514                 break;
515             }
516         }
517     }
518 
519     /**
520      * Returns a string representation of this FutureTask.
521      *
522      * @implSpec
523      * The default implementation returns a string identifying this
524      * FutureTask, as well as its completion state.  The state, in
525      * brackets, contains one of the strings {@code "Completed Normally"},
526      * {@code "Completed Exceptionally"}, {@code "Cancelled"}, or {@code
527      * "Not completed"}.
528      *
529      * @return a string representation of this FutureTask
530      */
531     override string toString() {
532         string status;
533         switch (state) {
534         case NORMAL:
535             status = "[Completed normally]";
536             break;
537         case EXCEPTIONAL:
538             status = "[Completed exceptionally: " ~ exception.toString() ~ "]";
539             break;
540         case CANCELLED:
541         case INTERRUPTING:
542         case INTERRUPTED:
543             status = "[Cancelled]";
544             break;
545         default:
546             Callable!V callable = this.callable;
547             status = (callable is null)
548                 ? "[Not completed]"
549                 : "[Not completed, task = " ~ (cast(Object)callable).toString() ~ "]";
550         }
551         return super.toString() ~ status;
552     }
553 
554 }