Fixes for virtual threads - #526
Open
lachlan-roberts wants to merge 1 commit into
Open
lachlan-roberts wants to merge 1 commit into
lachlan-roberts wants to merge 1 commit into
Conversation
Use a ReentrantLock instead of synchronized so waiting on a log flush no longer pins the carrier thread on JDK 21, restoring the pre-63422a21 structure of both writers. Remove the GAE_MEMORY_MB parallelism cap; the JVM's CPU-quota default already covers it. Signed-off-by: Lachlan Roberts <lachlan.p.roberts@gmail.com>
ludoch
force-pushed
the
virtualthreads-jdk21-fixes
branch
from
September 30, 2026 17:17
ce6dfe5 to
5c2d3e5
Compare
Collaborator
|
Do another review. It cannot be merge in gh as there are also non visible changes in google3 needed for this change, so do your own review first here. |
Collaborator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
With
appengine.use.virtualthreads=trueonjava21in HTTP connector mode,AppLogsWriterwaits for the previous log-flush RPC insidesynchronized. On JDK 21 that pins the carrier thread, and an F1 instance reports one CPU so it has only one carrier — every other request on the instance stalls until the flush returns.RPC mode (still in released versions; removed from
mainin8f12d12f) is unaffected because it never runs application code on a virtual thread.java25is unaffected because JDK 24+ no longer pins insynchronized(JEP 491).This is what users hit on v5.0.x. On v5.1.0 and current
mainthe Jetty 12 adapter does not run requests on virtual threads at all (63422a21replaced the executor with a boundedForkJoinPool— see Notes), so today the pin is reachable only through the Jetty 12.1 adapter (appengine.use.jetty121=true) or virtual threads the application creates itself. It comes back for the defaultjava21path as soon as #523 restores the real executor, so this fix should land with or before it.Changes
AppLogsWriter(runtime andapi/setup):synchronized→ReentrantLock, restoring the code from before63422a21. A virtual thread unmounts while waiting on aReentrantLock, so the pin is gone; this is the fix JEP 444 recommends. The legacy/virtual-thread fork added in63422a21is dropped: it existed only to work around the monitor, and it had bugs (split log messages could interleave with a child thread's lines;flushAndWait()could return without flushing the caller's lines if its wait on the previous flush was interrupted or timed out).63422a21still pass and are kept.JavaRuntimeMain.configureVirtualThreadParallelism().availableProcessors()already reflects the instance's CPU quota (measured: 1 on an F1, 2 on a B8), so the cap was a no-op on small instances and raised parallelism to 4 on 2 GB ones.getMaxSafeCarrierParallelism()from the Jetty 12.1 adapter, its test, ande2etest.md, an internal planning note that references it and misdescribes how RPC mode dispatches.Notes
63422a21also replaced the Jetty 12 virtual-thread executor with a boundedForkJoinPool, which switches virtual threads off entirely and caps request handling at 1–4 platform threads. That is still onmain; Optimize API clients and Jetty runtime for Virtual Threads and high throughput #523 reverts it. This PR does not touch that code, but it should land before (or with) Optimize API clients and Jetty runtime for Virtual Threads and high throughput #523: once Optimize API clients and Jetty runtime for Virtual Threads and high throughput #523 restores the real virtual-thread executor, the pin fixed here becomes reachable again on the defaultjava21path.63422a21shipped in v5.1.0.synchronizedwill still pin onjava21.AppLogsWriterTest16/16,JavaRuntimeMainTest6/6. The pin itself can't be unit-tested in-process (the scheduler is JVM-global); it was verified with a one-carrier JDK 21 reproduction, and in production-Djdk.tracePinnedThreads=fullor the JFRjdk.VirtualThreadPinnedevent shows whether any pins remain.