New Early Access builds

Mark Raynsford org.openjdk at io7m.com
Tue Jun 30 19:25:22 UTC 2020


On 2020-06-30T19:49:55 +0100
Ron Pressler <ron.pressler at oracle.com> wrote:

> You will need to write a custom Executor, more likely an ExecutorService. You’ll probably 
> want to run the execution loop inside close, and perhaps in invokeAll/Any.

Ok, sounds good! Just so that I know that I actually do need to write
one. :)

> 
> Please let us know how it works out.
> 
> Could you perhaps explain why this use-case is important to you and why using 
> a simple single-worker executor that is not run on the current thread is unsatisfactory?

It's a little circuitous, but the basic issue is that I'm working under
soft-realtime constraints (3D rendering), and I'm working with APIs such
as Vulkan that demand that I make requests from specific threads.

Essentially, I have one specific thread that talks to Vulkan, and I
have to guarantee that, at any given moment, I have data available for
that thread to turn into rendering commands for Vulkan.

The typical approach for getting things done under these kinds of
constraints is to break the application up into subsystems, where each
subsystem is assigned exactly one dedicated heavyweight/kernel thread
[0]. For example, I have one subsystem/thread in charge of handling
user input, one subsystem/thread in charge of handling the actual
simulation state (physics, etc), one subsystem/thread in charge of
audio, and one subsystem/thread in charge of rendering. There's no
sharing of state between subsystems, and any communication happens by
having subsystems react to events published on an event queue. For
example, the "simulation" subsystem produces immutable snapshots of
data at a fixed rate, and the rendering subsystem consumes them from
the event queue.

Sharing nothing means that code within a single subsystem doesn't need
to care about synchronization, data races, etc. However, I often want
to express the work within a given subsystem as a set of more or less
cooperative tasks - I just don't want the extra semantic complexity
of dealing with multi-threaded access to data structures, locking, etc.

Therefore, the most obvious approach to me seems to be to schedule
virtual threads directly on the carrier thread assigned to each
subsystem. That way, I get to break my problems up into cooperating
tasks, but I don't have to protect the data structures internal to
the subsystem with synchronization. Without virtual threads, I have to
build things that look and smell a bit like virtual threads (typically
some sort of monadic structure), but that tend to be hard to debug and
have nasty stack traces.

There are exceptions to this, of course. In some cases - such as
asynchronous loading of resources from disk, and so on - where I really
don't care which thread is doing the work. Code that actually has to
manipulate subsystem internal data structures, however, I'd really
prefer to keep strictly single-(kernel-)threaded.

[0] https://en.wikipedia.org/wiki/Entity_component_system

-- 
Mark Raynsford | https://www.io7m.com



More information about the loom-dev mailing list