How can I know if a Java 19 Structured Concurrency StructuredTaskScope is shutdown ( canceled )?

430 views Asked by At

A Java 19 ShutdownOnFailure scope also allows for explicit cancellation of all tasks using the shutdown method. How can I know if the scope has been shutdown? The API includes an isShutdown method, but it is private.

Here is some (incomplete) code just to illustrate a possible use, where a scope is canceled some time after starting by the owner calling shutdown.

I guess that after a join() I could issue another scope.fork() and check if if returns a future to a canceled task but that seems weird. Is that the way to go?

ExecutorService executor = Executors.newCachedThreadPool();
try (StructuredTaskScope.ShutdownOnFailure scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Future<Integer> numOrders = scope.fork(Business::getNumItemsFromRequest);
    Future<Double> price = scope.fork(Business::getPriceFromDB);
    
    Business.sleepFor(500);
    scope.shutdown(); // cancels all the scope tasks
    //numOrders.cancel(true); // cancel just one task
    
    scope.join(); // wait for all tasks
    try {
        scope.throwIfFailed();
        // how would I know if scope was shutdown before all tasks completed?
        System.out.println("Scope Completed Without Errors ( But possibly canceled ) ");
        double amount = numOrders.get() * price.get();
2

There are 2 answers

1
Michael Easter On BEST ANSWER

Working code (as an experiment) located here.

Given this:

var result = "error";

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
     var numOrders = scope.fork(() -> taskGetNumItemsFromRequest()); 
     var price = scope.fork(() -> taskGetPriceFromDB());

    if (doShutdown) {
        scope.shutdown();
    }
            
    scope.join();          
    scope.throwIfFailed();

    result = numOrders.resultNow() + " " + price.resultNow();
} catch (Exception ex) {
    if (ex != null) {
        System.err.println("caught ex: " + ex.getClass());
        System.err.println("caught ex message: " + ex.getMessage());
    }
}

then this line:

    result = numOrders.resultNow() + " " + price.resultNow();

will throw an Exception if the scope is shutdown. See the code for Future.resultNow().

It's unclear if you want to distinguish between other exception cases, such as closing the scope, or an exception thrown by a subtask? If you do want to distinguish, it would help to explain the reason in the question (i.e. the ultimate goal).

In general, there should be two paths: the happy-path for result and then a path of an exception being thrown (for whatever reason).

1
paulsm4 On

SUGGESTION: Encapsulate the functionality you need in a subclass.

From the Incubator documentation:

https://download.java.net/java/early_access/loom/docs/api/jdk.incubator.concurrent/jdk/incubator/concurrent/StructuredTaskScope.html

Extending StructuredTaskScope

StructuredTaskScope can be extended, and the handleComplete overridden, to implement policies other than those implemented by ShutdownOnSuccess and ShutdownOnFailure. The method may be overridden to, for example, collect the results of subtasks that complete with a result and ignore subtasks that fail. It may collect exceptions when subtasks fail. It may invoke the shutdown method to shut down and cause join to wakeup when some condition arises.

A subclass will typically define methods to make available results, state, or other outcome to code that executes after the join method. A subclass that collects results and ignores subtasks that fail may define a method that returns a collection of results. A subclass that implements a policy to shut down when a subtask fails may define a method to retrieve the exception of the first subtask to fail.