A number of tests that use JDI invokeMethod() support occasionally fail when 
run using virtual threads. The tests fail with:


# ERROR: TEST FAILED: wrong invocation:
# ERROR: invoking debuggee thread instance of 
java.lang.VirtualThread(name='invokemethod010tThr', id=272)
# ERROR: is not suspended again after the invocation 


Although as explained later, this is a misleading message, and does not 
accurately reflect why the test is failing. More on that below.

The root cause of the failure is due to these tests using JDI invokeMethod 
support with the  INVOKE_SINGLE_THREADED flag. This is not always going to work 
with virtual threads because the invoked method is doing a Thread.sleep(400), 
and that is at risk of blocking indefinitely if all other threads are blocked 
form making progress. Note that technically platform threads could fail in the 
same manner. However, the reason the failure only happens now with virtual 
threads is because the implementation of Thread.sleep() differs for virtual 
threads, and may require ownership of a monitor that sometimes is held by 
another thread.

Another issue is that the tests do not do a very good job of error handling 
when this happens, and give the misleading failure reason of the invoked thread 
not being suspended after the invoke completed. The reason it is not suspended 
is because the invoke has actually not completed. There was a timeout that the 
test did not properly note as the cause of the failure. The test (debugger 
side) spawns a thread to do the JDI invokeMethod with, and then waits for it 
with:

                invThr.join(argHandler.getWaitTime()*60000);

This join() times out, but the test assumes once it returns the invoke is 
complete, even though the invoked thread is actually still in the middle of the 
invoke. So that is the reason debuggee invokemethod thread is not currently 
suspended. I've fixed this by having a test check if invThr is still alive 
after the join. If it is, then the test is made to fail at that point, rather 
than continuing on and checking the debuggee threads status. The failure then 
becomes:

nsk.share.TestFailure: TEST FAILED: invoke never completed

At that point a vm.resume() is done to allow the invoke to complete, and the 
test will exit with this failure.

As for avoiding the failure in the first place (the deadlock in the debuggee 
during the invoke), this is really a test bug for relying on 
INVOKE_SINGLE_THREADED and assuming that the invoked thread won't become 
deadlocked. Since there is a Thread.sleep() call in the invoked method, it 
can't make this assumption. From the ObjectReference.invoke() spec:

"By default, all threads in the target VM are resumed while the method is being 
invoked if they were previously suspended by an event or by 
VirtualMachine.suspend() or ThreadReference.suspend(). This is done to prevent 
the deadlocks that will occur if any of the threads own monitors that will be 
needed by the invoked method."

"The resumption of other threads during the invocation can be prevented by 
specifying the INVOKE_SINGLE_THREADED bit flag in the options argument; 
however, there is no protection against or recovery from the deadlocks 
described above, so this option should be used with great caution."

For platform threads, sleep() doesn't require any monitors, so these tests 
never ran into problems before. For virtual threads however there is some 
synchronization done, and potential reliance on other threads not being 
suspended. A way around this is to always use sleep(0), which will at least 
attempt to yield the thread. For platform threads an actual yield is likely. 
For a virtual thread it will not yield in this particular case because the 
virtual thread is pinned to the carrier thread due to the jvmti breakpoint 
callback that is currently in the call chain of the invoked thread. So for 
virtual threads this effectively the same as sitting in a spin loop with no 
yielding. This is ok. CPU wasting is not a concern.

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

Commit messages:
 - Remove invokemethod011 from problem list.
 - Fix invokemethod tests that can fail when using INVOKE_SINGLE_THREADED.
 - Fix invokemethod tests that can fail when using INVOKE_SINGLE_THREADED.

Changes: https://git.openjdk.org/jdk/pull/12420/files
 Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=12420&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8282379
  Stats: 89 lines in 15 files changed: 66 ins; 2 del; 21 mod
  Patch: https://git.openjdk.org/jdk/pull/12420.diff
  Fetch: git fetch https://git.openjdk.org/jdk pull/12420/head:pull/12420

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

Reply via email to