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 }