Interface StructuredTaskScope.Joiner<T, R, R_X extends Throwable>

Type Parameters:
T - the result type of subtasks forkedPREVIEW in the scope
R - the type of the result returned by the join()PREVIEW method
R_X - the type of the exception thrown by the join()PREVIEW method
Enclosing interface:
StructuredTaskScopePREVIEW<T, R, R_X extends Throwable>

public static interface StructuredTaskScope.Joiner<T, R, R_X extends Throwable>
Joiner is a preview API of the Java platform.
Programs can only use Joiner when preview features are enabled.
Preview features may be removed in a future release, or upgraded to permanent features of the Java platform.
An object used with a StructuredTaskScopePREVIEW to produce the outcome for the scope's join()PREVIEW method.

A StructuredTaskScope is opened with a Joiner that handles subtasks as they are forkedPREVIEW in the scope and again when they complete execution. The Joiner handles subtasks that complete successfully with a result of type T, or fail with any exception or error. The Joiner implements a policy that may cancel the scope when some condition is reached (for example, a subtask fails). When all subtasks complete execution, or the scope is cancelled, the Joiner produces the outcome for the join()PREVIEW method. The outcome is a result of type R or an exception of type R_X.

Joiner defines static methods to create Joiner objects for common cases:

  • allSuccessfulOrThrow() and allSuccessfulOrThrow(Function) create a Joiner that produces a list of all results for join() to return when all subtasks complete successfully. The Joiner cancels the scope and causes join() to throw ExecutionException, or an exception returned by an exception supplying function, if any subtask fails.
  • anySuccessfulOrThrow() and anySuccessfulOrThrow(Function) create a Joiner that produces the result of any successful subtask for join() to return. The Joiner causes join() to throw ExecutionException, or an exception returned by an exception supplying function, if all subtasks fail.
  • awaitAllSuccessfulOrThrow() and awaitAllSuccessfulOrThrow(Function) create a Joiner that waits for all subtasks to complete successfully. The Joiner does not produce a non-null result for join() to return. The Joiner cancels the scope and causes join() to throw ExecutionException, or an exception returned by an exception supplying function, if any subtask fails.

In addition to the methods to create Joiner objects for common cases, the allUntil(Predicate) method can be used to create a Joiner that implements a cancellation policy. The Joiner is created with a predicate that is evaluated on completed subtasks. The Joiner cancels the scope if the predicate evaluates to true. When using this Joiner, the outcome of the join() method is the list of all subtasks forked in the scope.

More advanced policies can be developed by implementing the Joiner interface. The onFork(Subtask) method is invoked when subtasks are forked. The onComplete(Subtask) method is invoked when subtasks complete with a result or exception. These methods return a boolean to indicate whether the scope should be cancelled. These methods can be used to collect subtasks, results, or exceptions, and control when to cancel the scope. The result() method must be implemented to produce the outcome (result or exception) for the join() method. The timeout() method must be implemented to produce the outcome for the timeoutPREVIEW case.

Unless otherwise specified, passing a null argument to a method in this class will cause a NullPointerException to be thrown.

API Note:
It is very important that a new Joiner object is created for each StructuredTaskScope. Joiner objects should never be shared with different scopes or re-used after a scope is closed.
Implementation Requirements:
Implementations of this interface must be thread-safe. The onComplete(Subtask) method may be invoked concurrently as multiple subtasks can complete at the same time. Additionally, the onComplete(Subtask) method may be called concurrently with the scope owner thread invoking the onFork(Subtask), result(), or timeout() methods.

A Joiner should clearly document how it handles timeoutsPREVIEW. In many cases, a timeout will cause the join() method to throw an exception with CancelledByTimeoutExceptionPREVIEW as the cause. Some Joiner implementation may be capable of returning a result for the timeout case.

Designing a Joiner should take into account the code at the use-site where the results from the join()PREVIEW method are processed. It should be clear what the Joiner does vs. the application code at the use-site. In general, the Joiner implementation is not the place for "business logic". A Joiner should be designed to be as general purpose as possible.

