Interface StructuredTaskScope.Joiner<T, R, R_X extends Throwable>
- Type Parameters:
T- the result type of subtasks forked in the scopeR- the type of the result returned by thejoin()methodR_X- the type of the exception thrown by thejoin()method
- Enclosing interface:
StructuredTaskScope<T, R, R_X extends Throwable>
StructuredTaskScope to produce the outcome for the
scope's join() method.
A StructuredTaskScope is opened with a Joiner that handles
subtasks as they are forked 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() 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()andallSuccessfulOrThrow(Function)create aJoinerthat produces a list of all results forjoin()to return when all subtasks complete successfully. TheJoinercancels the scope and causesjoin()to throwExecutionException, or an exception returned by an exception supplying function, if any subtask fails. -
anySuccessfulOrThrow()andanySuccessfulOrThrow(Function)create aJoinerthat produces the result of any successful subtask forjoin()to return. TheJoinercancels the scope when any subtask completes successfully. TheJoinercausesjoin()to throwExecutionException, or an exception returned by an exception supplying function, if all subtasks fail. -
awaitAllSuccessfulOrThrow()andawaitAllSuccessfulOrThrow(Function)create aJoinerthat waits for all subtasks to complete successfully. TheJoinerdoes not produce a non-nullresult forjoin()to return. TheJoinercancels the scope and causesjoin()to throwExecutionException, 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 or when
a thread cannot be created to execute a subtask. 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
timeout 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
Joinerobject is created for eachStructuredTaskScope.Joinerobjects should never be shared with different scopes or reused 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, theonComplete(Subtask)method may be called concurrently with the scope owner thread invoking theonFork(Subtask),result(), ortimeout()methods.A
Joinershould clearly document how it handles timeouts. In many cases, a timeout will cause thejoin()method to throw an exception withCancelledByTimeoutExceptionas the cause. SomeJoinerimplementations may be capable of returning a result for the timeout case.Designing a
Joinershould take into account the code at the use site where the results from thejoin()method are processed. It should be clear what theJoinerdoes as opposed to the application code at the use site. In general, theJoinerimplementation is not the place for "business logic". AJoinershould be designed to be as general purpose as possible. - Since:
- 28
- See Also:
-
Method Summary
Modifier and TypeMethodDescriptionstatic <T> StructuredTaskScope.Joiner<T, List<T>, ExecutionException> Returns a new Joiner that produces a list of all results when all subtasks complete successfully.static <T, R_X extends Throwable>
StructuredTaskScope.Joiner<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.static <T> StructuredTaskScope.Joiner<T, List<StructuredTaskScope.Subtask<T>>, RuntimeException> allUntil(Predicate<? super StructuredTaskScope.Subtask<T>> isDone) Returns a new Joiner that produces a list of all subtasks when all subtasks complete, or when evaluation of a predicate on a completed subtask causes the scope to be cancelled.static <T> StructuredTaskScope.Joiner<T, T, ExecutionException> Returns a new Joiner that produces the result of any successful subtask.static <T, R_X extends Throwable>
StructuredTaskScope.Joiner<T, T, R_X> anySuccessfulOrThrow(Function<Throwable, R_X> esf) Returns a new Joiner that produces the result of any successful subtask.static <T> StructuredTaskScope.Joiner<T, Void, ExecutionException> Returns a new Joiner that causes thejoin()method to wait for all subtasks to complete successfully.static <T, R_X extends Throwable>
StructuredTaskScope.Joiner<T, Void, R_X> awaitAllSuccessfulOrThrow(Function<Throwable, R_X> esf) Returns a new Joiner that causes thejoin()method to wait for all subtasks to complete successfully.default booleanonComplete(StructuredTaskScope.Subtask<T> subtask) Invoked by the thread that executed a subtask after the subtask completes, or by the scope owner thread when forking is unable to create a thread to execute the subtask.default booleanonFork(StructuredTaskScope.Subtask<T> subtask) Invoked by the scope owner thread when forking a subtask.result()timeout()Invoked by thejoin()method to produce the outcome (result or exception) when the scope was opened with a timeout and the timeout expires before thejoin()method is invoked or completes.
-
Method Details
-
onFork
Invoked by the scope owner thread 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
forkmethods. It should not be invoked directly. - Implementation Requirements:
- The default implementation throws
NullPointerExceptionif the subtask isnull. It throwsIllegalArgumentExceptionif the subtask is not in theUNAVAILABLEstate, and otherwise returnsfalse. - Parameters:
subtask- the subtask- Returns:
trueto cancel the scope, otherwisefalse
-
onComplete
Invoked by the thread that executed a subtask after the subtask completes, or by the scope owner thread when forking is unable to create a thread to execute the subtask.This method is not invoked for subtasks that are forked or complete after the scope is cancelled.
If
fork(Callable)orfork(Runnable)fails to create a thread to execute a subtask, theforkmethod invokes this method directly with the subtask in theFAILEDstate. In this case, theSubtask.exception()method returns the exception or error that prevented the thread from being created.- API Note:
- This method is invoked by the thread that executed a subtask or by a
forkmethod. It should not be invoked directly. - Implementation Requirements:
- The default implementation throws
NullPointerExceptionif the subtask isnull. It throwsIllegalArgumentExceptionif the subtask is not in theSUCCESSorFAILEDstate, and otherwise returnsfalse. - Parameters:
subtask- the subtask- Returns:
trueto cancel the scope, otherwisefalse
-
result
Invoked by thejoin()method to produce the outcome (result or exception) after all subtasks complete or the scope is cancelled. This method is not invoked if the scope was opened with a timeout and the timeout expires before thejoin()method is invoked or completes.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
Invoked by thejoin()method to produce the outcome (result or exception) when the scope was opened with a timeout and the timeout expires before thejoin()method is invoked or completes.If the outcome is an exception, this method throws the exception with a
CancelledByTimeoutExceptionas thecause.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 ofCancelledByTimeoutException, if the outcome is an exception
-
allSuccessfulOrThrow
static <T, R_X extends Throwable> StructuredTaskScope.Joiner<T,List<T>, allSuccessfulOrThrowR_X> (Function<Throwable, R_X> esf) Returns a new Joiner that produces a list of all results when all subtasks complete successfully. TheJoinercancels the scope and causes thejoin()method to throw if any subtask fails.The
join()method of aStructuredTaskScopeopened with this Joiner returns the list of the results, in the order that the subtasks were forked, when all subtasks complete successfully. An empty list is returned if no subtasks were forked. If any subtask fails then theJoinercauses thejoin()method to throw the exception returned by the given exception supplying function when applied to the exception from a failed subtask. The function should return an exception with the exception from the failed subtask (the function argument) as the cause. If the function returnsnullthen it causes thejoin()method to throwNullPointerException.Timeout Handling: The
Joinercannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeout, and the timeout expires before thejoin()method is invoked or completes, then theJoinercauses thejoin()method to throw the exception returned by the exception supplying function when applied to aCancelledByTimeoutException.- API Note:
- A Joiner returned by this method is suited to cases where all subtasks
return a result of the same type. The Joiner removes the need for
bookkeeping and the need to keep a reference to the
Subtaskobjects returned by thefork(Callable)method. Joiners returned byawaitAllSuccessfulOrThrow(Function)andawaitAllSuccessfulOrThrow()are suited to cases where the subtasks return results of different types and where it is necessary to keep a reference to theSubtaskobjects.The following example is a method that opens a
StructuredTaskScopewith a Joiner created withallSuccessfulOrThrow(Function). The method is invoked with a collection of callables. It forks a subtask to execute each callable, waits injoin()for all subtasks to complete successfully, and then returns a list of the results. It throws the runtime exceptionCompletionExceptionif any subtask fails, with the exception from a failed subtask 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 subtasksR_X- the type of the exception thrown by thejoin()method- Parameters:
esf- the exception supplying function- Returns:
- a new Joiner that produces a list of all results when all subtasks complete successfully
- See Also:
-
allSuccessfulOrThrow
Returns a new Joiner that produces a list of all results when all subtasks complete successfully. TheJoinercancels the scope and causes thejoin()method to throwExecutionExceptionif any subtask fails.The
join()method of aStructuredTaskScopeopened with this Joiner returns the list of the results, in the order that the subtasks were forked, when all subtasks complete successfully. An empty list is returned if no subtasks were forked. If any subtask fails then the Joiner causes thejoin()method to throwExecutionExceptionwith the exception from a failed subtask as the cause.Timeout Handling: The
Joinercannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeout, and the timeout expires before thejoin()method is invoked or completes, then theJoinercauses thejoin()method to throwExecutionExceptionwith aCancelledByTimeoutExceptionas the cause.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.Joiner<T,T, anySuccessfulOrThrowR_X> (Function<Throwable, R_X> esf) Returns a new Joiner that produces the result of any successful subtask. TheJoinercancels the scope when a subtask completes successfully. It causes thejoin()method to throw if all subtasks fail.The
join()method of aStructuredTaskScopeopened with this Joiner returns the result of a successful subtask. If a subtask completes successfully then the scope is cancelled and thejoin()method returns its result. If all subtasks fail then the Joiner causes thejoin()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 a failed subtask (the function argument) as the cause. If the function returnsnullthen it causes thejoin()method to throwNullPointerException. If no subtasks were forked then the Joiner causes thejoin()method to throw the exception returned by the function when applied to an instance ofNoSuchElementException.Timeout Handling: The
Joinercannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeout, and the timeout expires before thejoin()method is invoked or completes, then theJoinercauses thejoin()method to throw the exception returned by the exception supplying function when applied to aCancelledByTimeoutException.- API Note:
- The following example is a method that opens a
StructuredTaskScopewith a Joiner created withanySuccessfulOrThrow(Function). The method is invoked with a collection of callables. It forks a subtask to execute each callable, waits injoin()for any subtask to complete successfully, returning its result. It throws the runtime exceptionCompletionExceptionif 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 subtasksR_X- the type of the exception thrown by thejoin()method- Parameters:
esf- the exception supplying function- Returns:
- a new Joiner that produces the result of any successful subtask
- See Also:
-
anySuccessfulOrThrow
Returns a new Joiner that produces the result of any successful subtask. TheJoinercancels the scope when a subtask completes successfully. It causes thejoin()method to throwExecutionExceptionif all subtasks fail.The
join()method of aStructuredTaskScopeopened with this Joiner returns the result of a successful subtask. If a subtask completes successfully then the scope is cancelled and thejoin()method returns its result. If all subtasks fail then the Joiner causes thejoin()method to throwExecutionExceptionwith the exception from a failed subtask as the cause. If no subtasks were forked then theJoinercauses thejoin()method to throwExecutionExceptionwithNoSuchElementExceptionas the cause.Timeout Handling: The
Joinercannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeout, and the timeout expires before thejoin()method is invoked or completes, then theJoinercauses thejoin()method to throwExecutionExceptionwith aCancelledByTimeoutExceptionas the cause.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
-
awaitAllSuccessfulOrThrow
static <T, R_X extends Throwable> StructuredTaskScope.Joiner<T,Void, awaitAllSuccessfulOrThrowR_X> (Function<Throwable, R_X> esf) Returns a new Joiner that causes thejoin()method to wait for all subtasks to complete successfully. TheJoinercancels the scope and causes thejoin()method to throw if any subtask fails.The
join()method of aStructuredTaskScopeopened with this Joiner returnsnullwhen all subtasks complete successfully. If any subtask fails then the Joiner causes thejoin()method to throw the exception returned by the given exception supplying function when applied to the exception from a failed subtask. The function should return an exception with the exception from the failed subtask (the function argument) as the cause. If the function returnsnullthen it causes thejoin()method to throwNullPointerException.Timeout Handling: The
Joinercannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeout, and the timeout expires before thejoin()method is invoked or completes, then theJoinercauses thejoin()method to throw the exception returned by the exception supplying function when applied to aCancelledByTimeoutException.- 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
Subtask. Joiners returned byallSuccessfulOrThrow(Function)andallSuccessfulOrThrow()are suited to cases where the subtasks return a result of the same type.The following example opens a
StructuredTaskScopewith a Joiner created withawaitAllSuccessfulOrThrow(). It forks two subtasks, then waits injoin()for both subtasks to complete successfully or either subtask to fail. If both subtasks complete successfully then it invokesSubtask.get()on both subtasks to get their results. It throws the runtime exceptionCompletionExceptionif 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 subtasksR_X- the type of the exception thrown by thejoin()method- Parameters:
esf- the exception supplying function- Returns:
- a new Joiner that causes the
join()method to wait for all subtasks to complete successfully
-
awaitAllSuccessfulOrThrow
Returns a new Joiner that causes thejoin()method to wait for all subtasks to complete successfully. TheJoinercancels the scope and causes thejoin()method to throwExecutionExceptionif any subtask fails.The
join()method of aStructuredTaskScopeopened with this Joiner returnsnullwhen all subtasks complete successfully. If any subtask fails then the Joiner causes thejoin()method to throwExecutionExceptionwith the exception from a failed subtask as the cause.Timeout Handling: The
Joinercannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeout, and the timeout expires before thejoin()method is invoked or completes, then theJoinercauses thejoin()method to throwExecutionExceptionwith aCancelledByTimeoutExceptionas the cause.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()method to wait for all subtasks to complete successfully - See Also:
-
allUntil
static <T> StructuredTaskScope.Joiner<T, List<StructuredTaskScope.Subtask<T>>, RuntimeException> allUntil(Predicate<? super StructuredTaskScope.Subtask<T>> isDone) Returns a new Joiner that produces a list of all subtasks when all subtasks complete, or when evaluation of a predicate on a completed subtask causes the scope to be cancelled. TheJoinerdoes not cause thejoin()method to throw if subtasks fail or a configured timeout expires. This method can be used to create aJoinerthat implements a cancellation policy.The
join()method of aStructuredTaskScopeopened with this Joiner returns a list of all subtasks, in the order that they were forked, when all subtasks complete or the scope is cancelled. The returned list may contain subtasks that completed (in theSUCCESSorFAILEDstate), or subtasks in theUNAVAILABLEstate if they were forked or completed after the scope was cancelled. The scope is cancelled if the given predicate evaluates totrue, or a configured timeout expires before thejoin()method is invoked or completes.If a subtask completes before the scope is cancelled, the thread that executed the subtask invokes the given
Predicate'stest(Object)method with the completed subtask. The method is invoked after the subtask completes (successfully or with an exception) and before the thread terminates. The scope is cancelled if thetestmethod returnstrue. Thetestmethod 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.If
fork(Callable)orfork(Runnable)fails to create a thread to execute a subtask, theforkmethod invokes the predicate directly with the subtask in theFAILEDstate.Timeout Handling: If used with a scope that has a timeout 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 theUNAVAILABLEstate.- API Note:
- The following example uses
allUntilto 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
allUntilto 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
allUntilto create a Joiner that does not itself 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 thejoin()method is invoked or completes.<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 when evaluation of a predicate on a completed subtask causes the scope to be cancelled
-