Naming Things - Task and Subtask
Holo The Sage Wolf
holo3146 at gmail.com
Fri May 19 19:16:38 UTC 2023
Eric,
Then comes the question of Scope…
I proposed something like this a long time ago, but at the time people
claimed this was unnecessary abstraction
I am not sure when exactly you talked about this so I don’t have a full
timeline, but I talked in the mailing list of Loom and of Amber several
time about the problem in Java’s current limitations of
“try-with-resources”, and I was told that there is a discussion about
improving it. Having a stronger TWR mechanism that has out-of-the-box
composition, and forces safe-use is a direct generalisation of an abstract
Scope object.
I actually thought about bringing this up again in a separate conversation
when I will have a bit of free time, I think this is an extremely important
tool to add to the Java ecosystem.
but I want to say something (not well thought out yet) like
Coincidentally, I have started working on something similar to this, but
stopped because of (1) time problems and (2) fighting Java’s compiler’s
plugin API wasn’t fun.
My idea was that if you start a structured-task, the plugin that is shipped
with the library will verify on compile time that you don’t use any
scoped-variables that are not initialized. I am planning on finishing this
when my current occupation will end (which will be in about 10 months
unfortunately)
Attila (and still Eric)
It seems like both of you don’t like the TWR part, but I find it extremely
nice to have, and a lot of times prefer it over a lambda argument.
TWR allows us to propagate checked exceptions, which till we add
union-types in generic levels is impossible to do with lambdas, and it
doesn’t have the (justified) restrictions of lambdas.
Scoped-concurrency are meant to be used with “standard” Java coding-style,
so the restrictions on lambdas is a big deal.
I think that:
var scopeBuilder = StructuredTask.ScopeBuilder()
.value<String>("USERNAME",
"duke");try (var scope = scopeBuilder.build(strategy)) {
...
}// alternatively
var scopeBuilder = StructuredTask.ScopeBuilder(strategy)
.value<String>("USERNAME",
"duke");try (var scope = scopeBuilder.start()) {
...
}
Is better (note that this is currently impossible because of how
scopedValued are implemented. Something I talked about in this mailing-list
long ago and was pushed back because Java’s TWR is not good enough to
guarantee no memory leaks from mis-using ScopedValues), and to allow a
different writing-style, we can add a new “StructuredTask” implementation,
of “Stream”:
var results = StructuredTask.StreamBuilder()
.value<String>("USERNAME", "duke")
.flatFork(() -> Stream.of(workers))
.join()
.throwIfFailed() //
"StructuredTask.Stream" doesn't need a "strategy" because it exposes
functions like this instead
.map(Subtask::get) // "structured"
comes into play here, you can't call terminating operators without
first calling "join"
.toList();
On Fri, May 19, 2023 at 9:33 PM Attila Kelemen <attila.kelemen85 at gmail.com>
wrote:
> There could be some restrictions which I didn't notice, but STS takes
> a `ThreadFactory`, so it is not necessarily related to virtual threads
> (though I guess people will use it with virtual threads pretty much
> exclusively for performance reasons).
>
> > ... but it's not clear to me that StructuredTaskScope implies
> ScopedValue?
>
> Scoped values are captured by STS upon its creation and are propagated
> to forked tasks. So, the "Scope" in those names are well justified
> (and the normal calls naturally inherit scoped values of course like a
> thread local).
>
> As for combining scoped values and STS into a single builder: It
> doesn't seem that useful to me, because you don't just want to share
> the scoped values with an STS, but normal calls as well usually (at
> least the use cases I can think of, you would not immediately start an
> STS, but just call a method). However, the part where you don't have
> to use try-with-resources is good of course, but it is an easy to
> write utility (having the convenience in the JDK would be nice though
> of course).
>
> Eric Kolotyluk <eric at kolotyluk.net> ezt írta (időpont: 2023. máj. 19., P,
> 1:07):
> >
> > Just some thoughts on Threads, Task, etc... please ignore if you think I
> am out to lunch...
> >
> > Originally, Project Loom started with the notion of Fibers, but then
> change the name to VirtualThreads. I liked "Fibers" but I agree that
> "VirtualTreads" makes more sense in some ways.
> >
> > When we talk about "Tasks" we usually think about concurrent things,
> where a Task is an abstract concept of concurrency and a Thread is an
> implementation.
> >
> > Task is what
> >
> > Thread is how
> >
> > If we changed the term "VirtualThread" to "Subthread" then there would
> be better symmetry with "Task" and "Subtask" but it's far too late to
> change the name "VirtualThread" and I am not sure if I like the term
> "VirtualTask" but I could live with it.
> >
> > In a way, we could create a symmetry between Task and Thread, where
> every Task has a Thread. Maybe this is a useless symmetry, but creating a
> Task does not mean you have to start the thread, just that it's the root of
> abstraction on concurrency, where thread is the root of implementation.
> >
> > Then comes the question of Scope...
> >
> > I proposed something like this a long time ago, but at the time people
> claimed this was unnecessary abstraction, and then later came Structured
> Concurrency... oh well...
> >
> > I have since seen discussions on StructuredTaskScope and now
> ScopedValue, but it's not clear to me that StructuredTaskScope implies
> ScopedValue? It doesn't but it can. Both StructuredTaskScope and
> ScopedValue are examples of dynamic scope.
> >
> > Maybe instead of saying
> >
> > try (var scope = new StructuredTaskScope<Object>()) {
> >
> > Subtask<String> subtask1 = scope.fork(task1);
> > Subtask<Integer> subtask2 = scope.fork(task2);
> >
> > scope.join();
> >
> > ... process results/exceptions ...
> >
> > } // close
> >
> > we should be saying
> >
> > try (var scope = Task.StructuredScope<Object>()) {
> >
> > Subtask<String> subtask1 = scope.fork(task1);
> > Subtask<Integer> subtask2 = scope.fork(task2);
> >
> > scope.join();
> >
> > ... process results/exceptions ...
> >
> > } // close
> >
> > where we have to figure out what we really mean by "Task" and "Subtask"
> >
> > Given that Subtask is a sub-interface of Supplier<T> , maybe Task should
> also be a sub-interface of Supplier<C> where C is some Collection class.
> This might make concurrent collection based coding more elegant, where we
> state our intention to 'Supply' some collection via get().
> >
> > Where we have
> >
> > private static final ScopedValue<String> USERNAME =
> ScopedValue.newInstance();
> >
> > ScopedValue.runWhere(USERNAME, "duke", () -> {
> > try (var scope = new StructuredTaskScope<String>()) {
> >
> > scope.fork(() -> childTask1());
> > scope.fork(() -> childTask2());
> > scope.fork(() -> childTask3());
> >
> > ...
> > }
> > });
> >
> > we might have
> >
> > private static final Scope.Value<String> USERNAME = Scope.Value();
> >
> > Scope.Value.runWhere(USERNAME, "duke", () -> {
> > try (var scope = new Task.StructuredScope<String>()) {
> >
> > scope.fork(() -> childTask1());
> > scope.fork(() -> childTask2());
> > scope.fork(() -> childTask3());
> >
> > ...
> > }
> > });
> >
> > but I want to say something (not well thought out yet) like
> >
> > List<String> results = Task.Builder(workers)
> > .scope.value<String>("USERNAME", "duke")
> > .scope.structured<String>( context-> {
> > List<Subtask<T>> subtasks
> > = workers.stream().map(scope::fork).toList();
> > context.join()
> > .throwIfFailed(); // Propagate exception if any subtask
> fails
> > // Here, all tasks have succeeded, so compose their results
> > return subtasks.stream().map(Subtask::get).toList();
> > })
> > .start();
> >
> > Where the "try" is implicitly there... where it's boilerplate I don't
> need to see. I am learning to appreciate the Builder Pattern a little more,
> and wondering if Loom could make better use of it.
> >
> > Anyway, just thinking out loud, and perhaps these discussions are better
> shared over beer? I know that the Scala community prefer Scotch, primarily
> Ardbeg, which is perhaps why Scala Architecture looks different than Java
> Architecture ;-)
> >
> > Again
> >
> > There are only two hard things in Computer Science: cache invalidation
> and naming things.
> >
> > -- Phil Karlton
> >
> >
>
--
Holo The Wise Wolf Of Yoitsu
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20230519/635f1744/attachment-0001.htm>
More information about the loom-dev
mailing list