Since:
25
See Also:
  • Method Details

    • onFork

      default boolean onFork(StructuredTaskScope.SubtaskPREVIEW<T> subtask)
      Invoked by fork(Callable)PREVIEW and fork(Runnable)PREVIEW when forking a subtask. The method is invoked before a thread is created to execute the subtask.
      API Note:
      This method is invoked by the fork methods. It should not be invoked directly.
      Implementation Requirements:
      The default implementation throws NullPointerException if the subtask is null. It throws IllegalArgumentException if the subtask is not in the UNAVAILABLEPREVIEW state, it otherwise returns false.
      Parameters:
      subtask - the subtask
      Returns:
      true to cancel the scope, otherwise false
    • onComplete

      default boolean onComplete(StructuredTaskScope.SubtaskPREVIEW<T> subtask)
      Invoked by the thread that executed a subtask after the subtask completes successfully or fails with an exception. This method is not invoked by subtasks that complete after the scope is cancelled.
      API Note:
      This method is invoked by subtasks when they complete. It should not be invoked directly.
      Implementation Requirements:
      The default implementation throws NullPointerException if the subtask is null. It throws IllegalArgumentException if the subtask is not in the SUCCESSPREVIEW or FAILEDPREVIEW state, it otherwise returns false.
      Parameters:
      subtask - the subtask
      Returns:
      true to cancel the scope, otherwise false
    • result

      R result() throws R_X
      Invoked by the join()PREVIEW method to produce the outcome (result or exception) after waiting for all subtasks to complete or the scope is cancelled. This method is not invoked if the scope was opened with a timeout and the timeout expires before or while waiting.

      This method will be called at most once, by the join() method, to produce the outcome. The behavior of this method when invoked directly is undefined.

      API Note:
      This method is invoked by the join() method. It should not be invoked directly.
      Returns:
      the result
      Throws:
      R_X - if the outcome is an exception
    • timeout

      R timeout() throws R_X
      Invoked by the join()PREVIEW method to produce the outcome (result or exception) when the scope was opened with a timeout and the timeout expires before or while waiting in the join() method.

      If the outcome is an exception, this method throws the exception with a CancelledByTimeoutExceptionPREVIEW as the cause.

      This method will be called at most once, by the join() method, to produce the outcome. The behavior of this method when invoked directly is undefined.

      API Note:
      This method is invoked by the join() method. It should not be invoked directly.
      Returns:
      the result
      Throws:
      R_X - with a cause of CancelledByTimeoutException, if the outcome is an exception
      Since:
      27
    • allSuccessfulOrThrow

      static <T, R_X extends Throwable> StructuredTaskScope.JoinerPREVIEW<T,List<T>,R_X> allSuccessfulOrThrow(Function<Throwable, R_X> esf)
      Returns a new Joiner that produces a list of all results when all subtasks complete successfully. The Joiner cancels the scope and causes the join()PREVIEW method to throw if any subtask fails.

      The join()PREVIEW method of a StructuredTaskScopePREVIEW opened with this Joiner returns the list of the results, in the order that the subtasks were forkedPREVIEW, when all subtasks complete successfully. An empty list is returned if no subtasks were forked. If any subtask fails then the Joiner causes the join() method to throw the exception returned by the given exception supplying function when applied to the exception from the first subtask to fail. The function should return an exception with the exception from the failed subtask (the function argument) as the cause. If the function returns null then it causes the join() method to throw NullPointerException.

      Timeout Handling: The Joiner cannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeoutPREVIEW, and the timeout expires before or while waiting for all subtasks to complete successfully, then the Joiner causes the join() method to throw the exception returned by the exception supplying function when applied to a CancelledByTimeoutExceptionPREVIEW.

      API Note:
      Joiners returned by this method are suited to cases where all subtasks return a result of the same type. It removes the need for bookkeeping and the need to keep a reference to the SubtaskPREVIEW objects returned by the fork(Callable)PREVIEW method. Joiners returned by awaitAllSuccessfulOrThrow(Function) and awaitAllSuccessfulOrThrow() are suited to cases where the subtasks return results of different types and where it is necessary to keep a reference to the Subtask objects.

      The following example is a method that opens a StructuredTaskScope with a Joiner created with allSuccessfulOrThrow(Function). The method is invoked with a collection of callables. It forksPREVIEW a subtask to execute each callable, waits in join()PREVIEW for all subtasks to complete successfully, and then returns a list of the results. It throws the runtime exception CompletionException if any subtask fails, with the exception from the first subtask to fail as the cause.

        <T> List<T> invokeAll(Collection<Callable<T>> tasks) throws InterruptedException {
              try (var scope = StructuredTaskScope.open(
                      Joiner.<T, CompletionException>allSuccessfulOrThrow(CompletionException::new))) {
                  tasks.forEach(scope::fork);
                  List<T> results = scope.join();
                  return results;
              }
        }
      
      Type Parameters:
      T - the result type of subtasks
      R_X - the type of the exception thrown by the join()PREVIEW method
      Parameters:
      esf - the exception supplying function
      Returns:
      a new Joiner that produces a list of all results when all subtasks complete successfully
      Since:
      27
      See Also:
    • allSuccessfulOrThrow

      static <T> StructuredTaskScope.JoinerPREVIEW<T, List<T>, ExecutionException> allSuccessfulOrThrow()
      Returns a new Joiner that produces a list of all results when all subtasks complete successfully. The Joiner cancels the scope and causes the join()PREVIEW method to throw ExecutionException if any subtask fails.

      The join()PREVIEW method of a StructuredTaskScopePREVIEW opened with this Joiner returns the list of the results, in the order that the subtasks were forkedPREVIEW, when all subtasks complete successfully. An empty list is returned if no subtasks were forked. If any subtask fails then the Joiner causes the join() method to throw ExecutionException with the exception from the first subtask to fail as the cause.

      Timeout Handling: The Joiner cannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeoutPREVIEW, and the timeout expires before or while waiting for all subtasks to complete successfully, then the Joiner causes the join() method to throw ExecutionException with a CancelledByTimeoutExceptionPREVIEW as the cause.

      Implementation Requirements:
      A Joiner returned by this method is equivalent to invoking allSuccessfulOrThrow(ExecutionException::new).
      Type Parameters:
      T - the result type of subtasks
      Returns:
      a new Joiner that produces a list of all results when all subtasks complete successfully
    • anySuccessfulOrThrow

      static <T, R_X extends Throwable> StructuredTaskScope.JoinerPREVIEW<T,T,R_X> anySuccessfulOrThrow(Function<Throwable, R_X> esf)
      Returns a new Joiner that produces the result of any successful subtask. The Joiner cancels the scope when a subtask completes successfully. It causes the join()PREVIEW method to throw if all subtasks fail.

      The join()PREVIEW method of a StructuredTaskScopePREVIEW opened with this Joiner returns the result of a successful subtask. If a subtask completes successfully then the scope is cancelled and the join() method returns its result. If all subtasks fail then the Joiner causes the join() method to throw the exception returned by the given exception supplying function when applied to the exception from one of the failed subtasks. The function should return an exception with the exception from the failed subtask (the function argument) as the cause. If the function returns null then it causes the join() method to throw NullPointerException. If no subtasks were forked then the Joiner causes the join() method to throw the exception returned by the function when applied to an instance of NoSuchElementException.

      Timeout Handling: The Joiner cannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeoutPREVIEW, and the timeout expires before or while waiting for a subtask to complete successfully, then the Joiner causes the join() method to throw the exception returned by the exception supplying function when applied to a CancelledByTimeoutExceptionPREVIEW.

      API Note:
      The following example is a method that opens a StructuredTaskScope with a Joiner created with anySuccessfulOrThrow(Function). The method is invoked with a collection of callables. It forksPREVIEW a subtask to execute each callable, waits in join()PREVIEW for any subtask to complete successfully, returning its result. It throws the runtime exception CompletionException if all subtasks fail, with the exception from a failed subtask as the cause.
        <T> T invokeAny(Collection<Callable<T>> tasks) throws InterruptedException {
              try (var scope = StructuredTaskScope.open(
                    Joiner.<T, CompletionException>anySuccessfulOrThrow(CompletionException::new))) {
                  tasks.forEach(scope::fork);
                  T result = scope.join();
                  return result;
              }
        }
      
      Type Parameters:
      T - the result type of subtasks
      R_X - the type of the exception thrown by the join()PREVIEW method
      Parameters:
      esf - the exception supplying function
      Returns:
      a new Joiner that produces the result of any successful subtask
      Since:
      27
      See Also:
    • anySuccessfulOrThrow

      static <T> StructuredTaskScope.JoinerPREVIEW<T, T, ExecutionException> anySuccessfulOrThrow()
      Returns a new Joiner that produces the result of any successful subtask. The Joiner cancels the scope when a subtask completes successfully. It causes the join()PREVIEW method to throw ExecutionException if all subtasks fail.

      The join()PREVIEW method of a StructuredTaskScopePREVIEW opened with this Joiner returns the result of a successful subtask. If a subtask completes successfully then the scope is cancelled and the join() method returns its result. If all subtasks fail then the Joiner causes the join() method to throw ExecutionException with the exception from one of the failed subtasks as the cause. If no subtasks were forked then the Joiner causes the join() method to throw ExecutionException with NoSuchElementException as the cause.

      Timeout Handling: The Joiner cannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeoutPREVIEW, and the timeout expires before or while waiting for a subtask to complete successfully, then the Joiner causes the join() method to throw ExecutionException with a CancelledByTimeoutExceptionPREVIEW as the cause.

      Implementation Requirements:
      A Joiner returned by this method is equivalent to invoking anySuccessfulOrThrow(ExecutionException::new).
      Type Parameters:
      T - the result type of subtasks
      Returns:
      a new Joiner that produces the result of any successful subtask
      Since:
      26
    • awaitAllSuccessfulOrThrow

      static <T, R_X extends Throwable> StructuredTaskScope.JoinerPREVIEW<T,Void,R_X> awaitAllSuccessfulOrThrow(Function<Throwable, R_X> esf)
      Returns a new Joiner that causes the join()PREVIEW method to wait for all subtasks to complete successfully. The Joiner cancels the scope and causes the join()PREVIEW method to throw if any subtask fails.

      The join()PREVIEW method of a StructuredTaskScopePREVIEW opened with this Joiner returns null when all subtasks complete successfully. If any subtask fails then the Joiner causes the join() method to throw the exception returned by the given exception supplying function when applied to the exception from the first subtask to fail. The function should return an exception with the exception from the failed subtask (the function argument) as the cause. If the function returns null then it causes the join() method to throw NullPointerException.

      Timeout Handling: The Joiner cannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeoutPREVIEW, and the timeout expires before or while waiting for all subtasks to complete successfully, then the Joiner causes the join() method to throw the exception returned by the exception supplying function when applied to a CancelledByTimeoutExceptionPREVIEW.

      API Note:
      Joiners returned by this method are suited to cases where subtasks return results of different types and where it is necessary to keep a reference to each SubtaskPREVIEW. Joiners returned by allSuccessfulOrThrow(Function) and allSuccessfulOrThrow() are suited to cases where the subtasks return a result of the same type.

      The following example opens a StructuredTaskScope with a Joiner created with awaitAllSuccessfulOrThrow(). It forksPREVIEW two subtasks, then waits in join()PREVIEW for both subtasks to complete successfully or either subtask to fail. If both subtasks complete successfully then it invokes Subtask.get()PREVIEW on both subtasks to get their results. It throws the runtime exception CompletionException if any subtask fails, with the exception from a failed subtask as the cause.

        try (var scope = StructuredTaskScope.open(
                 Joiner.awaitAllSuccessfulOrThrow(CompletionException::new))) {
            Subtask<String> subtask1 = scope.fork(callable1);
            Subtask<Integer> subtask2 = scope.fork(callable2);
      
            // throws CompletionException if either subtask fails
            scope.join();
      
            // both subtasks completed successfully
            var result = new MyResult(subtask1.get(), subtask2.get());
        }
      
      Type Parameters:
      T - the result type of subtasks
      R_X - the type of the exception thrown by the join()PREVIEW method
      Parameters:
      esf - the exception supplying function
      Returns:
      a new Joiner that causes the join()PREVIEW method to wait for all subtasks to complete successfully
      Since:
      27
    • awaitAllSuccessfulOrThrow

      static <T> StructuredTaskScope.JoinerPREVIEW<T, Void, ExecutionException> awaitAllSuccessfulOrThrow()
      Returns a new Joiner that causes the join()PREVIEW method to wait for all subtasks to complete successfully. The Joiner cancels the scope and causes the join()PREVIEW method to throw ExecutionException if any subtask fails.

      The join()PREVIEW method of a StructuredTaskScopePREVIEW opened with this Joiner returns null when all subtasks complete successfully. If any subtask fails then the Joiner causes the join() method to throw ExecutionException with the exception from the first subtask to fail as the cause.

      Timeout Handling: The Joiner cannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeoutPREVIEW, and the timeout expires before or while waiting for all subtasks to complete successfully, then the Joiner causes the join() method to throw ExecutionException with a CancelledByTimeoutExceptionPREVIEW as the cause.

      Implementation Requirements:
      A Joiner returned by this method is equivalent to invoking awaitAllSuccessfulOrThrow(ExecutionException::new).
      Type Parameters:
      T - the result type of subtasks
      Returns:
      a new Joiner that causes the join()PREVIEW method to wait for all subtasks to complete successfully
      See Also:
    • allUntil

      Returns a new Joiner that produces a list of all subtasks when all subtasks complete or evaluating a predicate on a completed subtask causes the scope to be cancelled. The Joiner does not cause the join()PREVIEW method to throw if subtasks fail or a configured timeoutPREVIEW expires. This method can be used to create a Joiner that implements a cancellation policy.

      The join()PREVIEW method of a StructuredTaskScopePREVIEW opened with this Joiner returns a list of all subtasks, in the order that they were forkedPREVIEW, when all subtasks complete or the scope is cancelled. The returned list may contain subtasks that completed (in the SUCCESSPREVIEW or FAILEDPREVIEW state), or subtasks in the UNAVAILABLEPREVIEW state if they were forked or completed after the scope was cancelled. The scope is cancelled if the given predicate evaluates to true, or a configured timeout expires before or while waiting in the join() method.

      The given Predicate's test(Object) method is invoked on a completed subtask by the thread that executed the subtask. The method is invoked after the subtask completes (successfully or with an exception) before the thread terminates. The scope is cancelled if the test method returns true. The test method must be thread safe. It may be invoked concurrently from several threads as multiple subtasks can complete at the same time. If the method throws an exception or error, the thread invokes the uncaught exception handler with the exception or error before the thread terminates.

      Timeout Handling: If used with a scope that has a timeoutPREVIEW set, and the timeout expires before all subtasks complete then the join() method returns the list of all subtasks. It does not throw an exception. Subtasks that did not complete before the timeout expires will be in the UNAVAILABLE state.

      API Note:
      The following example uses allUntil to create a Joiner that cancels the scope when two or more subtasks fail.
         class CancelAfterTwoFailures implements Predicate<Subtask<?>> {
              private final AtomicInteger failedCount = new AtomicInteger();
              @Override
              public boolean test(Subtask<?> subtask) {
                  return subtask.state() == Subtask.State.FAILED
                          && failedCount.incrementAndGet() >= 2;
              }
          }
      
          var joiner = Joiner.<String>allUntil(new CancelAfterTwoFailures());
      

      The following example uses allUntil to create a Joiner that cancels the scope when any subtask completes successfully. The subtasks are grouped according to their state to produce a map with up to three key-value mappings. The map key is the subtask state, the value is the list of subtasks in that state.

          Predicate<Subtask<?>> isSuccessful = s -> s.state() == Subtask.State.SUCCESS;
      
          try (var scope = StructuredTaskScope.open(Joiner.<String>allUntil(isSuccessful))) {
              tasks.forEach(scope::fork);
      
              Map<Subtask.State, List<Subtask<String>>> subtasksByState = scope.join()
                      .stream()
                      .collect(Collectors.groupingBy(Subtask::state));
          }
      

      The following example is a method that uses allUntil to create a Joiner that does not cancel the scope. The method waits for all subtasks to complete or a timeout to expire. It returns a list of all subtasks, in the same order as the collection of callables, even if the timeout expires before or while waiting in join().

         <T> List<Subtask<T>> invokeAll(Collection<Callable<T>> tasks, Duration timeout) throws InterruptedException {
             try (var scope = StructuredTaskScope.open(Joiner.<T>allUntil(_ -> false), cf -> cf.withTimeout(timeout))) {
                 tasks.forEach(scope::fork);
                 return scope.join();
             }
         }
      
      Type Parameters:
      T - the result type of subtasks
      Parameters:
      isDone - the predicate to evaluate completed subtasks
      Returns:
      a new Joiner that produces a list of all subtasks when all subtasks complete or evaluating a predicate on a completed subtask causes the scope to be cancelled