[External] : Re: A new build and a new structured concurrency API

Remi Forax forax at univ-mlv.fr
Tue Nov 16 14:21:45 UTC 2021


----- Original Message -----
> From: "Remi Forax" <forax at univ-mlv.fr>
> To: "Ron Pressler" <ron.pressler at oracle.com>
> Cc: "loom-dev" <loom-dev at openjdk.java.net>
> Sent: Mardi 16 Novembre 2021 14:51:33
> Subject: Re: [External] : Re: A new build and a new structured concurrency API

> ----- Original Message -----
>> From: "Ron Pressler" <ron.pressler at oracle.com>
>> To: "Remi Forax" <forax at univ-mlv.fr>
>> Cc: "loom-dev" <loom-dev at openjdk.java.net>
>> Sent: Mardi 16 Novembre 2021 12:28:01
>> Subject: Re: [External] : Re: A new build and a new structured concurrency API
> 
>> Hi Rémi!
>> 
>> Let me first respond generally, and then specifically.
>> 
>> Generally, what we realised when designing this API is that the design space for
>> structured concurrency is large, and there is no one right way to do it. That
>> is why the JEP draft says “it is not a goal to provide a definitive structured
>> concurrency API for Java.” StructuredExecutor is not meant to be *the*
>> structured concurrency API, but *a* structured concurrency API. The plan, as
>> you can tell from the draft, is to expose a lower-level API that would allow
>> third-party libraries, or even the JDK at a later point, to add other SC
>> constructs. We’ve played with various designs, including ones very similar to
>> what you’re proposing (including generifying over exception types in fork) and
>> even cleverer and/or more opinionated ones, but settled on something that is a
>> compromise between SC power and familiarity and explicitness, with the intent
>> of allowing external libraries to offer cleverer alternatives. The nice thing
>> about SC is that its use is very local. You can use one construct in one
>> method, and a different one in another.
>> 
>> More specifically, you can bring your own policy handler that calls join, but
>> I’d rather decide on whether or not we want to do that in this API, or
>> regarding passing the executor in the constructor (and keep in mind that
>> different forks in the same session might employ different policies) only after
>> we get feedback on actual use. I think we’re at the point where we shouldn’t
>> hypothesise about the relative value in alternatives, but rather get some
>> feedback about real problems people encounter with what we have here first.
> 
> I've decided to focus on the high-level API first.
> I agree that separating the low-level API and allow anyone to create more high
> level APIs is a good design.
> For me the low-level API which is almost synonymous of StructuredExecutor.
> 
> Do we agree that ShutdownOnSuccess/ShutdownOnFailure are examples of the kind of
> high level APIs that can be written ?
> 
> I believe that passing the StructuredExecutor at construction helps to solve
> several issues.
> As i said, it ties a little more the lifecycle of the Handler policies to the
> lifecycle of the StructuredExecutor.
> It also allows to avoid to have a method accept() to be part of the public API
> (see below).
> There is also no problem to use several policies with each one taking the same
> StructuredExecutor.
> It's less flexible than the current API because you can share a policy handler
> between several different StructuredExecutors but i see that more as something
> bug prone than as a feature.
> 
> Currently, the API is creates from two parts,
>  StructuredExecutor.fork(Callable, BiConsumer)
> and
>  BiConsumer.accept(StructuredExecutor, Future)
> 
> For me, this handshake should be secret and not part of the public API given
> that
> - StructuredExecutor.fork(Callable, BiConsumer) should not be part of the public
> API because users should either use the variant without a BiConsumer or use the
> BiConsumer/Handler API which can be better tailored to the use case the policy
> handler want to solve
> - BiConsumer.accept(StructuredExecutor, Future) should not be part of the public
> API because users don't care at all about how this is implemented.
> 
> By example with a SPI, that make StructuredExecutor.fork(Callable, BiConsumer)
> non visible in StructuredExecutor
>  public class StructuredExecutorSPI {
>    public static <V> Future<V> fork(StructuredExecutor executor, Callable<? extends
>    V> callable, Consumer<? super Future<V>> completerConsumer) {
>      return ...
>    }
>  }
> 
> 
> ShutdownOnSuccess can be written like this:
> 
>  public class ShutdownOnSuccess<V, X extends Exception> {
>    private final StructuredExecutor executor;
> 
>    public ShutdownOnSuccess(StructuredExecutor executor) {
>      this.executor = executor;
>    }
> 
>    public Future<V> fork(CallableWithException<V, X> task) {
>      return StructuredExecutorSPI.fork(executor, task::call, future -> {
>        // do something with the executor and the future
>      });
>    }
> 
>  ...
> }

Thinking a little bit more, using a SPI is an overkill here,
having a method fork inside StructuredExecutor that takes a Callable and a Consumer of Future is not that bad. 

> 
> regards,
> Rémi
> 
>> 
>> — Ron

Rémi

