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.Executors; 13 14 import hunt.concurrency.AbstractExecutorService; 15 import hunt.concurrency.atomic.AtomicHelper; 16 import hunt.concurrency.Delayed; 17 import hunt.concurrency.Exceptions; 18 import hunt.concurrency.ExecutorService; 19 // import hunt.concurrency.ForkJoinPool; 20 import hunt.concurrency.Future; 21 import hunt.concurrency.LinkedBlockingQueue; 22 import hunt.concurrency.ScheduledExecutorService; 23 import hunt.concurrency.ScheduledThreadPoolExecutor; 24 import hunt.concurrency.ThreadFactory; 25 import hunt.concurrency.ThreadPoolExecutor; 26 27 import hunt.collection.List; 28 import hunt.Exceptions; 29 import hunt.util.Common; 30 import hunt.util.DateTime; 31 32 import core.thread; 33 import core.time; 34 import std.conv; 35 36 // import static java.lang.ref.Reference.reachabilityFence; 37 // import java.security.AccessControlContext; 38 // import java.security.AccessControlException; 39 // import java.security.AccessController; 40 // import java.security.PrivilegedAction; 41 // import java.security.PrivilegedActionException; 42 // import java.security.PrivilegedExceptionAction; 43 // import hunt.collection.Collection; 44 // import java.util.List; 45 // import sun.security.util.SecurityConstants; 46 47 /** 48 * Factory and utility methods for {@link Executor}, {@link 49 * ExecutorService}, {@link ScheduledExecutorService}, {@link 50 * ThreadFactory}, and {@link Callable} classes defined in this 51 * package. This class supports the following kinds of methods: 52 * 53 * <ul> 54 * <li>Methods that create and return an {@link ExecutorService} 55 * set up with commonly useful configuration settings. 56 * <li>Methods that create and return a {@link ScheduledExecutorService} 57 * set up with commonly useful configuration settings. 58 * <li>Methods that create and return a "wrapped" ExecutorService, that 59 * disables reconfiguration by making implementation-specific methods 60 * inaccessible. 61 * <li>Methods that create and return a {@link ThreadFactory} 62 * that sets newly created threads to a known state. 63 * <li>Methods that create and return a {@link Callable} 64 * out of other closure-like forms, so they can be used 65 * in execution methods requiring {@code Callable}. 66 * </ul> 67 * 68 * @author Doug Lea 69 */ 70 class Executors { 71 72 /** 73 * Creates a thread pool that reuses a fixed number of threads 74 * operating off a shared unbounded queue. At any point, at most 75 * {@code nThreads} threads will be active processing tasks. 76 * If additional tasks are submitted when all threads are active, 77 * they will wait in the queue until a thread is available. 78 * If any thread terminates due to a failure during execution 79 * prior to shutdown, a new one will take its place if needed to 80 * execute subsequent tasks. The threads in the pool will exist 81 * until it is explicitly {@link ExecutorService#shutdown shutdown}. 82 * 83 * @param nThreads the number of threads in the pool 84 * @return the newly created thread pool 85 * @throws IllegalArgumentException if {@code nThreads <= 0} 86 */ 87 static ThreadPoolExecutor newFixedThreadPool(int nThreads) { 88 return new ThreadPoolExecutor(nThreads, nThreads, 0.hnsecs, 89 new LinkedBlockingQueue!(Runnable)()); 90 } 91 92 // /** 93 // * Creates a thread pool that maintains enough threads to support 94 // * the given parallelism level, and may use multiple queues to 95 // * reduce contention. The parallelism level corresponds to the 96 // * maximum number of threads actively engaged in, or available to 97 // * engage in, task processing. The actual number of threads may 98 // * grow and shrink dynamically. A work-stealing pool makes no 99 // * guarantees about the order in which submitted tasks are 100 // * executed. 101 // * 102 // * @param parallelism the targeted parallelism level 103 // * @return the newly created thread pool 104 // * @throws IllegalArgumentException if {@code parallelism <= 0} 105 // */ 106 // static ExecutorService newWorkStealingPool(int parallelism) { 107 // return new ForkJoinPool 108 // (parallelism, 109 // ForkJoinPool.defaultForkJoinWorkerThreadFactory, 110 // null, true); 111 // } 112 113 // /** 114 // * Creates a work-stealing thread pool using the number of 115 // * {@linkplain Runtime#availableProcessors available processors} 116 // * as its target parallelism level. 117 // * 118 // * @return the newly created thread pool 119 // * @see #newWorkStealingPool(int) 120 // */ 121 // static ExecutorService newWorkStealingPool() { 122 // return new ForkJoinPool 123 // (Runtime.getRuntime().availableProcessors(), 124 // ForkJoinPool.defaultForkJoinWorkerThreadFactory, 125 // null, true); 126 // } 127 128 /** 129 * Creates a thread pool that reuses a fixed number of threads 130 * operating off a shared unbounded queue, using the provided 131 * ThreadFactory to create new threads when needed. At any point, 132 * at most {@code nThreads} threads will be active processing 133 * tasks. If additional tasks are submitted when all threads are 134 * active, they will wait in the queue until a thread is 135 * available. If any thread terminates due to a failure during 136 * execution prior to shutdown, a new one will take its place if 137 * needed to execute subsequent tasks. The threads in the pool will 138 * exist until it is explicitly {@link ExecutorService#shutdown 139 * shutdown}. 140 * 141 * @param nThreads the number of threads in the pool 142 * @param threadFactory the factory to use when creating new threads 143 * @return the newly created thread pool 144 * @throws NullPointerException if threadFactory is null 145 * @throws IllegalArgumentException if {@code nThreads <= 0} 146 */ 147 static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { 148 return new ThreadPoolExecutor(nThreads, nThreads, 0.msecs, 149 new LinkedBlockingQueue!(Runnable)(), 150 threadFactory); 151 } 152 153 /** 154 * Creates an Executor that uses a single worker thread operating 155 * off an unbounded queue. (Note however that if this single 156 * thread terminates due to a failure during execution prior to 157 * shutdown, a new one will take its place if needed to execute 158 * subsequent tasks.) Tasks are guaranteed to execute 159 * sequentially, and no more than one task will be active at any 160 * given time. Unlike the otherwise equivalent 161 * {@code newFixedThreadPool(1)} the returned executor is 162 * guaranteed not to be reconfigurable to use additional threads. 163 * 164 * @return the newly created single-threaded Executor 165 */ 166 // static ExecutorService newSingleThreadExecutor() { 167 // return new FinalizableDelegatedExecutorService 168 // (new ThreadPoolExecutor(1, 1, 169 // 0L, TimeUnit.MILLISECONDS, 170 // new LinkedBlockingQueue!(Runnable)())); 171 // } 172 173 // /** 174 // * Creates an Executor that uses a single worker thread operating 175 // * off an unbounded queue, and uses the provided ThreadFactory to 176 // * create a new thread when needed. Unlike the otherwise 177 // * equivalent {@code newFixedThreadPool(1, threadFactory)} the 178 // * returned executor is guaranteed not to be reconfigurable to use 179 // * additional threads. 180 // * 181 // * @param threadFactory the factory to use when creating new threads 182 // * @return the newly created single-threaded Executor 183 // * @throws NullPointerException if threadFactory is null 184 // */ 185 // static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { 186 // return new FinalizableDelegatedExecutorService 187 // (new ThreadPoolExecutor(1, 1, 188 // 0L, TimeUnit.MILLISECONDS, 189 // new LinkedBlockingQueue!(Runnable)(), 190 // threadFactory)); 191 // } 192 193 /** 194 * Creates a thread pool that creates new threads as needed, but 195 * will reuse previously constructed threads when they are 196 * available. These pools will typically improve the performance 197 * of programs that execute many short-lived asynchronous tasks. 198 * Calls to {@code execute} will reuse previously constructed 199 * threads if available. If no existing thread is available, a new 200 * thread will be created and added to the pool. Threads that have 201 * not been used for sixty seconds are terminated and removed from 202 * the cache. Thus, a pool that remains idle for long enough will 203 * not consume any resources. Note that pools with similar 204 * properties but different details (for example, timeout parameters) 205 * may be created using {@link ThreadPoolExecutor} constructors. 206 * 207 * @return the newly created thread pool 208 */ 209 // static ExecutorService newCachedThreadPool() { 210 // return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 211 // 60L, TimeUnit.SECONDS, 212 // new SynchronousQueue!(Runnable)()); 213 // } 214 215 // /** 216 // * Creates a thread pool that creates new threads as needed, but 217 // * will reuse previously constructed threads when they are 218 // * available, and uses the provided 219 // * ThreadFactory to create new threads when needed. 220 // * 221 // * @param threadFactory the factory to use when creating new threads 222 // * @return the newly created thread pool 223 // * @throws NullPointerException if threadFactory is null 224 // */ 225 // static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { 226 // return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 227 // 60L, TimeUnit.SECONDS, 228 // new SynchronousQueue!(Runnable)(), 229 // threadFactory); 230 // } 231 232 /** 233 * Creates a single-threaded executor that can schedule commands 234 * to run after a given delay, or to execute periodically. 235 * (Note however that if this single 236 * thread terminates due to a failure during execution prior to 237 * shutdown, a new one will take its place if needed to execute 238 * subsequent tasks.) Tasks are guaranteed to execute 239 * sequentially, and no more than one task will be active at any 240 * given time. Unlike the otherwise equivalent 241 * {@code newScheduledThreadPool(1)} the returned executor is 242 * guaranteed not to be reconfigurable to use additional threads. 243 * 244 * @return the newly created scheduled executor 245 */ 246 static ScheduledExecutorService newSingleThreadScheduledExecutor() { 247 return new DelegatedScheduledExecutorService!ScheduledThreadPoolExecutor 248 (new ScheduledThreadPoolExecutor(1)); 249 } 250 251 /** 252 * Creates a single-threaded executor that can schedule commands 253 * to run after a given delay, or to execute periodically. (Note 254 * however that if this single thread terminates due to a failure 255 * during execution prior to shutdown, a new one will take its 256 * place if needed to execute subsequent tasks.) Tasks are 257 * guaranteed to execute sequentially, and no more than one task 258 * will be active at any given time. Unlike the otherwise 259 * equivalent {@code newScheduledThreadPool(1, threadFactory)} 260 * the returned executor is guaranteed not to be reconfigurable to 261 * use additional threads. 262 * 263 * @param threadFactory the factory to use when creating new threads 264 * @return the newly created scheduled executor 265 * @throws NullPointerException if threadFactory is null 266 */ 267 static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) { 268 return new DelegatedScheduledExecutorService!ScheduledThreadPoolExecutor 269 (new ScheduledThreadPoolExecutor(1, threadFactory)); 270 } 271 272 /** 273 * Creates a thread pool that can schedule commands to run after a 274 * given delay, or to execute periodically. 275 * @param corePoolSize the number of threads to keep in the pool, 276 * even if they are idle 277 * @return the newly created scheduled thread pool 278 * @throws IllegalArgumentException if {@code corePoolSize < 0} 279 */ 280 static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { 281 return new ScheduledThreadPoolExecutor(corePoolSize); 282 } 283 284 /** 285 * Creates a thread pool that can schedule commands to run after a 286 * given delay, or to execute periodically. 287 * @param corePoolSize the number of threads to keep in the pool, 288 * even if they are idle 289 * @param threadFactory the factory to use when the executor 290 * creates a new thread 291 * @return the newly created scheduled thread pool 292 * @throws IllegalArgumentException if {@code corePoolSize < 0} 293 * @throws NullPointerException if threadFactory is null 294 */ 295 static ScheduledExecutorService newScheduledThreadPool( 296 int corePoolSize, ThreadFactory threadFactory) { 297 return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); 298 } 299 300 // /** 301 // * Returns an object that delegates all defined {@link 302 // * ExecutorService} methods to the given executor, but not any 303 // * other methods that might otherwise be accessible using 304 // * casts. This provides a way to safely "freeze" configuration and 305 // * disallow tuning of a given concrete implementation. 306 // * @param executor the underlying implementation 307 // * @return an {@code ExecutorService} instance 308 // * @throws NullPointerException if executor null 309 // */ 310 // static ExecutorService unconfigurableExecutorService(ExecutorService executor) { 311 // if (executor is null) 312 // throw new NullPointerException(); 313 // return new DelegatedExecutorService(executor); 314 // } 315 316 // /** 317 // * Returns an object that delegates all defined {@link 318 // * ScheduledExecutorService} methods to the given executor, but 319 // * not any other methods that might otherwise be accessible using 320 // * casts. This provides a way to safely "freeze" configuration and 321 // * disallow tuning of a given concrete implementation. 322 // * @param executor the underlying implementation 323 // * @return a {@code ScheduledExecutorService} instance 324 // * @throws NullPointerException if executor null 325 // */ 326 // static ScheduledExecutorService unconfigurableScheduledExecutorService(ScheduledExecutorService executor) { 327 // if (executor is null) 328 // throw new NullPointerException(); 329 // return new DelegatedScheduledExecutorService(executor); 330 // } 331 332 /** 333 * Returns a default thread factory used to create new threads. 334 * This factory creates all new threads used by an Executor in the 335 * same {@link ThreadGroupEx}. If there is a {@link 336 * java.lang.SecurityManager}, it uses the group of {@link 337 * System#getSecurityManager}, else the group of the thread 338 * invoking this {@code defaultThreadFactory} method. Each new 339 * thread is created as a non-daemon thread with priority set to 340 * the smaller of {@code Thread.PRIORITY_DEFAULT} and the maximum 341 * priority permitted in the thread group. New threads have names 342 * accessible via {@link Thread#getName} of 343 * <em>pool-N-thread-M</em>, where <em>N</em> is the sequence 344 * number of this factory, and <em>M</em> is the sequence number 345 * of the thread created by this factory. 346 * @return a thread factory 347 */ 348 static ThreadFactory defaultThreadFactory() { 349 return ThreadFactory.defaultThreadFactory(); 350 } 351 352 // /** 353 // * Returns a thread factory used to create new threads that 354 // * have the same permissions as the current thread. 355 // * This factory creates threads with the same settings as {@link 356 // * Executors#defaultThreadFactory}, additionally setting the 357 // * AccessControlContext and contextClassLoader of new threads to 358 // * be the same as the thread invoking this 359 // * {@code privilegedThreadFactory} method. A new 360 // * {@code privilegedThreadFactory} can be created within an 361 // * {@link AccessController#doPrivileged AccessController.doPrivileged} 362 // * action setting the current thread's access control context to 363 // * create threads with the selected permission settings holding 364 // * within that action. 365 // * 366 // * <p>Note that while tasks running within such threads will have 367 // * the same access control and class loader settings as the 368 // * current thread, they need not have the same {@link 369 // * java.lang.ThreadLocal} or {@link 370 // * java.lang.InheritableThreadLocal} values. If necessary, 371 // * particular values of thread locals can be set or reset before 372 // * any task runs in {@link ThreadPoolExecutor} subclasses using 373 // * {@link ThreadPoolExecutor#beforeExecute(Thread, Runnable)}. 374 // * Also, if it is necessary to initialize worker threads to have 375 // * the same InheritableThreadLocal settings as some other 376 // * designated thread, you can create a custom ThreadFactory in 377 // * which that thread waits for and services requests to create 378 // * others that will inherit its values. 379 // * 380 // * @return a thread factory 381 // * @throws AccessControlException if the current access control 382 // * context does not have permission to both get and set context 383 // * class loader 384 // */ 385 // static ThreadFactory privilegedThreadFactory() { 386 // return new PrivilegedThreadFactory(); 387 // } 388 389 /** 390 * Returns a {@link Callable} object that, when 391 * called, runs the given task and returns the given result. This 392 * can be useful when applying methods requiring a 393 * {@code Callable} to an otherwise resultless action. 394 * @param task the task to run 395 * @param result the result to return 396 * @param (T) the type of the result 397 * @return a callable object 398 * @throws NullPointerException if task null 399 */ 400 static Callable!(void) callable(Runnable task) { 401 if (task is null) 402 throw new NullPointerException(); 403 return new RunnableAdapter!(void)(task); 404 } 405 406 static Callable!(T) callable(T)(Runnable task, T result) if(!is(T == void)) { 407 if (task is null) 408 throw new NullPointerException(); 409 return new RunnableAdapter!(T)(task, result); 410 } 411 412 /** 413 * Returns a {@link Callable} object that, when 414 * called, runs the given task and returns {@code null}. 415 * @param task the task to run 416 * @return a callable object 417 * @throws NullPointerException if task null 418 */ 419 // static Callable!(Object) callable(Runnable task) { 420 // if (task is null) 421 // throw new NullPointerException(); 422 // return new RunnableAdapter!(Object)(task, null); 423 // } 424 425 // /** 426 // * Returns a {@link Callable} object that, when 427 // * called, runs the given privileged action and returns its result. 428 // * @param action the privileged action to run 429 // * @return a callable object 430 // * @throws NullPointerException if action null 431 // */ 432 // static Callable!(Object) callable(PrivilegedAction<?> action) { 433 // if (action is null) 434 // throw new NullPointerException(); 435 // return new Callable!(Object)() { 436 // Object call() { return action.run(); }}; 437 // } 438 439 // /** 440 // * Returns a {@link Callable} object that, when 441 // * called, runs the given privileged exception action and returns 442 // * its result. 443 // * @param action the privileged exception action to run 444 // * @return a callable object 445 // * @throws NullPointerException if action null 446 // */ 447 // static Callable!(Object) callable(PrivilegedExceptionAction<?> action) { 448 // if (action is null) 449 // throw new NullPointerException(); 450 // return new Callable!(Object)() { 451 // Object call() throws Exception { return action.run(); }}; 452 // } 453 454 // /** 455 // * Returns a {@link Callable} object that will, when called, 456 // * execute the given {@code callable} under the current access 457 // * control context. This method should normally be invoked within 458 // * an {@link AccessController#doPrivileged AccessController.doPrivileged} 459 // * action to create callables that will, if possible, execute 460 // * under the selected permission settings holding within that 461 // * action; or if not possible, throw an associated {@link 462 // * AccessControlException}. 463 // * @param callable the underlying task 464 // * @param (T) the type of the callable's result 465 // * @return a callable object 466 // * @throws NullPointerException if callable null 467 // */ 468 // static !(T) Callable!(T) privilegedCallable(Callable!(T) callable) { 469 // if (callable is null) 470 // throw new NullPointerException(); 471 // return new PrivilegedCallable!(T)(callable); 472 // } 473 474 // /** 475 // * Returns a {@link Callable} object that will, when called, 476 // * execute the given {@code callable} under the current access 477 // * control context, with the current context class loader as the 478 // * context class loader. This method should normally be invoked 479 // * within an 480 // * {@link AccessController#doPrivileged AccessController.doPrivileged} 481 // * action to create callables that will, if possible, execute 482 // * under the selected permission settings holding within that 483 // * action; or if not possible, throw an associated {@link 484 // * AccessControlException}. 485 // * 486 // * @param callable the underlying task 487 // * @param (T) the type of the callable's result 488 // * @return a callable object 489 // * @throws NullPointerException if callable null 490 // * @throws AccessControlException if the current access control 491 // * context does not have permission to both set and get context 492 // * class loader 493 // */ 494 // static !(T) Callable!(T) privilegedCallableUsingCurrentClassLoader(Callable!(T) callable) { 495 // if (callable is null) 496 // throw new NullPointerException(); 497 // return new PrivilegedCallableUsingCurrentClassLoader!(T)(callable); 498 // } 499 500 501 // Methods for ExecutorService 502 503 /** 504 * Submits a Runnable task for execution and returns a Future 505 * representing that task. The Future's {@code get} method will 506 * return {@code null} upon <em>successful</em> completion. 507 * 508 * @param task the task to submit 509 * @return a Future representing pending completion of the task 510 * @throws RejectedExecutionException if the task cannot be 511 * scheduled for execution 512 * @throws NullPointerException if the task is null 513 */ 514 static Future!(void) submit(ExecutorService es, Runnable task) { 515 516 AbstractExecutorService aes = cast(AbstractExecutorService)es; 517 if(aes is null) 518 throw new RejectedExecutionException("ExecutorService is null"); 519 else 520 return aes.submit(task); 521 522 // TypeInfo typeInfo = typeid(cast(Object)es); 523 // if(typeInfo == typeid(ThreadPoolExecutor)) { 524 // AbstractExecutorService aes = cast(AbstractExecutorService)es; 525 // return aes.submit(task); 526 // } else { 527 // implementationMissing(false); 528 // } 529 } 530 531 /** 532 * Submits a Runnable task for execution and returns a Future 533 * representing that task. The Future's {@code get} method will 534 * return the given result upon successful completion. 535 * 536 * @param task the task to submit 537 * @param result the result to return 538 * @param (T) the type of the result 539 * @return a Future representing pending completion of the task 540 * @throws RejectedExecutionException if the task cannot be 541 * scheduled for execution 542 * @throws NullPointerException if the task is null 543 */ 544 static Future!(T) submit(T)(ExecutorService es, Runnable task, T result) { 545 AbstractExecutorService aes = cast(AbstractExecutorService)es; 546 if(aes is null) 547 throw new RejectedExecutionException("ExecutorService is null"); 548 else 549 return aes.submit!T(task, result); 550 551 // TypeInfo typeInfo = typeid(cast(Object)es); 552 // if(typeInfo == typeid(ThreadPoolExecutor)) { 553 // AbstractExecutorService aes = cast(AbstractExecutorService)es; 554 // if(aes is null) 555 // throw new RejectedExecutionException("ExecutorService is null"); 556 // else 557 // return aes.submit!T(task, result); 558 // } else { 559 // implementationMissing(false); 560 // } 561 } 562 563 /** 564 * Submits a value-returning task for execution and returns a 565 * Future representing the pending results of the task. The 566 * Future's {@code get} method will return the task's result upon 567 * successful completion. 568 * 569 * <p> 570 * If you would like to immediately block waiting 571 * for a task, you can use constructions of the form 572 * {@code result = exec.submit(aCallable).get();} 573 * 574 * <p>Note: The {@link Executors} class includes a set of methods 575 * that can convert some other common closure-like objects, 576 * for example, {@link java.security.PrivilegedAction} to 577 * {@link Callable} form so they can be submitted. 578 * 579 * @param task the task to submit 580 * @param (T) the type of the task's result 581 * @return a Future representing pending completion of the task 582 * @throws RejectedExecutionException if the task cannot be 583 * scheduled for execution 584 * @throws NullPointerException if the task is null 585 */ 586 static Future!(T) submit(T)(ExecutorService es, Callable!(T) task) { 587 AbstractExecutorService aes = cast(AbstractExecutorService)es; 588 if(aes is null) 589 throw new RejectedExecutionException("ExecutorService is null"); 590 else 591 return aes.submit!(T)(task); 592 593 // TypeInfo typeInfo = typeid(cast(Object)es); 594 // if(typeInfo == typeid(ThreadPoolExecutor)) { 595 // AbstractExecutorService aes = cast(AbstractExecutorService)es; 596 // if(aes is null) 597 // throw new RejectedExecutionException("ExecutorService is null"); 598 // else 599 // return aes.submit!(T)(task); 600 // } else { 601 // implementationMissing(false); 602 // } 603 } 604 605 /** 606 * Executes the given tasks, returning a list of Futures holding 607 * their status and results when all complete. 608 * {@link Future#isDone} is {@code true} for each 609 * element of the returned list. 610 * Note that a <em>completed</em> task could have 611 * terminated either normally or by throwing an exception. 612 * The results of this method are undefined if the given 613 * collection is modified while this operation is in progress. 614 * 615 * @param tasks the collection of tasks 616 * @param (T) the type of the values returned from the tasks 617 * @return a list of Futures representing the tasks, in the same 618 * sequential order as produced by the iterator for the 619 * given task list, each of which has completed 620 * @throws InterruptedException if interrupted while waiting, in 621 * which case unfinished tasks are cancelled 622 * @throws NullPointerException if tasks or any of its elements are {@code null} 623 * @throws RejectedExecutionException if any task cannot be 624 * scheduled for execution 625 */ 626 static List!(Future!(T)) invokeAll(T)(ExecutorService es, Collection!(Callable!(T)) tasks) { 627 628 AbstractExecutorService aes = cast(AbstractExecutorService)es; 629 if(aes is null) 630 throw new RejectedExecutionException("ExecutorService is null"); 631 else { 632 aes.invokeAll!(T)(tasks); 633 } 634 635 } 636 637 /** 638 * Executes the given tasks, returning a list of Futures holding 639 * their status and results 640 * when all complete or the timeout expires, whichever happens first. 641 * {@link Future#isDone} is {@code true} for each 642 * element of the returned list. 643 * Upon return, tasks that have not completed are cancelled. 644 * Note that a <em>completed</em> task could have 645 * terminated either normally or by throwing an exception. 646 * The results of this method are undefined if the given 647 * collection is modified while this operation is in progress. 648 * 649 * @param tasks the collection of tasks 650 * @param timeout the maximum time to wait 651 * @param unit the time unit of the timeout argument 652 * @param (T) the type of the values returned from the tasks 653 * @return a list of Futures representing the tasks, in the same 654 * sequential order as produced by the iterator for the 655 * given task list. If the operation did not time out, 656 * each task will have completed. If it did time out, some 657 * of these tasks will not have completed. 658 * @throws InterruptedException if interrupted while waiting, in 659 * which case unfinished tasks are cancelled 660 * @throws NullPointerException if tasks, any of its elements, or 661 * unit are {@code null} 662 * @throws RejectedExecutionException if any task cannot be scheduled 663 * for execution 664 */ 665 static List!(Future!(T)) invokeAll(T)(ExecutorService es, Collection!(Callable!(T)) tasks, 666 Duration timeout) { 667 AbstractExecutorService aes = cast(AbstractExecutorService)es; 668 if(aes is null) 669 throw new RejectedExecutionException("ExecutorService is null"); 670 else { 671 aes.invokeAll!(T)(tasks, timeout); 672 } 673 } 674 675 /** 676 * Executes the given tasks, returning the result 677 * of one that has completed successfully (i.e., without throwing 678 * an exception), if any do. Upon normal or exceptional return, 679 * tasks that have not completed are cancelled. 680 * The results of this method are undefined if the given 681 * collection is modified while this operation is in progress. 682 * 683 * @param tasks the collection of tasks 684 * @param (T) the type of the values returned from the tasks 685 * @return the result returned by one of the tasks 686 * @throws InterruptedException if interrupted while waiting 687 * @throws NullPointerException if tasks or any element task 688 * subject to execution is {@code null} 689 * @throws IllegalArgumentException if tasks is empty 690 * @throws ExecutionException if no task successfully completes 691 * @throws RejectedExecutionException if tasks cannot be scheduled 692 * for execution 693 */ 694 static T invokeAny(T)(ExecutorService es, Collection!(Callable!(T)) tasks) { 695 AbstractExecutorService aes = cast(AbstractExecutorService)es; 696 if(aes is null) 697 throw new RejectedExecutionException("ExecutorService is null"); 698 else { 699 aes.invokeAny!(T)(tasks); 700 } 701 } 702 703 /** 704 * Executes the given tasks, returning the result 705 * of one that has completed successfully (i.e., without throwing 706 * an exception), if any do before the given timeout elapses. 707 * Upon normal or exceptional return, tasks that have not 708 * completed are cancelled. 709 * The results of this method are undefined if the given 710 * collection is modified while this operation is in progress. 711 * 712 * @param tasks the collection of tasks 713 * @param timeout the maximum time to wait 714 * @param unit the time unit of the timeout argument 715 * @param (T) the type of the values returned from the tasks 716 * @return the result returned by one of the tasks 717 * @throws InterruptedException if interrupted while waiting 718 * @throws NullPointerException if tasks, or unit, or any element 719 * task subject to execution is {@code null} 720 * @throws TimeoutException if the given timeout elapses before 721 * any task successfully completes 722 * @throws ExecutionException if no task successfully completes 723 * @throws RejectedExecutionException if tasks cannot be scheduled 724 * for execution 725 */ 726 static T invokeAny(T)(ExecutorService es, Collection!(Callable!(T)) tasks, 727 Duration timeout) { 728 AbstractExecutorService aes = cast(AbstractExecutorService)es; 729 if(aes is null) 730 throw new RejectedExecutionException("ExecutorService is null"); 731 else { 732 aes.invokeAny!(T)(tasks, timeout); 733 } 734 } 735 736 /** Cannot instantiate. */ 737 private this() {} 738 } 739 740 // Non-classes supporting the methods 741 742 /** 743 * A callable that runs given task and returns given result. 744 */ 745 private final class RunnableAdapter(T) : Callable!(T) if(is(T == void)) { 746 private Runnable task; 747 this(Runnable task) { 748 this.task = task; 749 } 750 751 T call() { 752 task.run(); 753 } 754 755 override string toString() { 756 return super.toString() ~ "[Wrapped task = " ~ (cast(Object)task).toString() ~ "]"; 757 } 758 } 759 760 private final class RunnableAdapter(T) : Callable!(T) if(!is(T == void)) { 761 private Runnable task; 762 private T result; 763 764 this(Runnable task, T result) { 765 this.task = task; 766 this.result = result; 767 } 768 769 T call() { 770 task.run(); 771 return result; 772 } 773 774 override string toString() { 775 return super.toString() ~ "[Wrapped task = " ~ (cast(Object)task).toString() ~ "]"; 776 } 777 } 778 779 // /** 780 // * A callable that runs under established access control settings. 781 // */ 782 // private final class PrivilegedCallable!(T) : Callable!(T) { 783 // Callable!(T) task; 784 // AccessControlContext acc; 785 786 // PrivilegedCallable(Callable!(T) task) { 787 // this.task = task; 788 // this.acc = AccessController.getContext(); 789 // } 790 791 // T call() throws Exception { 792 // try { 793 // return AccessController.doPrivileged( 794 // new PrivilegedExceptionAction!(T)() { 795 // T run() throws Exception { 796 // return task.call(); 797 // } 798 // }, acc); 799 // } catch (PrivilegedActionException e) { 800 // throw e.getException(); 801 // } 802 // } 803 804 // string toString() { 805 // return super.toString() ~ "[Wrapped task = " ~ task ~ "]"; 806 // } 807 // } 808 809 // /** 810 // * A callable that runs under established access control settings and 811 // * current ClassLoader. 812 // */ 813 // private final class PrivilegedCallableUsingCurrentClassLoader(T) 814 // : Callable!(T) { 815 // Callable!(T) task; 816 // AccessControlContext acc; 817 // ClassLoader ccl; 818 819 // this(Callable!(T) task) { 820 // SecurityManager sm = System.getSecurityManager(); 821 // if (sm !is null) { 822 // // Calls to getContextClassLoader from this class 823 // // never trigger a security check, but we check 824 // // whether our callers have this permission anyways. 825 // sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); 826 827 // // Whether setContextClassLoader turns out to be necessary 828 // // or not, we fail fast if permission is not available. 829 // sm.checkPermission(new RuntimePermission("setContextClassLoader")); 830 // } 831 // this.task = task; 832 // this.acc = AccessController.getContext(); 833 // this.ccl = Thread.getThis().getContextClassLoader(); 834 // } 835 836 // T call() throws Exception { 837 // try { 838 // return AccessController.doPrivileged( 839 // new PrivilegedExceptionAction!(T)() { 840 // T run() throws Exception { 841 // Thread t = Thread.getThis(); 842 // ClassLoader cl = t.getContextClassLoader(); 843 // if (ccl == cl) { 844 // return task.call(); 845 // } else { 846 // t.setContextClassLoader(ccl); 847 // try { 848 // return task.call(); 849 // } finally { 850 // t.setContextClassLoader(cl); 851 // } 852 // } 853 // } 854 // }, acc); 855 // } catch (PrivilegedActionException e) { 856 // throw e.getException(); 857 // } 858 // } 859 860 // string toString() { 861 // return super.toString() ~ "[Wrapped task = " ~ task ~ "]"; 862 // } 863 // } 864 865 void reachabilityFence(ExecutorService) { 866 // do nothing; 867 // TODO: Tasks pending completion -@zxp at 5/10/2019, 10:50:31 AM 868 // remove this 869 } 870 871 /** 872 * A wrapper class that exposes only the ExecutorService methods 873 * of an ExecutorService implementation. 874 */ 875 private class DelegatedExecutorService(U) : ExecutorService 876 if(is(U : ExecutorService)) { 877 878 private U e; 879 880 this(U executor) { e = executor; } 881 882 void execute(Runnable command) { 883 try { 884 e.execute(command); 885 } finally { reachabilityFence(this); } 886 } 887 888 void shutdown() { e.shutdown(); } 889 890 List!(Runnable) shutdownNow() { 891 try { 892 return e.shutdownNow(); 893 } finally { reachabilityFence(this); } 894 } 895 896 bool isShutdown() { 897 try { 898 return e.isShutdown(); 899 } finally { reachabilityFence(this); } 900 } 901 902 bool isTerminated() { 903 try { 904 return e.isTerminated(); 905 } finally { reachabilityFence(this); } 906 } 907 908 bool awaitTermination(Duration timeout) { 909 try { 910 return e.awaitTermination(timeout); 911 } finally { reachabilityFence(this); } 912 } 913 914 Future!void submit(Runnable task) { 915 try { 916 return e.submit(task); 917 } finally { reachabilityFence(this); } 918 } 919 920 Future!(T) submit(T)(Callable!(T) task) { 921 try { 922 return e.submit(task); 923 } finally { reachabilityFence(this); } 924 } 925 926 Future!(T) submit(T)(Runnable task, T result) { 927 try { 928 return e.submit(task, result); 929 } finally { reachabilityFence(this); } 930 } 931 932 List!(Future!(T)) invokeAll(T)(Collection!(Callable!(T)) tasks) { 933 try { 934 return e.invokeAll(tasks); 935 } finally { reachabilityFence(this); } 936 } 937 938 List!(Future!(T)) invokeAll(T)(Collection!(Callable!(T)) tasks, 939 Duration timeout) { 940 try { 941 return e.invokeAll(tasks, timeout, unit); 942 } finally { reachabilityFence(this); } 943 } 944 945 T invokeAny(T)(Collection!(Callable!(T)) tasks) { 946 try { 947 return e.invokeAny(tasks); 948 } finally { reachabilityFence(this); } 949 } 950 951 T invokeAny(T)(Collection!(Callable!(T)) tasks, 952 Duration timeout) { 953 try { 954 return e.invokeAny(tasks, timeout, unit); 955 } finally { reachabilityFence(this); } 956 } 957 } 958 959 private class FinalizableDelegatedExecutorService(T) : DelegatedExecutorService!T { 960 this(T executor) { 961 super(executor); 962 } 963 964 protected void finalize() { 965 super.shutdown(); 966 } 967 } 968 969 /** 970 * A wrapper class that exposes only the ScheduledExecutorService 971 * methods of a ScheduledExecutorService implementation. 972 */ 973 private class DelegatedScheduledExecutorService(T) : DelegatedExecutorService!T, 974 ScheduledExecutorService if(is(T : ScheduledExecutorService)){ 975 976 private T e; 977 978 this(T executor) { 979 super(executor); 980 e = executor; 981 } 982 983 ScheduledFuture!void schedule(Runnable command, Duration delay) { 984 return e.schedule(command, delay); 985 } 986 987 ScheduledFuture!(V) schedule(V)(Callable!(V) callable, Duration delay) { 988 return e.schedule!V(callable, delay); 989 } 990 991 ScheduledFuture!void scheduleAtFixedRate(Runnable command, Duration initialDelay, Duration period) { 992 return e.scheduleAtFixedRate(command, initialDelay, period); 993 } 994 995 ScheduledFuture!void scheduleWithFixedDelay(Runnable command, Duration initialDelay, Duration delay) { 996 return e.scheduleWithFixedDelay(command, initialDelay, delay); 997 } 998 }