Interaction between virtual threads and ForkJoinPool.commonPool

Levi Aul levi at leviaul.com
Fri Oct 8 20:19:14 UTC 2021


Thanks, this is useful information.

Specifically for parallel stream operations in code we control (which we
have a lot of), is it currently possible, with considerable effort, to
build a proxy object around a threadPerTaskExecutor, that "looks like" a
ForkJoinPool to java.util.stream.AbstractTask et al, so that we could
submit parallel-stream workloads to this fake FJP and have it schedule the
ForkJoinTasks onto virtual-threads? I can't see any way of accomplishing
this myself (save for forking the JVM further), as ForkJoinWorkerThread
isn't an interface but a subclass; but I'm hoping you folks know more
secrets of the internals of java.util.concurrent than I do.

Given the current state of the Loom API, what would you recommend as the
current/tentative "most idiomatic" approach for explicitly using virtual
threads to parallelize e.g. an expensive IO-blocking call inside a mapping
closure in a stream pipeline? Would it be the obvious—i.e. lift the
expensive IO-blocking calls out of the pipeline, with code before the
pipeline first scheduling the operations onto a virtualThreadExecutor,
blocking for all those futures to resolve, and then accessing the
resolved futures from within the pipeline once there's no longer a
possibility of IO-blocking? Or is there something cleaner/simpler that can
be done here?

On Wed, Oct 6, 2021 at 1:21 AM Alan Bateman <Alan.Bateman at oracle.com> wrote:

> On 06/10/2021 02:34, Levi Aul wrote:
> > I have some work projects that have third-party-lib dependencies which
> > schedule background work by creating ForkJoinTasks, and then explicitly
> > submitting those ForkJoinTasks to the ForkJoinPool.commonPool. These
> > background workloads in turn do blocking IO; and so (with high-enough
> > concurrency) they end up blocking the whole set of
> ForkJoinPool.commonPool
> > worker threads for long periods, resulting in awful latencies as other
> > quick tasks scheduled to the commonPool never start.
> >
> > I'm looking into Loom's virtual threads to solve this problem. But, from
> > what I've read so far, the Loom changes by themselves don't seem to do
> > anything to "enhance" or "co-opt" the ForkJoinPool.commonPool, and so
> won't
> > be of much help in my situation.
> >
> > Is this correct, or have I missed something?
> That is correct, the FJP common pool works as before.
>
>
> > Is it possible under the
> > Loom-branch JVM to configure the ForkJoinPool.commonPool to act as a dumb
> > proxy for a VirtualThreadExecutor, such that tasks run against the
> > commonPool get unbounded IO concurrency? Or is it even unnecessary (i.e.
> do
> > *all* ForkJoinPools in Loom park their ForkJoinTasks when they hit
> > low-level runtime blocking-yield-points, such that all ForkJoinTasks are
> > equivalent to "virtual threads" in behavior, even if the ones that are
> > explicitly ForkJoinTasks have no associated isVirtual=true Thread
> object)?
> >
> > Also, to clarify: if a CompletableFuture/Callable/etc. is scheduled onto
> a
> > Loom virtual thread (which is running on a ForkJoinPool-based scheduler),
> > is that workload then implicitly running in the scope of a ForkJoinTask,
> > such that it has a thread-local reference to its (carrier thread's)
> > ForkJoinPool, such that calls that implicitly schedule "through" that
> > reference if available (e.g. stream().parallel()) will schedule as Loom
> > virtual threads? Or do these just end up using the
> ForkJoinPool.commonPool
> > as well?
> If code executing in a virtual thread uses stream parallel() or
> CF.xxAsync then the tasks run in the common pool as before (or the CF
> default executor if overridden). Yes, this is area that needs further
> attention and exploration. There was some discussion about it early on
> but it was kicked down the road to give time to figure out the
> programming model and core implementation. So I expect it will come back.
>
> -Alan
>
>
>


More information about the loom-dev mailing list