[External] : Re: A new build and a new structured concurrency API
forax at univ-mlv.fr
forax at univ-mlv.fr
Tue Nov 16 13:51:33 UTC 2021
----- 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
});
}
...
}
regards,
Rémi
>
> — Ron
>
>
>> 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