Commits
Tarun Sukhani authored c4f754a5711
fix(vt): audit pass — silent-failure capture, observability, scheduler shutdown
C-class (correctness):
- C1 Invoker.ensureExecutor: consult VirtualThreadConfig in DEV-mode lazy
bootstrap so first request matches init()'s eventual executor type
- C2 JobsPlugin.afterInvocation: wrap callables to log thrown exceptions
(Future-bound submissions don't trigger Thread UncaughtExceptionHandler)
- C3 VirtualThreadFactory: install a default UEH that logs via Play.Logger
H-class (high):
- H1 VirtualThreadScheduledExecutor.scheduleWithFixedDelay: cancel handle
on scheduler.shutdown() between runs — prevents callers from hanging on
get() forever
- H2 Invoker: add public AtomicLong inflightInvocations/totalInvocations
via a centralized submitTracked helper (covers immediate, delayed, and
Promise-resume paths uniformly); PlayStatusPlugin surfaces these in both
text and JSON status, in both platform and VT modes
- H3 WaitForTasksCompletion.waitFor: catch RejectedExecutionException on
resume submit (would otherwise silently strand suspended requests on
scheduler shutdown)
- H4 Mail.getMailGate: guard null Play.configuration and unparseable
play.mail.maxConcurrent — both used to throw before the fallback path
M-class (medium):
- M2 VirtualThreadConfig: blank/whitespace subsystem override now inherits
from global instead of being treated as explicit "false"
- M4 InvokerVirtualThreadTest / VirtualThreadIntegrationTest: switch off
deprecated mirror fields onto Invoker.scheduler facade
- M5 JobsPlugin.onApplicationStop: comment why VT mode skips the platform-
side queue clear (dispatch lambdas vs user tasks)
L-class (low):
- L1 ExecutorFacade.State: drop redundant virtualMode boolean from record
- L3 Mail: name VT executor threads "mail-vthread-N" via VirtualThreadFactory
instead of JDK-default "virtual-N"
- L4 New JobsPluginVirtualThreadTest, MailVirtualThreadTest covering
silent-exception fix + VT plumbing + H4 fallbacks
- L5 New VirtualThreadPinningTest using JFR RecordingStream to assert
request lifecycle does not pin VT carriers (tagged @Tag("vt-pinning"))
- L7 InvokerVirtualThreadTest: add finallyClearsInvocationContextOnVT and
inflightCounterIsDecrementedAfterInvocation tests that exercise the
production lifecycle (the existing test bypassed _finally)
Skipped with rationale (in review report):
- M1 caching reads of VirtualThreadConfig — only 3 production callers, all
at startup
- M3 making deprecated public fields final — breaking ABI
- M6 reflective annotation reads in JobsPlugin.getStatus — minor perf
- M7 Mail newCachedThreadPool — pre-existing pre-VT design
- L2 setDaemon — VTs are always daemon
- L6 JMH benchmark — separate workstream
ant unittest passes on Java 25; new tests +19 cases.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>