An asynchronous exception sent by JVMTI `StopThread` to a virtual thread can be 
processed and thrown at a point where it is unsafe to do so, leaving the 
virtual thread in an invalid state. The unsafe nature of `StopThread` is not an 
issue exclusive to virtual threads, but due to extra code executed during 
mount/unmount transitions, some tests that are safe for platform threads are 
not for virtual threads. In the reported bug, where the async exception is sent 
to a thread executing `Thread.yield` in a loop, the exception ends up being 
thrown on return from `VirtualThread.startTransition`. Since the transition 
bits remain set, the virtual thread hits the reported assert in 
`MountUnmountDisabler::start_transition` at the next unmount attempt.

A similar issue can happen if the exception is thrown right after executing 
`VirtualThread.endFirstTransition` or right before 
`VirtualThread.startFinalTransition` which can be reproduced by calling 
`StopThread` on virtual threads with empty tasks.

The patch fixes these cases and potentially others that can happen if an async 
exception is thrown while executing a method in the `VirtualThread` class, i.e. 
it improves the robustness of the implementation in the presence of async 
exceptions. It is not an attempt to make `StopThread` bulletproof as that would 
be a more ambitious task.

The proposed changes add two extra checks before installing the asynchronous 
exception handshake. The first verifies that the top method is not a 
`VirtualThread` method. The second verifies that the exception will be thrown 
at the current bytecode, i.e. that exception processing will not be deferred to 
a later safepoint poll where the target might already be in one of the unsafe 
methods.

A less restrictive alternative that avoids that second check is to have the 
target defer processing of the handshake as long as it’s unsafe to do so (as 
based on the first check above). If the handshake is still pending when an 
unmount transition begins, we process it and save the exception in the virtual 
thread’s `JvmtiThreadState` to be thrown at the end of the next mount. I have a 
patch implementing this approach, but the code is a bit more involved and I 
wasn’t convinced it was worth it.

I also refactored `StopThread` by introducing `StopThreadClosure` and 
`StopThreadAsyncClosure` handshake classes. This aligns it with the other JVMTI 
methods that use `JvmtiHandshake`, and also keeps the `StopThread` specific 
logic local to the JVMTI code.

The changes include a new JVMTI test that reproduces the reported crash without 
this fix. I also tested the patch in mach5 tiers1-6.

Thanks,
Patricio

---------
- [x] I confirm that I make this contribution in accordance with the [OpenJDK 
Interim AI Policy](https://openjdk.org/legal/ai).

-------------

Commit messages:
 - v1

Changes: https://git.openjdk.org/jdk/pull/31759/files
  Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=31759&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8386116
  Stats: 344 lines in 9 files changed: 293 ins; 46 del; 5 mod
  Patch: https://git.openjdk.org/jdk/pull/31759.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/31759/head:pull/31759

PR: https://git.openjdk.org/jdk/pull/31759

Reply via email to