<div dir="auto">Yep, Just using Thread::yield in the virtual thread should make it happen.<div dir="auto">I was thinking to rely on the thread state after a continuation's run, but I see that it has a similar problem, Good catch!</div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">Il mer 9 lug 2025, 02:35 Liam Miller-Cushon <<a href="mailto:cushon@google.com">cushon@google.com</a>> ha scritto:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div>Hello,</div><div><br></div><div>I am relaying a comment from my colleague Justin Bassett on one of the examples in <a href="https://github.com/openjdk/loom/blob/fibers/loom-docs/CustomSchedulers.md/" target="_blank" rel="noreferrer">https://github.com/openjdk/loom/blob/fibers/loom-docs/CustomSchedulers.md/</a></div><div><br></div><div>---</div><div><br></div>I believe there’s a race condition in the example in CustomSchedulers.md (<a href="https://github.com/openjdk/loom/blob/8540bb13940fb6d2ce8b5fc25ba8e681665fd8b8/loom-docs/CustomSchedulers.md#threadvirtualthreadtask" target="_blank" rel="noreferrer">https://github.com/openjdk/loom/blob/8540bb13940fb6d2ce8b5fc25ba8e681665fd8b8/loom-docs/CustomSchedulers.md#threadvirtualthreadtask</a>). 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);`.<br><br>```<br>@Override<br>public void execute(Runnable task) {<br>    pool.submit(() -> {<br>        var vthreadTask = (Thread.VirtualThreadTask) task;<br>        vthreadTask.attach(new Context(this, Thread.currentThread()));<br>        try {<br>            vthreadTask.run();<br>        } finally {<br>            vthreadTask.attach(null);<br>        }<br>    });<br>}<br>```<br><br>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.<br><br>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.</div>
</blockquote></div>