Business policy inside a StructuredTaskScope
Remi Forax
forax at univ-mlv.fr
Mon Sep 11 21:55:27 UTC 2023
> From: "David" <david.vlijmincx at gmail.com>
> To: "loom-dev" <loom-dev at openjdk.org>
> Sent: Monday, September 11, 2023 11:07:59 PM
> Subject: Business policy inside a StructuredTaskScope
> Hi,
> I have a question about where the business logic / shutdown policy is supposed
> to be when you extend from a StructuredTaskScope. Looking at online examples it
> seems like the business logic is supposed to be in the handleComplete method of
> a class that extends StructuredTaskScope, but is this not too far away from the
> place it is being used?
> Currently, I am using the following scope as it keeps the shutdown policy closer
> to where the scope is used in my code, but I do not know if this is the correct
> way of creating your own scopes.
> class TriggerScope < T > extends StructuredTaskScope < T > {
> final private Function < StructuredTaskScope . Subtask <? extends T >, Boolean >
> trigger ;
> TriggerScope ( Function < StructuredTaskScope . Subtask <? extends T >, Boolean
> > trigger) {
> this . trigger = trigger;
> }
> @Override
> protected void handleComplete ( Subtask <? extends T > subtask) {
> if ( trigger .apply(subtask)) {
> shutdown();
> }
> }
> }
> The above scope allows me to implement a new scope like below:
> public static void main ( String [] args) throws InterruptedException {
> Main main = new Main();
> try ( var scope = new TriggerScope< Product >( main ::trigger)) {
> StructuredTaskScope . Subtask < Product > fork = scope .fork(() -> new Product(
> 50 ));
> StructuredTaskScope . Subtask < Product > fork1 = scope .fork(() -> new Product(
> 100 ));
> StructuredTaskScope . Subtask < Product > fork2 = scope .fork(() -> new Product(
> 150 ));
> scope .join();
> }
> }
> private boolean trigger ( StructuredTaskScope . Subtask <? extends Product >
> subtask) {
> if ( Subtask . State . SUCCESS .equals(subtask.state()) && subtask.get().price()
> > 50 && subtask.get().price() < 150 ){
> System . out .println( "result: " + subtask.get().price());
> return true ;
> }
> return false ;
> }
> In my opinion, this way is more convenient, but is this still a correct
> implementation or does this deviate too much from the original idea of creating
> a scope in its own class?
> Looking forward to hearing your thoughts.
It is Ok but a little dangerous if you start to add fields to the Main object to retrive the price instead of printing it, perhaps it's better to declare tigger static and use Main::trigger instead of main::trigger.
In term of code, State.Sucess is an enum value so you can use == to compare it with the state, instead of calling subtasl.get().price() several times you can store it in a local variable and trigger should be typed as a Predicate (a function that return a boolean).
Now, i agree with you that storing the business logic iside the STS is a king of ugly.
That's why I want to add a STS that see the tasks as a stream, to avoid to store the business logic inside a STS, here is your code rewritten using a Streamable STS.
static boolean isValid(Subtask<Product> task) {
if (task.state() != SUCCESS) {
return false;
}
var price = task.get().price();
return price > 50 && price < 150;
}
...
Optional<Product> resultingProduct;
try(var scope = new StructuredTaskScope.Streamable<Product>>()) { // need a better name
scope.fork(() -> new Product(50));
scope.fork(() -> new Product(100));
scope.fork(() -> new Product(150));
resultingProduct = scope.joinWhile(stream -> stream.filter(Utils::isValid).map(Subtask::get).findFirst());
}
This code can also be written with one mapMulti() instead of filter() + map(), but given that not a lot of people known how mapMulti() works, i've preferred to write this version.
> Kind regards,
> David
regards,
Rémi
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20230911/7e045ef0/attachment-0001.htm>
More information about the loom-dev
mailing list