ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/ForkJoinTask.java
(Generate patch)

Comparing jsr166/src/main/java/util/concurrent/ForkJoinTask.java (file contents):
Revision 1.5 by jsr166, Fri Jul 31 20:41:13 2009 UTC vs.
Revision 1.6 by jsr166, Mon Aug 3 01:18:07 2009 UTC

# Line 14 | Line 14 | import java.util.Map;
14   import java.util.WeakHashMap;
15  
16   /**
17 < * Abstract base class for tasks that run within a {@link
18 < * ForkJoinPool}.  A ForkJoinTask is a thread-like entity that is much
17 > * Abstract base class for tasks that run within a {@link ForkJoinPool}.
18 > * A {@code ForkJoinTask} is a thread-like entity that is much
19   * lighter weight than a normal thread.  Huge numbers of tasks and
20   * subtasks may be hosted by a small number of actual threads in a
21   * ForkJoinPool, at the price of some usage limitations.
22   *
23 < * <p> A "main" ForkJoinTask begins execution when submitted to a
24 < * {@link ForkJoinPool}. Once started, it will usually in turn start
25 < * other subtasks.  As indicated by the name of this class, many
26 < * programs using ForkJoinTasks employ only methods {@code fork} and
27 < * {@code join}, or derivatives such as {@code invokeAll}.  However,
28 < * this class also provides a number of other methods that can come
29 < * into play in advanced usages, as well as extension mechanics that
30 < * allow support of new forms of fork/join processing.
23 > * <p>A "main" {@code ForkJoinTask} begins execution when submitted
24 > * to a {@link ForkJoinPool}.  Once started, it will usually in turn
25 > * start other subtasks.  As indicated by the name of this class,
26 > * many programs using {@code ForkJoinTask} employ only methods
27 > * {@link #fork} and {@link #join}, or derivatives such as {@link
28 > * #invokeAll}.  However, this class also provides a number of other
29 > * methods that can come into play in advanced usages, as well as
30 > * extension mechanics that allow support of new forms of fork/join
31 > * processing.
32   *
33 < * <p>A ForkJoinTask is a lightweight form of {@link Future}.  The
34 < * efficiency of ForkJoinTasks stems from a set of restrictions (that
35 < * are only partially statically enforceable) reflecting their
36 < * intended use as computational tasks calculating pure functions or
37 < * operating on purely isolated objects.  The primary coordination
38 < * mechanisms are {@link #fork}, that arranges asynchronous execution,
39 < * and {@link #join}, that doesn't proceed until the task's result has
40 < * been computed.  Computations should avoid {@code synchronized}
41 < * methods or blocks, and should minimize other blocking
42 < * synchronization apart from joining other tasks or using
43 < * synchronizers such as Phasers that are advertised to cooperate with
44 < * fork/join scheduling. Tasks should also not perform blocking IO,
45 < * and should ideally access variables that are completely independent
46 < * of those accessed by other running tasks. Minor breaches of these
47 < * restrictions, for example using shared output streams, may be
48 < * tolerable in practice, but frequent use may result in poor
49 < * performance, and the potential to indefinitely stall if the number
50 < * of threads not waiting for IO or other external synchronization
51 < * becomes exhausted. This usage restriction is in part enforced by
52 < * not permitting checked exceptions such as {@code IOExceptions}
53 < * to be thrown. However, computations may still encounter unchecked
54 < * exceptions, that are rethrown to callers attempting join
55 < * them. These exceptions may additionally include
56 < * RejectedExecutionExceptions stemming from internal resource
57 < * exhaustion such as failure to allocate internal task queues.
33 > * <p>A {@code ForkJoinTask} is a lightweight form of {@link Future}.
34 > * The efficiency of {@code ForkJoinTask}s stems from a set of
35 > * restrictions (that are only partially statically enforceable)
36 > * reflecting their intended use as computational tasks calculating
37 > * pure functions or operating on purely isolated objects.  The
38 > * primary coordination mechanisms are {@link #fork}, that arranges
39 > * asynchronous execution, and {@link #join}, that doesn't proceed
40 > * until the task's result has been computed.  Computations should
41 > * avoid {@code synchronized} methods or blocks, and should minimize
42 > * other blocking synchronization apart from joining other tasks or
43 > * using synchronizers such as Phasers that are advertised to
44 > * cooperate with fork/join scheduling. Tasks should also not perform
45 > * blocking IO, and should ideally access variables that are
46 > * completely independent of those accessed by other running
47 > * tasks. Minor breaches of these restrictions, for example using
48 > * shared output streams, may be tolerable in practice, but frequent
49 > * use may result in poor performance, and the potential to
50 > * indefinitely stall if the number of threads not waiting for IO or
51 > * other external synchronization becomes exhausted. This usage
52 > * restriction is in part enforced by not permitting checked
53 > * exceptions such as {@code IOExceptions} to be thrown. However,
54 > * computations may still encounter unchecked exceptions, that are
55 > * rethrown to callers attempting join them. These exceptions may
56 > * additionally include RejectedExecutionExceptions stemming from
57 > * internal resource exhaustion such as failure to allocate internal
58 > * task queues.
59   *
60   * <p>The primary method for awaiting completion and extracting
61   * results of a task is {@link #join}, but there are several variants:
# Line 72 | Line 74 | import java.util.WeakHashMap;
74   * performs the most common form of parallel invocation: forking a set
75   * of tasks and joining them all.
76   *
77 < * <p> The ForkJoinTask class is not usually directly subclassed.
77 > * <p>The ForkJoinTask class is not usually directly subclassed.
78   * Instead, you subclass one of the abstract classes that support a
79 < * particular style of fork/join processing.  Normally, a concrete
79 > * particular style of fork/join processing, typically {@link
80 > * RecursiveAction} for computations that do not return results, or
81 > * {@link RecursiveTask} for those that do.  Normally, a concrete
82   * ForkJoinTask subclass declares fields comprising its parameters,
83   * established in a constructor, and then defines a {@code compute}
84   * method that somehow uses the control methods supplied by this base
# Line 104 | Line 108 | import java.util.WeakHashMap;
108   * parallelism cannot improve throughput. If too small, then memory
109   * and internal task maintenance overhead may overwhelm processing.
110   *
111 + * <p>This class provides {@code adapt} methods for {@link
112 + * java.lang.Runnable} and {@link java.util.concurrent.Callable}, that
113 + * may be of use when mixing execution of ForkJoinTasks with other
114 + * kinds of tasks. When all tasks are of this form, consider using a
115 + * pool in {@link ForkJoinPool#setAsyncMode}.
116 + *
117   * <p>ForkJoinTasks are {@code Serializable}, which enables them
118   * to be used in extensions such as remote execution frameworks. It is
119   * in general sensible to serialize tasks only before or after, but
# Line 484 | Line 494 | public abstract class ForkJoinTask<V> im
494      /**
495       * Arranges to asynchronously execute this task.  While it is not
496       * necessarily enforced, it is a usage error to fork a task more
497 <     * than once unless it has completed and been reinitialized.  This
498 <     * method may be invoked only from within ForkJoinTask
499 <     * computations (as may be determined using method {@link
500 <     * #inForkJoinPool}). Attempts to invoke in other contexts result
501 <     * in exceptions or errors, possibly including ClassCastException.
497 >     * than once unless it has completed and been reinitialized.
498 >     *
499 >     * <p>This method may be invoked only from within {@code
500 >     * ForkJoinTask} computations (as may be determined using method
501 >     * {@link #inForkJoinPool}).  Attempts to invoke in other contexts
502 >     * result in exceptions or errors, possibly including {@code
503 >     * ClassCastException}.
504       *
505 <     * @return {@code this}, to simplify usage.
505 >     * @return {@code this}, to simplify usage
506       */
507      public final ForkJoinTask<V> fork() {
508          ((ForkJoinWorkerThread) Thread.currentThread())
# Line 500 | Line 512 | public abstract class ForkJoinTask<V> im
512  
513      /**
514       * Returns the result of the computation when it is ready.
515 <     * This method differs from {@code get} in that abnormal
516 <     * completion results in RuntimeExceptions or Errors, not
517 <     * ExecutionExceptions.
515 >     * This method differs from {@link #get()} in that
516 >     * abnormal completion results in {@code RuntimeException} or
517 >     * {@code Error}, not {@code ExecutionException}.
518       *
519       * @return the computed result
520       */
# Line 529 | Line 541 | public abstract class ForkJoinTask<V> im
541      }
542  
543      /**
544 <     * Forks both tasks, returning when {@code isDone} holds for
545 <     * both of them or an exception is encountered. This method may be
546 <     * invoked only from within ForkJoinTask computations (as may be
547 <     * determined using method {@link #inForkJoinPool}). Attempts to
548 <     * invoke in other contexts result in exceptions or errors,
549 <     * possibly including ClassCastException.
550 <     *
551 <     * @param t1 one task
552 <     * @param t2 the other task
553 <     * @throws NullPointerException if t1 or t2 are null
554 <     * @throws RuntimeException or Error if either task did so
544 >     * Forks the given tasks, returning when {@code isDone} holds
545 >     * for each task or an exception is encountered.
546 >     *
547 >     * <p>This method may be invoked only from within {@code
548 >     * ForkJoinTask} computations (as may be determined using method
549 >     * {@link #inForkJoinPool}).  Attempts to invoke in other contexts
550 >     * result in exceptions or errors, possibly including {@code
551 >     * ClassCastException}.
552 >     *
553 >     * @param t1 the first task
554 >     * @param t2 the second task
555 >     * @throws NullPointerException if any task is null
556 >     * @throws RuntimeException or Error if a task did so
557       */
558 <    public static void invokeAll(ForkJoinTask<?>t1, ForkJoinTask<?> t2) {
558 >    public static void invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2) {
559          t2.fork();
560          t1.invoke();
561          t2.join();
562      }
563  
564      /**
565 <     * Forks the given tasks, returning when {@code isDone} holds
566 <     * for all of them. If any task encounters an exception, others
567 <     * may be cancelled.  This method may be invoked only from within
568 <     * ForkJoinTask computations (as may be determined using method
569 <     * {@link #inForkJoinPool}). Attempts to invoke in other contexts
570 <     * result in exceptions or errors, possibly including
571 <     * ClassCastException.
565 >     * Forks the given tasks, returning when {@code isDone} holds for
566 >     * each task or an exception is encountered. If any task
567 >     * encounters an exception, others may be, but are not guaranteed
568 >     * to be, cancelled.
569 >     *
570 >     * <p>This method may be invoked only from within {@code
571 >     * ForkJoinTask} computations (as may be determined using method
572 >     * {@link #inForkJoinPool}).  Attempts to invoke in other contexts
573 >     * result in exceptions or errors, possibly including {@code
574 >     * ClassCastException}.
575 >     *
576 >     * <p>Overloadings of this method exist for the special cases
577 >     * of one to four arguments.
578       *
579 <     * @param tasks the array of tasks
579 >     * @param tasks the tasks
580       * @throws NullPointerException if tasks or any element are null
581       * @throws RuntimeException or Error if any task did so
582       */
# Line 594 | Line 614 | public abstract class ForkJoinTask<V> im
614      }
615  
616      /**
617 <     * Forks all tasks in the collection, returning when
618 <     * {@code isDone} holds for all of them. If any task
619 <     * encounters an exception, others may be cancelled.  This method
620 <     * may be invoked only from within ForkJoinTask computations (as
621 <     * may be determined using method {@link
622 <     * #inForkJoinPool}). Attempts to invoke in other contexts result
623 <     * in exceptions or errors, possibly including ClassCastException.
617 >     * Forks all tasks in the collection, returning when {@code
618 >     * isDone} holds for each task or an exception is encountered.
619 >     * If any task encounters an exception, others may be, but are
620 >     * not guaranteed to be, cancelled.
621 >     *
622 >     * <p>This method may be invoked only from within {@code
623 >     * ForkJoinTask} computations (as may be determined using method
624 >     * {@link #inForkJoinPool}).  Attempts to invoke in other contexts
625 >     * result in exceptions or errors, possibly including {@code
626 >     * ClassCastException}.
627       *
628       * @param tasks the collection of tasks
629       * @return the tasks argument, to simplify usage
# Line 681 | Line 704 | public abstract class ForkJoinTask<V> im
704       *
705       * <p>This method may be overridden in subclasses, but if so, must
706       * still ensure that these minimal properties hold. In particular,
707 <     * the cancel method itself must not throw exceptions.
707 >     * the {@code cancel} method itself must not throw exceptions.
708       *
709 <     * <p> This method is designed to be invoked by <em>other</em>
709 >     * <p>This method is designed to be invoked by <em>other</em>
710       * tasks. To terminate the current task, you can just return or
711       * throw an unchecked exception from its computation method, or
712       * invoke {@link #completeExceptionally}.
# Line 710 | Line 733 | public abstract class ForkJoinTask<V> im
733  
734      /**
735       * Returns the exception thrown by the base computation, or a
736 <     * CancellationException if cancelled, or null if none or if the
737 <     * method has not yet completed.
736 >     * {@code CancellationException} if cancelled, or {@code null} if
737 >     * none or if the method has not yet completed.
738       *
739       * @return the exception, or {@code null} if none
740       */
# Line 730 | Line 753 | public abstract class ForkJoinTask<V> im
753       * {@code join} and related operations. This method may be used
754       * to induce exceptions in asynchronous tasks, or to force
755       * completion of tasks that would not otherwise complete.  Its use
756 <     * in other situations is likely to be wrong.  This method is
756 >     * in other situations is discouraged.  This method is
757       * overridable, but overridden versions must invoke {@code super}
758       * implementation to maintain guarantees.
759       *
# Line 750 | Line 773 | public abstract class ForkJoinTask<V> im
773       * operations. This method may be used to provide results for
774       * asynchronous tasks, or to provide alternative handling for
775       * tasks that would not otherwise complete normally. Its use in
776 <     * other situations is likely to be wrong. This method is
776 >     * other situations is discouraged. This method is
777       * overridable, but overridden versions must invoke {@code super}
778       * implementation to maintain guarantees.
779       *
# Line 789 | Line 812 | public abstract class ForkJoinTask<V> im
812       * there are no potential dependencies between continuation of the
813       * current task and that of any other task that might be executed
814       * while helping. (This usually holds for pure divide-and-conquer
815 <     * tasks). This method may be invoked only from within
816 <     * ForkJoinTask computations (as may be determined using method
817 <     * {@link #inForkJoinPool}). Attempts to invoke in other contexts
818 <     * result in exceptions or errors, possibly including
819 <     * ClassCastException.
815 >     * tasks).
816 >     *
817 >     * <p>This method may be invoked only from within {@code
818 >     * ForkJoinTask} computations (as may be determined using method
819 >     * {@link #inForkJoinPool}).  Attempts to invoke in other contexts
820 >     * result in exceptions or errors, possibly including {@code
821 >     * ClassCastException}.
822       *
823       * @return the computed result
824       */
# Line 805 | Line 830 | public abstract class ForkJoinTask<V> im
830      }
831  
832      /**
833 <     * Possibly executes other tasks until this task is ready.  This
834 <     * method may be invoked only from within ForkJoinTask
835 <     * computations (as may be determined using method {@link
836 <     * #inForkJoinPool}). Attempts to invoke in other contexts result
837 <     * in exceptions or errors, possibly including ClassCastException.
833 >     * Possibly executes other tasks until this task is ready.
834 >     *
835 >     * <p>This method may be invoked only from within {@code
836 >     * ForkJoinTask} computations (as may be determined using method
837 >     * {@link #inForkJoinPool}).  Attempts to invoke in other contexts
838 >     * result in exceptions or errors, possibly including {@code
839 >     * ClassCastException}.
840       */
841      public final void quietlyHelpJoin() {
842          if (status >= 0) {
# Line 851 | Line 878 | public abstract class ForkJoinTask<V> im
878       * {@link ForkJoinPool#isQuiescent}. This method may be of use in
879       * designs in which many tasks are forked, but none are explicitly
880       * joined, instead executing them until all are processed.
881 +     *
882 +     * <p>This method may be invoked only from within {@code
883 +     * ForkJoinTask} computations (as may be determined using method
884 +     * {@link #inForkJoinPool}).  Attempts to invoke in other contexts
885 +     * result in exceptions or errors, possibly including {@code
886 +     * ClassCastException}.
887       */
888      public static void helpQuiesce() {
889          ((ForkJoinWorkerThread) Thread.currentThread())
# Line 863 | Line 896 | public abstract class ForkJoinTask<V> im
896       * this task, but only if reuse occurs when this task has either
897       * never been forked, or has been forked, then completed and all
898       * outstanding joins of this task have also completed. Effects
899 <     * under any other usage conditions are not guaranteed, and are
900 <     * almost surely wrong. This method may be useful when executing
899 >     * under any other usage conditions are not guaranteed.
900 >     * This method may be useful when executing
901       * pre-constructed trees of subtasks in loops.
902       */
903      public void reinitialize() {
# Line 877 | Line 910 | public abstract class ForkJoinTask<V> im
910       * Returns the pool hosting the current task execution, or null
911       * if this task is executing outside of any ForkJoinPool.
912       *
913 +     * @see #inForkJoinPool
914       * @return the pool, or {@code null} if none
915       */
916      public static ForkJoinPool getPool() {
# Line 902 | Line 936 | public abstract class ForkJoinTask<V> im
936       * by the current thread, and has not commenced executing in
937       * another thread.  This method may be useful when arranging
938       * alternative local processing of tasks that could have been, but
939 <     * were not, stolen. This method may be invoked only from within
940 <     * ForkJoinTask computations (as may be determined using method
941 <     * {@link #inForkJoinPool}). Attempts to invoke in other contexts
942 <     * result in exceptions or errors, possibly including
943 <     * ClassCastException.
939 >     * were not, stolen.
940 >     *
941 >     * <p>This method may be invoked only from within {@code
942 >     * ForkJoinTask} computations (as may be determined using method
943 >     * {@link #inForkJoinPool}).  Attempts to invoke in other contexts
944 >     * result in exceptions or errors, possibly including {@code
945 >     * ClassCastException}.
946       *
947       * @return {@code true} if unforked
948       */
# Line 921 | Line 957 | public abstract class ForkJoinTask<V> im
957       * value may be useful for heuristic decisions about whether to
958       * fork other tasks.
959       *
960 +     * <p>This method may be invoked only from within {@code
961 +     * ForkJoinTask} computations (as may be determined using method
962 +     * {@link #inForkJoinPool}).  Attempts to invoke in other contexts
963 +     * result in exceptions or errors, possibly including {@code
964 +     * ClassCastException}.
965 +     *
966       * @return the number of tasks
967       */
968      public static int getQueuedTaskCount() {
# Line 938 | Line 980 | public abstract class ForkJoinTask<V> im
980       * tasks, and to process computations locally if this threshold is
981       * exceeded.
982       *
983 +     * <p>This method may be invoked only from within {@code
984 +     * ForkJoinTask} computations (as may be determined using method
985 +     * {@link #inForkJoinPool}).  Attempts to invoke in other contexts
986 +     * result in exceptions or errors, possibly including {@code
987 +     * ClassCastException}.
988 +     *
989       * @return the surplus number of tasks, which may be negative
990       */
991      public static int getSurplusQueuedTaskCount() {
# Line 989 | Line 1037 | public abstract class ForkJoinTask<V> im
1037       * null even if a task exists but cannot be accessed without
1038       * contention with other threads.  This method is designed
1039       * primarily to support extensions, and is unlikely to be useful
1040 <     * otherwise.  This method may be invoked only from within
1041 <     * ForkJoinTask computations (as may be determined using method
1042 <     * {@link #inForkJoinPool}). Attempts to invoke in other contexts
1043 <     * result in exceptions or errors, possibly including
1044 <     * ClassCastException.
1040 >     * otherwise.
1041 >     *
1042 >     * <p>This method may be invoked only from within {@code
1043 >     * ForkJoinTask} computations (as may be determined using method
1044 >     * {@link #inForkJoinPool}).  Attempts to invoke in other contexts
1045 >     * result in exceptions or errors, possibly including {@code
1046 >     * ClassCastException}.
1047       *
1048       * @return the next task, or {@code null} if none are available
1049       */
# Line 1006 | Line 1056 | public abstract class ForkJoinTask<V> im
1056       * Unschedules and returns, without executing, the next task
1057       * queued by the current thread but not yet executed.  This method
1058       * is designed primarily to support extensions, and is unlikely to
1059 <     * be useful otherwise.  This method may be invoked only from
1060 <     * within ForkJoinTask computations (as may be determined using
1061 <     * method {@link #inForkJoinPool}). Attempts to invoke in other
1062 <     * contexts result in exceptions or errors, possibly including
1063 <     * ClassCastException.
1059 >     * be useful otherwise.
1060 >     *
1061 >     * <p>This method may be invoked only from within {@code
1062 >     * ForkJoinTask} computations (as may be determined using method
1063 >     * {@link #inForkJoinPool}).  Attempts to invoke in other contexts
1064 >     * result in exceptions or errors, possibly including {@code
1065 >     * ClassCastException}.
1066       *
1067       * @return the next task, or {@code null} if none are available
1068       */
# Line 1027 | Line 1079 | public abstract class ForkJoinTask<V> im
1079       * {@code null} result does not necessarily imply quiescence
1080       * of the pool this task is operating in.  This method is designed
1081       * primarily to support extensions, and is unlikely to be useful
1082 <     * otherwise.  This method may be invoked only from within
1083 <     * ForkJoinTask computations (as may be determined using method
1084 <     * {@link #inForkJoinPool}). Attempts to invoke in other contexts
1085 <     * result in exceptions or errors, possibly including
1086 <     * ClassCastException.
1082 >     * otherwise.
1083 >     *
1084 >     * <p>This method may be invoked only from within {@code
1085 >     * ForkJoinTask} computations (as may be determined using method
1086 >     * {@link #inForkJoinPool}).  Attempts to invoke in other contexts
1087 >     * result in exceptions or errors, possibly including {@code
1088 >     * ClassCastException}.
1089       *
1090       * @return a task, or {@code null} if none are available
1091       */
# Line 1071 | Line 1125 | public abstract class ForkJoinTask<V> im
1125       */
1126      static final class AdaptedCallable<T> extends ForkJoinTask<T>
1127          implements RunnableFuture<T> {
1128 <        final Callable<T> callable;
1128 >        final Callable<? extends T> callable;
1129          T result;
1130 <        AdaptedCallable(Callable<T> callable) {
1130 >        AdaptedCallable(Callable<? extends T> callable) {
1131              if (callable == null) throw new NullPointerException();
1132              this.callable = callable;
1133          }
# Line 1096 | Line 1150 | public abstract class ForkJoinTask<V> im
1150      }
1151  
1152      /**
1153 <     * Returns a new ForkJoinTask that performs the {@code run}
1154 <     * method of the given Runnable as its action, and returns a null
1155 <     * result upon {@code join}.
1153 >     * Returns a new {@code ForkJoinTask} that performs the {@code run}
1154 >     * method of the given {@code Runnable} as its action, and returns
1155 >     * a null result upon {@link #join}.
1156       *
1157       * @param runnable the runnable action
1158       * @return the task
1159       */
1160 <    public static ForkJoinTask<Void> adapt(Runnable runnable) {
1160 >    public static ForkJoinTask<?> adapt(Runnable runnable) {
1161          return new AdaptedRunnable<Void>(runnable, null);
1162      }
1163  
1164      /**
1165 <     * Returns a new ForkJoinTask that performs the {@code run}
1166 <     * method of the given Runnable as its action, and returns the
1167 <     * given result upon {@code join}.
1165 >     * Returns a new {@code ForkJoinTask} that performs the {@code run}
1166 >     * method of the given {@code Runnable} as its action, and returns
1167 >     * the given result upon {@link #join}.
1168       *
1169       * @param runnable the runnable action
1170       * @param result the result upon completion
# Line 1121 | Line 1175 | public abstract class ForkJoinTask<V> im
1175      }
1176  
1177      /**
1178 <     * Returns a new ForkJoinTask that performs the {@code call}
1179 <     * method of the given Callable as its action, and returns its
1180 <     * result upon {@code join}, translating any checked
1181 <     * exceptions encountered into {@code RuntimeException}.
1178 >     * Returns a new {@code ForkJoinTask} that performs the {@code call}
1179 >     * method of the given {@code Callable} as its action, and returns
1180 >     * its result upon {@link #join}, translating any checked exceptions
1181 >     * encountered into {@code RuntimeException}.
1182       *
1183       * @param callable the callable action
1184       * @return the task
1185       */
1186 <    public static <T> ForkJoinTask<T> adapt(Callable<T> callable) {
1186 >    public static <T> ForkJoinTask<T> adapt(Callable<? extends T> callable) {
1187          return new AdaptedCallable<T>(callable);
1188      }
1189  

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines