Race condition in custom schedulers example

Liam Miller-Cushon cushon at google.com
Tue Jul 8 22:37:39 UTC 2025


Hello,

I am relaying a comment from my colleague Justin Bassett on one of the
examples in
https://github.com/openjdk/loom/blob/fibers/loom-docs/CustomSchedulers.md/

---

I believe there’s a race condition in the example in CustomSchedulers.md (
https://github.com/openjdk/loom/blob/8540bb13940fb6d2ce8b5fc25ba8e681665fd8b8/loom-docs/CustomSchedulers.md#threadvirtualthreadtask).
In particular, the `vthreadTask.attach(null)` can race with the earlier
`vthreadTask.attach(new Context(...));`. This happens because the virtual
thread can be scheduled again as soon as its run continuation finishes
under `vthreadTask.run()`, so if the platform thread it was bound to
deschedules and the virtual thread is then scheduled on a different
platform thread, the newly scheduled task’s call to `vthreadTask.attach(new
Context(...));` races with the first platform thread’s
`vthreadTask.attach(null);`.

```
@Override
public void execute(Runnable task) {
    pool.submit(() -> {
        var vthreadTask = (Thread.VirtualThreadTask) task;
        vthreadTask.attach(new Context(this, Thread.currentThread()));
        try {
            vthreadTask.run();
        } finally {
            vthreadTask.attach(null);
        }
    });
}
```

This could be avoided by using a more complicated attachment that includes
a lock that blocks the new platform thread until the first platform thread
finishes the remaining post-run tasks.

It may also be worth mentioning the risk of deadlocks with the virtual
thread: any locks that we might block on in this custom scheduler code must
not be held by the virtual thread, else if it deschedules while holding the
lock, our custom scheduler deadlocks.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20250708/3b7e06cf/attachment-0001.htm>


More information about the loom-dev mailing list