>> 
>> 
>>> On 16 Nov 2021, at 08:34, Remi Forax <forax at univ-mlv.fr> wrote:
>>> 
>>> Hi Ron,
>>> I like the idea of StructuredExecutor + Handler but i think that given that each
>>> Handler (a completion policy?) has it's own semantics,
>>> the API should be twisted a bit.
>>> 
>>> When we have,
>>>  String result;
>>>  try (var executor = StructuredExecutor.open()) {
>>>    var handler = new ShutdownOnSuccess<String>();
>>> 
>>>    executor.fork(() -> fetch(left), handler);
>>>    executor.fork(() -> fetch(right), handler);
>>> 
>>>    executor.join();
>>> 
>>>    result = handler.result(e -> new WebApplicationException(e));
>>>  }
>>> 
>>> 
>>> I notice several points,
>>> - i don't like too much the fact that the handler is independent on the executor
>>> (not constructed with an executor) because it means that a user can create a
>>> handler store it in a static final and try to use it after. The lifercycle of
>>> the Handler should be coupled with the lifecycle on the executor.
>>> - depending on the Handler, the API we want is slightly different, by example
>>> for a ShutdownOnSuccess, it would be cool to also propagate the type of the
>>> exception but it does noyt make a lot of sense to do that in case of a
>>> ShutdownOnFailure.
>>> - join() and result() should be one method, again this is more true with a
>>> ShutdownOnSuccess than with a ShutdownOnFailure.
>>> 
>>> So i think it's better to move the operations fork() and join() on the Handler
>>> so the API can be tweaked to be specific to the completion policy.
>>> 
>>> By examples, for ShutdownOnSucess
>>> 
>>>    int value;
>>>    try(var executor = StructuredExecutor.open()) {
>>>      var shutdownOnSuccess = new ShutdownOnSuccess<Integer, IOException>(executor);
>>>      //shutdownOnSuccess.fork(() -> 3);
>>>      shutdownOnSuccess.fork(() -> { throw new IOException(); });
>>>      value = shutdownOnSuccess.race();
>>>    }
>>>    System.out.println(value);
>>> 
>>> If we change fork() to take a functional interface like this
>>>  public interface CallableWithException<V, X extends Exception> {
>>>    V call() throws X;
>>>  }
>>> 
>>> So the method race(), which is join() + Handler.result() can be declared to
>>> throw X.
>>> 
>>> 
>>> for ShutdownOnFailure, we may keep the classical fork/join API given that we can
>>> access the values using the futures
>>> 
>>>    try(var executor = StructuredExecutor.open()) {
>>>      var shutdownOnFailure = new ShutdownOnFailure(executor);
>>>      var future1 = shutdownOnFailure.fork(() -> 3);
>>>      var future2 = shutdownOnFailure.fork(() -> 4);
>>>      shutdownOnFailure.join();
>>>      value = future1.get() + future2.get();
>>>    }
>>>    System.out.println(value);
>>> 
>>> regards,
>>> Rémi
>>> 
>>> ----- Original Message -----
>>>> From: "Ron Pressler" <ron.pressler at oracle.com>
>>>> To: "loom-dev" <loom-dev at openjdk.java.net>
>>>> Sent: Lundi 15 Novembre 2021 21:21:45
>>>> Subject: A new build and a new structured concurrency API
>>> 
>>>> Hi.
>>>> 
>>>> We have just published a new Early Access build of Project Loom over on
>>>> https://urldefense.com/v3/__https://jdk.java.net/loom/__;!!ACWV5N9M2RV99hQ!aO7oShDxIUNAf-o50JOJP2nRtzk8neuNPy4apg3wV_-5Jmy189ds_8sVUBMhByQ71w$
>>>> 
>>>> The build is based on jdk-18+22, and now requires the --enable-preview flag to
>>>> use Loom features (when compiling, remember to also add `--release 18`).
>>>> 
>>>> The main new feature in this build is a new API for structured concurrency,
>>>> called StructuredExecutor. To learn more about its motivation, capabilities,
>>>> and use, please read this JEP draft [1] and the Javadoc [2]. Pay special
>>>> attention to the new methods added to Future, resultNow and exceptionNow
>>>> [3], and how they complement StructuredExecutor. One of the most exciting
>>>> capabilities of StructuredExecutor is the new structured thread-dump mentioned
>>>> in the JEP draft.
>>>> 
>>>> Another new feature is the ability to use virtual threads as Cleaner threads
>>>> [4]. We have also published a draft JEP for virtual threads [5]. The JEPs, like
>>>> the project, are still a work in progress.
>>>> 
>>>> As always, we appreciate feedback on these features from those who try them.
>>>> Please, download the new EA and tell us about your experience.
>>>> 
>>>> -- Ron
>>>> 
>>>> [1]: http://openjdk.java.net/jeps/8277129
>>>> [2]:
>>>> https://urldefense.com/v3/__https://download.java.net/java/early_access/loom/docs/api/java.base/java/util/concurrent/StructuredExecutor.html__;!!ACWV5N9M2RV99hQ!aO7oShDxIUNAf-o50JOJP2nRtzk8neuNPy4apg3wV_-5Jmy189ds_8sVUBPEUQSvYg$
>>>> [3]:
>>>> https://urldefense.com/v3/__https://download.java.net/java/early_access/loom/docs/api/java.base/java/util/concurrent/Future.html__;!!ACWV5N9M2RV99hQ!aO7oShDxIUNAf-o50JOJP2nRtzk8neuNPy4apg3wV_-5Jmy189ds_8sVUBNG3V2AMg$
>>>> [4]:
>>>> https://urldefense.com/v3/__https://download.java.net/java/early_access/loom/docs/api/java.base/java/lang/ref/Cleaner.html__;!!ACWV5N9M2RV99hQ!aO7oShDxIUNAf-o50JOJP2nRtzk8neuNPy4apg3wV_-5Jmy189ds_8sVUBMQpeQZxg$
> > >> [5]: http://openjdk.java.net/jeps/8277131


More information about the loom-dev mailing list