<html class="apple-mail-supports-explicit-dark-mode"><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div dir="ltr"></div><div dir="ltr">I suspect you have a race condition in your server - virtual threads with the lower cost context switching often exacerbates this. </div><div dir="ltr"><br></div><div dir="ltr">In addition to the Java details I would dump the kernel level queue and socket details and add the logging to show exactly which socket is stuck along with these details. </div><div dir="ltr"><br></div><div dir="ltr">I’m not saying that there can’t possibly be a bug, but the inability to create a standalone test case seems to say it isn’t a bug. </div><div dir="ltr"><br></div><div dir="ltr"><blockquote type="cite">On Feb 11, 2026, at 10:01 AM, Matthew Swift <matthew.swift@gmail.com> wrote:<br><br></blockquote></div><blockquote type="cite"><div dir="ltr"><div dir="ltr">Hi,<div><br></div><div>I suspect that I've discovered a bug in the IO polling mechanism used by virtual threads on Windows. I've been able to systematically reproduce the problem on Windows 2016, 2019 and 2022 Intel x64 using both JDK 25.0.2 and 25.0.3 EA build 01, and also JDK 27 EA 8 build. The problem occurs when the server attempts to send a large response message to the client. The server's write operation becomes blocked and is never notified when the socket becomes writable again:</div><div><br></div><div>#203 "Connection writer LDAPS(connId=13 from=/<a href="http://127.0.0.1:61126">127.0.0.1:61126</a> to=/<a href="http://127.0.0.1:1636">127.0.0.1:1636</a>)" virtual WAITING 2026-02-11T12:15:57.559772800Z</div><div> at java.base/java.lang.VirtualThread.park(VirtualThread.java:738)<br> at java.base/java.lang.System$1.parkVirtualThread(System.java:2284)<br> at java.base/java.util.concurrent.locks.LockSupport.park(LockSupport.java:367)<br> at java.base/sun.nio.ch.Poller.poll(Poller.java:197)<br> at java.base/sun.nio.ch.Poller.poll(Poller.java:144)<br> at java.base/sun.nio.ch.NioSocketImpl.park(NioSocketImpl.java:174)<br> at java.base/sun.nio.ch.NioSocketImpl.park(NioSocketImpl.java:200)<br> at java.base/sun.nio.ch.NioSocketImpl.implWrite(NioSocketImpl.java:420)<br> at java.base/sun.nio.ch.NioSocketImpl.write(NioSocketImpl.java:448)<br> at java.base/sun.nio.ch.NioSocketImpl$2.write(NioSocketImpl.java:821)<br> at java.base/java.net.Socket$SocketOutputStream.implWrite(Socket.java:1086)<br> at java.base/java.net.Socket$SocketOutputStream.write(Socket.java:1076)<br> at java.base/sun.security.ssl.SSLSocketOutputRecord.deliver(SSLSocketOutputRecord.java:345)<br> at java.base/sun.security.ssl.SSLSocketImpl$AppOutputStream.write(SSLSocketImpl.java:1306)</div><div> ...</div><div><br></div><div>The read/write pollers are waiting:</div><div><br></div><div>#132 "Read-Poller" RUNNABLE 2026-02-11T12:15:57.558775900Z<br> at java.base/sun.nio.ch.WEPoll.wait(Native Method)<br> at java.base/sun.nio.ch.WEPollPoller.poll(WEPollPoller.java:61)<br> at java.base/sun.nio.ch.Poller.pollerLoop(Poller.java:248)<br> at java.base/java.lang.Thread.run(Thread.java:1474)<br> at java.base/jdk.internal.misc.InnocuousThread.run(InnocuousThread.java:148)<br><br>#133 "Write-Poller" RUNNABLE 2026-02-11T12:15:57.558775900Z<br> at java.base/sun.nio.ch.WEPoll.wait(Native Method)<br> at java.base/sun.nio.ch.WEPollPoller.poll(WEPollPoller.java:61)<br> at java.base/sun.nio.ch.Poller.pollerLoop(Poller.java:248)<br> at java.base/java.lang.Thread.run(Thread.java:1474)<br> at java.base/jdk.internal.misc.InnocuousThread.run(InnocuousThread.java:148)</div><div><br></div><div>The jcmd Thread.vthread_pollers command reports a registered read/write poller, which is expected AFAIK:</div><div><br></div><div> Read I/O pollers:<br> [0] sun.nio.ch.WEPollPoller@246b483f [registered = 1, owner = Thread[#132,Read-Poller,5,InnocuousThreadGroup]]<br><br> Write I/O pollers:<br> [0] sun.nio.ch.WEPollPoller@c523ae0 [registered = 1, owner = Thread[#133,Write-Poller,5,InnocuousThreadGroup]]</div><div><br></div><div>For completeness, the scheduler's state is:</div><div><br></div><div> java.util.concurrent.ForkJoinPool@484dbf67[Running, parallelism = 4, size = 4, active = 0, running = 0, steals = 2164, tasks = 0, submissions = 0, delayed = 3]</div><div><br></div><div>Meanwhile, the client side is blocked waiting for the remainder of the server's response. I have reproduced the problem using several clients, some using blocking IO, some using async IO, and one using JDK's JNDI LDAP client as well. The client always uses platform threads. The problem only happens when the server is running on Windows, never Linux, and only when using virtual IO threads for read/write operations. I have not been able to reproduce the problem when using platform threads in the server for IO: I have a simple feature flag for switching the thread implementation. What's annoying is that I have been unable to isolate the problem into a standalone reproducible test case that I can publish here, despite several attempts. It only occurs in our server application for some reason. I wonder whether it could be due to the server's mixed use of platform and virtual threads for IO in different sub-systems (we're in the process of migrating). Could the polling mechanism used for virtual threads on Windows be interacting with the polling mechanism used for platform threads? I'm just guessing, sadly.</div><div><br></div><div>In summary, I strongly suspect that there is a bug in the Windows IO poller that is being used for virtual threads, but I have no idea how to proceed in debugging it further. Do you have any suggestions as to how I may proceed? </div><div><br></div><div>Thanks in advance,</div><div>Matt</div><div><br></div></div>
</div></blockquote></body></html>