JEP 480: Structured Concurrency (Third Preview) Feedback

Winkelman, Kyle G kyle.winkelman at optum.com
Tue Oct 15 17:13:42 UTC 2024


Things that felt strange or annoying:

  *   When subclassing StructuredTaskScope I always have to @Override `join` and `joinUntil` to return my subclass type to provide a fluent api.
  *   When subclassing StructuredTaskScope @Override `handleComplete` I find myself wanting to write a switch expression on Subtask#state(), but if I do, I always need to have a case for UNAVAILABLE and throw IllegalStateException.
  *   Using StructuredTaskScope (no subclassing) feels weird because you must decide to use either the `<>` diamond operator to allow any types or define a type T for restricting subtasks (makes you wonder if you are using it correctly). Whereas, both ShutdownOnFailure and ShutdownOnSuccess just feel right to use.
  *   In IntelliJ the Javadoc of StructuredTaskScope doesn’t seem to properly format and makes it very difficult to read (probably an issue with IntelliJ).
  *   Blocking IO is not interruptible and this can cause confusion when the scope won’t close in a timely manner even though something may have already thrown an exception (probably falls under the Non-Goal of thread cancellation mechanism). e.g. 2 calls to fork one reading a short file and the other reading an long file, short one fails, if I don’t do something to close the long file stream I will read the whole thing (making ShutdownOnFailure not really live up to its name).

I love that StructuredTaskScope interrupts threads on close/shutdown (it allows some short-circuiting that I struggled with when using Executors.newVirtualThreadPerTaskExecutor()). Here is my new favorite code snippet using StructuredTaskScope:

#!java --source 23 --enable-preview

import java.io.*;
import java.util.concurrent.*;
import java.util.function.Supplier;

void main() throws ExecutionException, InterruptedException, IOException {
  System.out.println(process("Hello, World!", "cat"));
}

ProcessResult process(String input, String... command)
    throws ExecutionException, InterruptedException, IOException {
  try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    var process = new ProcessBuilder(command).start();

    scope.fork(write(process::getOutputStream, input));
    var stdout = scope.fork(read(process::getInputStream));
    var stderr = scope.fork(read(process::getErrorStream));
    var exitValue = scope.fork(waitForOrDestroy(process));

    scope.join().throwIfFailed();

    return new ProcessResult(stdout.get(), stderr.get(), exitValue.get());
  }
}

Callable<Void> write(Supplier<OutputStream> outputStreamSupplier, String input) {
  return () -> {
    try (var outputStream = outputStreamSupplier.get()) {
      outputStream.write(input.getBytes());
      return null;
    }
  };
}

Callable<String> read(Supplier<InputStream> inputStreamSupplier) {
  return () -> {
    try (var inputStream = inputStreamSupplier.get()) {
      return new String(inputStream.readAllBytes());
    }
  };
}

Callable<Integer> waitForOrDestroy(Process process) {
  return () -> {
    try {
      return process.waitFor();
    } catch (InterruptedException e) {
      process.destroy();
      throw e;
    }
  };
}

record ProcessResult(String stdout, String stderr, int exitValue) {}


This e-mail, including attachments, may include confidential and/or
proprietary information, and may be used only by the person or entity
to which it is addressed. If the reader of this e-mail is not the intended
recipient or intended recipient’s authorized agent, the reader is hereby
notified that any dissemination, distribution or copying of this e-mail is
prohibited. If you have received this e-mail in error, please notify the
sender by replying to this message and delete this e-mail immediately.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20241015/ebbae78d/attachment-0001.htm>


More information about the loom-dev mailing list