Scheduler API Feedback

Mengyang Li dreamlike.vertx at gmail.com
Wed Oct 8 14:57:00 UTC 2025


*Thank you for your reply.*

While testing the code, I found an issue when using the custom scheduler
feature.

When I delegate to the default scheduler, I noticed that the nested virtual
thread created inside another virtual thread does *not* actually “use” my
custom scheduler.
``` java

public static void main(String[] args) throws Throwable {

System.getProperties().setProperty("jdk.virtualThreadScheduler.implClass",
"io.github.dreamlike.CustomerVirtualThreadScheduler");
    CompletableFuture<Void> future = new CompletableFuture<>();
    Thread.ofVirtual()
            .name("parent vt")
            .start(() -> {
                Thread.ofVirtual().name("sub vt")
                        .start(() -> {
                            future.complete(null);
                        });
            });

    future.get();
}

// other file io/github/dreamlike/CustomerVirtualThreadScheduler.java
public class CustomerVirtualThreadScheduler implements
Thread.VirtualThreadScheduler  {

    private final Thread.VirtualThreadScheduler defaultScheduler;

    public CustomerVirtualThreadScheduler(Thread.VirtualThreadScheduler
defaultScheduler) {
        this.defaultScheduler = defaultScheduler;
    }

    @Override
    public void execute(Thread vthread, Runnable task) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getClass());
        System.out.println(vthread.toString() + " " + vthread.getState() +
" currentThread " + thread + " current is virtual " + thread.isVirtual());
        new RuntimeException().printStackTrace();
        defaultScheduler.execute(vthread, task);
    }
}
```

Console output:
``` bash
WARNING: Using custom default scheduler, this is an experimental feature!
class java.lang.Thread
VirtualThread[#27,VirtualThreadA]/runnable RUNNABLE currentThread
Thread[#3,main,5,main] current is virtual false
java.lang.RuntimeException
        at
io.github.dreamlike.CustomerVirtualThreadScheduler.execute(CustomerVirtualThreadScheduler.java:16)
        at
java.base/java.lang.VirtualThread.submitRunContinuation(VirtualThread.java:367)
        at
java.base/java.lang.VirtualThread.externalSubmitRunContinuationOrThrow(VirtualThread.java:456)
        at java.base/java.lang.VirtualThread.start(VirtualThread.java:734)
        at java.base/java.lang.VirtualThread.start(VirtualThread.java:745)
        at
java.base/java.lang.ThreadBuilders$VirtualThreadBuilder.start(ThreadBuilders.java:257)
        at io.github.dreamlike.VTMain.main(VTMain.java:14)
```

After checking the relevant code, it seems that execution goes through
the *first
branch* below.

This behavior doesn’t appear to align with the semantics of a custom
scheduler — the nested virtual thread is submitted directly to
ct.getPool() instead
of going through CustomerVirtualThreadScheduler::execute.
``` java

// VirtualThread.java
private void externalSubmitRunContinuationOrThrow() {
    if (scheduler == DEFAULT_SCHEDULER && currentCarrierThread() instanceof
CarrierThread ct) {
        try {

ct.getPool().externalSubmit(ForkJoinTask.adapt(runContinuation));
        } catch (RejectedExecutionException ree) {
            submitFailed(ree);
            throw ree;
        }
    } else {
        submitRunContinuation(false);
    }
}
```

Best regards,

Mengyang li

Alan Bateman <alan.bateman at oracle.com> 于2025年10月5日周日 00:35写道:

> On 04/10/2025 14:19, dreamlike_ocean lei wrote:
>
> Hello @loom-dev <loom-dev at openjdk.org>,
>
> I have been enjoying the new POLLER_PER_CARRIER design in the Loom repo
> and I really like the direction it is going.
> While building on top of the latest code, I noticed a couple of issues and
> would like to ask for clarification.
>
> Thanks for your mail. It's useful to hear from folks that are trying out
> the experimental support for custom schedulers in the loom repo as this
> will help to inform whether any of the directions prototyped should be
> taken further.
>
> BTW: I assume "dreamlike_ocean lei" isn't your real name. It would be
> helpful to use a real name or affiliation so we have some idea who you are.
>
>
>    1.
>
>    When calling Thread.startVirtualThread, the new virtual thread does
>    not inherit the scheduler of the calling virtual thread, but instead uses
>    DEFAULT_SCHEDULER. What is the reasoning behind this design? Could
>    there be a mechanism to allow implicit inheritance? This would be very
>    helpful for custom schedulers based on the per-core model.
>
> In a system with several virtual thread schedulers in use, which I think
> is what you mean, then it would be unpredictable and problematic to inherit
> the scheduler in many cases. If some library were, on first usage, start a
> virtual thread as a "background thread" that runs indefinitely then
> inheritance would mean it depends on the first usage. Examples where
> inheritance at thread create time is problematic are the thread context
> class loader, and until recently, the inherited access control context.
>
> When you say "custom schedulers based on the per-core model" do you mean
> you are experimenting with a scheduler per core in order to get "carrier
> affinity"  (virtual thread X will always be scheduled on carrier Y). You
> might also be using processor affinity to bind carrier Y to specific sets
> of CPUs.
>
> You might find it simpler to just use one scheduler and keep a mapping of
> virtual thread to carrier. That would remove complications with lifecycle
> that would arise if carriers were to terminate (e.g. idle/keep-alive). It
> might avoid needing to expose APIs to get a virtual thread's scheduler,
> which I think what your second point is about.
>
> -Alan
>

Alan Bateman <alan.bateman at oracle.com> 于2025年10月5日周日 00:35写道:

> On 04/10/2025 14:19, dreamlike_ocean lei wrote:
>
> Hello @loom-dev <loom-dev at openjdk.org>,
>
> I have been enjoying the new POLLER_PER_CARRIER design in the Loom repo
> and I really like the direction it is going.
> While building on top of the latest code, I noticed a couple of issues and
> would like to ask for clarification.
>
> Thanks for your mail. It's useful to hear from folks that are trying out
> the experimental support for custom schedulers in the loom repo as this
> will help to inform whether any of the directions prototyped should be
> taken further.
>
> BTW: I assume "dreamlike_ocean lei" isn't your real name. It would be
> helpful to use a real name or affiliation so we have some idea who you are.
>
>
>    1.
>
>    When calling Thread.startVirtualThread, the new virtual thread does
>    not inherit the scheduler of the calling virtual thread, but instead uses
>    DEFAULT_SCHEDULER. What is the reasoning behind this design? Could
>    there be a mechanism to allow implicit inheritance? This would be very
>    helpful for custom schedulers based on the per-core model.
>
> In a system with several virtual thread schedulers in use, which I think
> is what you mean, then it would be unpredictable and problematic to inherit
> the scheduler in many cases. If some library were, on first usage, start a
> virtual thread as a "background thread" that runs indefinitely then
> inheritance would mean it depends on the first usage. Examples where
> inheritance at thread create time is problematic are the thread context
> class loader, and until recently, the inherited access control context.
>
> When you say "custom schedulers based on the per-core model" do you mean
> you are experimenting with a scheduler per core in order to get "carrier
> affinity"  (virtual thread X will always be scheduled on carrier Y). You
> might also be using processor affinity to bind carrier Y to specific sets
> of CPUs.
>
> You might find it simpler to just use one scheduler and keep a mapping of
> virtual thread to carrier. That would remove complications with lifecycle
> that would arise if carriers were to terminate (e.g. idle/keep-alive). It
> might avoid needing to expose APIs to get a virtual thread's scheduler,
> which I think what your second point is about.
>
> -Alan
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20251008/e69b6ca5/attachment.htm>


More information about the loom-dev mailing list