Virtual threads

A virtual thread that blocks inside a synchronized block pins its carrier: the platform thread underneath cannot be reused while it waits. On a client whose whole point is concurrency, that turns a cheap thread back into an expensive one. This suite asks whether OkHttp does it.

Current results

Loading the latest virtual-thread evidence…

What the suite asserts

BasicLoomTest runs a Dispatcher backed by newVirtualThreadPerTaskExecutor(), fires twenty concurrent HTTPS requests at a MockServer container, and asserts that the JVM printed nothing. The JVM is started with -Djdk.tracePinnedThreads=short, so any pinning event lands on standard output; the test captures that stream and requires it to be empty. It is an assertion about a side effect, which is the only way to observe pinning from outside the JDK.

An observed pinning signature

A recorded OkHttp 5.4.0/JDK 21 result produced this signature. Treat it as an example for interpreting the live result above, not as the current result:

VirtualThread[#51]/runnable@ForkJoinPool-1-worker-3 reason:MONITOR
    okhttp3.internal.http2.Http2Writer.flush(Http2Writer.kt:131) <== monitors:1
    okhttp3.internal.http2.Http2Connection.newStream(Http2Connection.kt:270) <== monitors:2

Http2Connection.newStream holds a monitor across Http2Writer.flush's blocking write. That is a true statement about the published artifact, not a broken test — so the suite runs under the loomTest Gradle task with ignoreFailures, and the assertion stays exactly as written. On JDK 21 through 23, the status collector classifies this predicted failure as expected and supplies the reason in place of the stack trace. On JDK 24 and later the exemption does not apply: a pinning trace there is an unpredicted finding.

Why the answer depends on the JDK

JEP 491, delivered in JDK 24, lets a virtual thread release its carrier while blocking inside a synchronized method or block. The same code that pins on 21 should not pin on 24 and later. Recording that difference — same artifact, same test, different runtime — is exactly what a testbed running against published versions is for. The suite is gated with @EnabledForJreRange(min = JRE.JAVA_21), and the daily run includes both 21 and 25, showing the behavior before and after that JDK change.

References

DocumentWhat it covers
JEP 444 Virtual Threads, final in JDK 21 — the feature under test.
JEP 491 Synchronize Virtual Threads without Pinning, JDK 24 — what should make this finding go away.
Core Libraries: Virtual Threads Pinning, jdk.tracePinnedThreads, and how to read its output.
RFC 9113 HTTP/2 — the multiplexed write path where the monitor is held.

Reading a result

A green loomTest is the interesting outcome, not the boring one: it means either the runtime changed underneath the test or OkHttp stopped pinning. Both are worth knowing about the moment they happen, which is why the finding is recorded on every run rather than muted.

Move a suite into this reporting category only when it is asserting about OkHttp's behaviour — never to quiet a test that is genuinely broken here. Everything under the test task stays fatal.