8308400: add ForceEarlyReturn support for virtual threads

Reviewed-by: alanb, lmesnik
This commit is contained in:
Serguei Spitsyn 2023-05-25 02:29:22 +00:00
parent 1451ac1770
commit 89b99143ac
9 changed files with 522 additions and 45 deletions
src/hotspot/share/prims
test/hotspot/jtreg
ProblemList-Virtual.txt
serviceability/jvmti/vthread

@ -3071,8 +3071,12 @@ err = (*jvmti)->Deallocate(jvmti, stack_info);
<category id="ForceEarlyReturn" label="Force Early Return">
<intro>
These functions allow an agent to force a method
to return at any point during its execution.
These functions allow an agent to force a return from the current frame.
The specified thread must be suspended or must be the current thread.
These functions may be used to force a return from the current frame
of a virtual thread when it is suspended at an event.
An implementation may support forcing a return from the current frame
of a suspended virtual thread in other cases.
The method which will return early is referred to as the <i>called method</i>.
The called method is the current method
(as defined by
@ -3080,7 +3084,6 @@ err = (*jvmti)-&gt;Deallocate(jvmti, stack_info);
for the specified thread at
the time the function is called.
<p/>
The specified thread must be suspended or must be the current thread.
The return occurs when execution of Java programming
language code is resumed on this thread.
Between calling one of these functions and resumption
@ -3136,7 +3139,7 @@ err = (*jvmti)-&gt;Deallocate(jvmti, stack_info);
<error id="JVMTI_ERROR_OPAQUE_FRAME">
Attempted to return early from a frame
corresponding to a native method.
The thread is a virtual thread and the implementation is
The thread is a suspended virtual thread and the implementation was
unable to force its current frame to return.
Or the implementation is unable to provide
this functionality on this frame.
@ -3188,7 +3191,7 @@ err = (*jvmti)-&gt;Deallocate(jvmti, stack_info);
<error id="JVMTI_ERROR_OPAQUE_FRAME">
Attempted to return early from a frame
corresponding to a native method.
The thread is a virtual thread and the implementation is
The thread is a suspended virtual thread and the implementation was
unable to force its current frame to return.
Or the implementation is unable to provide
this functionality on this frame.
@ -3236,7 +3239,7 @@ err = (*jvmti)-&gt;Deallocate(jvmti, stack_info);
<error id="JVMTI_ERROR_OPAQUE_FRAME">
Attempted to return early from a frame
corresponding to a native method.
The thread is a virtual thread and the implementation is
The thread is a suspended virtual thread and the implementation was
unable to force its current frame to return.
Or the implementation is unable to provide
this functionality on this frame.
@ -3281,7 +3284,7 @@ err = (*jvmti)-&gt;Deallocate(jvmti, stack_info);
<error id="JVMTI_ERROR_OPAQUE_FRAME">
Attempted to return early from a frame
corresponding to a native method.
The thread is a virtual thread and the implementation is
The thread is a suspended virtual thread and the implementation was
unable to force its current frame to return.
Or the implementation is unable to provide
this functionality on this frame.
@ -3326,7 +3329,7 @@ err = (*jvmti)-&gt;Deallocate(jvmti, stack_info);
<error id="JVMTI_ERROR_OPAQUE_FRAME">
Attempted to return early from a frame
corresponding to a native method.
The thread is a virtual thread and the implementation is
The thread is a suspended virtual thread and the implementation was
unable to force its current frame to return.
Or the implementation is unable to provide
this functionality on this frame.
@ -3365,7 +3368,7 @@ err = (*jvmti)-&gt;Deallocate(jvmti, stack_info);
<error id="JVMTI_ERROR_OPAQUE_FRAME">
Attempted to return early from a frame
corresponding to a native method.
The thread is a virtual thread and the implementation is
The thread is a suspended virtual thread and the implementation was
unable to force its current frame to return.
Or the implementation is unable to provide
this functionality on this frame.

@ -1886,22 +1886,11 @@ JvmtiEnv::PopFrame(jthread thread) {
if (err != JVMTI_ERROR_NONE) {
return err;
}
bool is_virtual = thread_obj != nullptr && thread_obj->is_a(vmClasses::BaseVirtualThread_klass());
bool self = java_thread == current_thread;
if (is_virtual) {
if (!is_JavaThread_current(java_thread, thread_obj)) {
if (!is_vthread_suspended(thread_obj, java_thread)) {
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
if (java_thread == nullptr) { // unmounted virtual thread
return JVMTI_ERROR_OPAQUE_FRAME;
}
}
} else { // platform thread
if (java_thread != current_thread && !java_thread->is_suspended() &&
!java_thread->is_carrier_thread_suspended()) {
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
err = check_non_suspended_or_opaque_frame(java_thread, thread_obj, self);
if (err != JVMTI_ERROR_NONE) {
return err;
}
// retrieve or create the state
@ -1919,8 +1908,8 @@ JvmtiEnv::PopFrame(jthread thread) {
MutexLocker mu(JvmtiThreadState_lock);
UpdateForPopTopFrameClosure op(state);
if (java_thread == current_thread) {
op.doit(java_thread, true /* self */);
if (self) {
op.doit(java_thread, self);
} else {
Handshake::execute(&op, java_thread);
}

@ -1381,6 +1381,30 @@ JvmtiEnvBase::get_threadOop_and_JavaThread(ThreadsList* t_list, jthread thread,
return JVMTI_ERROR_NONE;
}
// Check for JVMTI_ERROR_NOT_SUSPENDED and JVMTI_ERROR_OPAQUE_FRAME errors.
// Used in PopFrame and ForceEarlyReturn implementations.
jvmtiError
JvmtiEnvBase::check_non_suspended_or_opaque_frame(JavaThread* jt, oop thr_obj, bool self) {
bool is_virtual = thr_obj != nullptr && thr_obj->is_a(vmClasses::BaseVirtualThread_klass());
if (is_virtual) {
if (!is_JavaThread_current(jt, thr_obj)) {
if (!is_vthread_suspended(thr_obj, jt)) {
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
if (jt == nullptr) { // unmounted virtual thread
return JVMTI_ERROR_OPAQUE_FRAME;
}
}
} else { // platform thread
if (!self && !jt->is_suspended() &&
!jt->is_carrier_thread_suspended()) {
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
}
return JVMTI_ERROR_NONE;
}
jvmtiError
JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject object, jvmtiMonitorUsage* info_ptr) {
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
@ -2036,10 +2060,12 @@ JvmtiEnvBase::force_early_return(jthread thread, jvalue value, TosState tos) {
oop thread_obj = nullptr;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (thread_obj != nullptr && thread_obj->is_a(vmClasses::BaseVirtualThread_klass())) {
// No support for virtual threads (yet).
return JVMTI_ERROR_OPAQUE_FRAME;
if (err != JVMTI_ERROR_NONE) {
return err;
}
bool self = java_thread == current_thread;
err = check_non_suspended_or_opaque_frame(java_thread, thread_obj, self);
if (err != JVMTI_ERROR_NONE) {
return err;
}
@ -2058,8 +2084,8 @@ JvmtiEnvBase::force_early_return(jthread thread, jvalue value, TosState tos) {
}
SetForceEarlyReturn op(state, value, tos);
if (java_thread == current_thread) {
op.doit(java_thread, true /* self */);
if (self) {
op.doit(java_thread, self);
} else {
Handshake::execute(&op, java_thread);
}
@ -2075,12 +2101,6 @@ SetForceEarlyReturn::doit(Thread *target, bool self) {
if (java_thread->is_exiting()) {
return; /* JVMTI_ERROR_THREAD_NOT_ALIVE (default) */
}
if (!self) {
if (!java_thread->is_suspended()) {
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
return;
}
}
// Check to see if a ForceEarlyReturn was already in progress
if (_state->is_earlyret_pending()) {

@ -224,6 +224,10 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
// Check if VirtualThread or BoundVirtualThread is suspended.
static bool is_vthread_suspended(oop vt_oop, JavaThread* jt);
// Check for JVMTI_ERROR_NOT_SUSPENDED and JVMTI_ERROR_OPAQUE_FRAME errors.
// Used in PopFrame and ForceEarlyReturn implementations.
static jvmtiError check_non_suspended_or_opaque_frame(JavaThread* jt, oop thr_obj, bool self);
static JvmtiEnv* JvmtiEnv_from_jvmti_env(jvmtiEnv *env) {
return (JvmtiEnv*)((intptr_t)env - in_bytes(jvmti_external_offset()));
};

@ -99,6 +99,7 @@ vmTestbase/nsk/jdi/ExceptionEvent/catchLocation/location002/TestDescription.java
## Note forceEarlyReturn002 was converted to support vthreads. The rest were not
## since there is no added value (JVMTI_ERROR_OPAQUE_FRAME is expected).
vmTestbase/nsk/jdi/ThreadReference/forceEarlyReturn/forceEarlyReturn002/forceEarlyReturn002.java
vmTestbase/nsk/jdi/ThreadReference/forceEarlyReturn/forceEarlyReturn014/forceEarlyReturn014.java 8285415 generic-all
vmTestbase/nsk/jdi/stress/serial/forceEarlyReturn001/TestDescription.java 8285415 generic-all
vmTestbase/nsk/jdi/stress/serial/forceEarlyReturn002/TestDescription.java 8285415 generic-all

@ -115,10 +115,6 @@ test_unsupported_jvmti_functions(jvmtiEnv *jvmti, JNIEnv *jni, jthread vthread,
fatal(jni, "Virtual threads are not supported");
}
LOG("Testing ForceEarlyReturnVoid\n");
err = jvmti->ForceEarlyReturnVoid(vthread);
check_jvmti_error_opaque_frame(jni, "ForceEarlyReturnVoid", err);
LOG("Testing GetThreadCpuTime\n");
err = jvmti->GetThreadCpuTime(vthread, &nanos);
check_jvmti_error_unsupported_operation(jni, "GetThreadCpuTime", err);

@ -0,0 +1,272 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test id=default
* @summary Verifies JVMTI ForceEarlyReturn support for virtual threads.
* @requires vm.continuations
* @run main/othervm/native -agentlib:ForceEarlyReturnTest ForceEarlyReturnTest
*/
/*
* @test id=no-vmcontinuations
* @summary Verifies JVMTI ForceEarlyReturn support for bound virtual threads.
* @run main/othervm/native -agentlib:ForceEarlyReturnTest -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations ForceEarlyReturnTest
*/
/*
* @test id=platform
* @summary Verifies JVMTI ForceEarlyReturn support for platform threads.
* @run main/othervm/native -agentlib:ForceEarlyReturnTest ForceEarlyReturnTest platform
*/
import java.lang.AssertionError;
/*
* The test exercises the JVMTI function ForceEarlyReturn.
* The test creates a new virtual or platform thread.
* Its method run() invokes the following methods:
* - method A() that is blocked on a monitor
* - method B() that is stopped at a breakpoint
* - method C() that forces agent to call ForceEarlyReturn on its own thread
* JVMTI ForceEarlyReturn is called in all cases.
*/
public class ForceEarlyReturnTest {
private static final String agentLib = "ForceEarlyReturnTest";
static final int JVMTI_ERROR_NONE = 0;
static final int THREAD_NOT_SUSPENDED = 13;
static final int OPAQUE_FRAME = 32;
static final int PASSED = 0;
static final int FAILED = 2;
static final int expValA1 = 111;
static final int expValA2 = 222;
static final String expValB1 = "B1";
static final String expValB2 = "B2";
static final String expValB3 = "B3";
static void log(String str) { System.out.println(str); }
static native void prepareAgent(Class taskClass);
static native void suspendThread(Thread thread);
static native void resumeThread(Thread thread);
static native void ensureAtBreakpoint();
static native void notifyAtBreakpoint();
static native int forceEarlyReturnV(Thread thread);
static native int forceEarlyReturnI(Thread thread, int val);
static native int forceEarlyReturnO(Thread thread, Object obj);
static int status = PASSED;
static boolean is_virtual = true;
static void setFailed(String msg) {
log("\nFAILED: " + msg);
status = FAILED;
}
static void throwFailed(String msg) {
log("\nFAILED: " + msg);
throw new RuntimeException("ForceEarlyReturnTest failed!");
}
public static void main(String args[]) {
is_virtual = !(args.length > 0 && args[0].equals("platform"));
run();
if (status == FAILED) {
throwFailed("ForceEarlyReturnTest!");
}
log("\nForceEarlyReturnTest passed");
}
public static void run() {
TestTask testTask = new TestTask();
Thread testTaskThread = null;
int errCode;
prepareAgent(TestTask.class);
log("\nMain #A: method A() must be blocked on entering a synchronized statement");
if (is_virtual) {
testTaskThread = Thread.ofVirtual().name("TestTaskThread").start(testTask);
} else {
testTaskThread = Thread.ofPlatform().name("TestTaskThread").start(testTask);
}
{
TestTask.ensureAtPointA();
log("\nMain #A.1: unsuspended");
errCode = forceEarlyReturnI(testTaskThread, expValA1);
if (errCode != THREAD_NOT_SUSPENDED) {
throwFailed("Main #A.1: expected THREAD_NOT_SUSPENDED instead of: " + errCode);
} else {
log("Main #A.1: got expected THREAD_NOT_SUSPENDED");
}
log("\nMain #A.2: suspended");
suspendThread(testTaskThread);
errCode = forceEarlyReturnI(testTaskThread, expValA2);
if (errCode != JVMTI_ERROR_NONE) {
throwFailed("Main #A.2: expected JVMTI_ERROR_NONE instead of: " + errCode);
} else {
log("Main #A.2: got expected JVMTI_ERROR_NONE");
}
resumeThread(testTaskThread);
TestTask.clearDoLoop();
TestTask.sleep(5);
}
log("\nMain #B: method B() must be blocked in a breakpoint event handler");
{
ensureAtBreakpoint();
log("\nMain #B.1: unsuspended");
errCode = forceEarlyReturnO(testTaskThread, expValB1);
if (errCode != THREAD_NOT_SUSPENDED) {
throwFailed("Main #B.1: expected THREAD_NOT_SUSPENDED instead of: " + errCode);
}
log("Main #B.1: got expected THREAD_NOT_SUSPENDED");
log("\nMain #B.2: suspended");
suspendThread(testTaskThread);
errCode = forceEarlyReturnO(testTaskThread, expValB2);
if (errCode != JVMTI_ERROR_NONE) {
throwFailed("Main #B.2: expected JVMTI_ERROR_NONE");
}
log("Main #B.2: got expected JVMTI_ERROR_NONE");
resumeThread(testTaskThread);
notifyAtBreakpoint();
TestTask.sleep(5);
log("\nMain #B.3: unsuspended, call ForceEarlyReturn on own thread");
ensureAtBreakpoint();
notifyAtBreakpoint();
TestTask.sleep(5);
}
log("\nMain #C: method C() calls ForceEarlyReturn on its own thread");
{
// ForceEarlyReturn is called from the test task (own thread) and expected to succeed.
// No suspension of the test task thread is required or can be done in this case.
TestTask.ensureFinished();
}
try {
testTaskThread.join();
} catch (InterruptedException ex) {
throwFailed("Unexpected " + ex);
}
}
static class TestTask implements Runnable {
static void log(String str) { System.out.println(str); }
static volatile boolean doLoop = true;
static volatile boolean atPointA = false;
static volatile boolean finished = false;
static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new RuntimeException("Interruption in TestTask.sleep: \n\t" + e);
}
}
// Ensure thread is ready.
static void ensureAtPointA() {
while (!atPointA) {
sleep(1);
}
}
// Ensure thread is finished.
static void ensureFinished() {
while (!finished) {
sleep(1);
}
}
static void clearDoLoop() {
doLoop = false;
}
public void run() {
log("TestTask.run: started");
int valA2 = A();
if (valA2 != expValA2) {
setFailed("TestTask.A: expValA2: " + expValA2 + "got: " + valA2);
}
sleep(1); // to cause yield
String valB2 = B(false, expValB2); // false: do not force early return in breakpoint
if (!valB2.equals(expValB2)) {
setFailed("TestTask.B.2: expValB2: " + expValB2 + "got: " + valB2);
}
sleep(1); // to cause yield
String valB3 = B(true, expValB3); // true: force early return in breakpoint
if (!valB3.equals(expValB3)) {
setFailed("TestTask.B.3: expected valB3: " + expValB3 + "got: " + valB3);
}
sleep(1); // to cause yield
C();
finished = true;
}
// Method is busy in a while loop.
// ForceEarlyReturn is used two times:
// - when not suspended: THREAD_NOT_SUSPENDED is expected
// - when suspended: JVMTI_ERROR_NONE is expected
static int A() {
log("TestTask.A: started");
atPointA = true;
while (doLoop) {
}
log("TestTask.A: finished");
return 0;
}
// A breakpoint is set at start of this method.
// ForceEarlyReturn is used two times:
// - when not suspended: THREAD_NOT_SUSPENDED is expected
// - when suspended: expected to succeed
static String B(boolean forceRet, String retObj) {
log("TestTask.B: started");
return "00";
}
// This method uses ForceEarlyReturn on its own thread. It is expected to return OPAQUE_FRAME.
static void C() {
log("TestTask.C: started");
int errCode = ForceEarlyReturnTest.forceEarlyReturnV(Thread.currentThread());
if (errCode == OPAQUE_FRAME) {
log("TestTask.C: got expected OPAQUE_FRAME");
} else {
setFailed("TestTask.C: expected OPAQUE_FRAME from ForceEarlyReturn instead of: " + errCode);
}
}
}
}

@ -0,0 +1,196 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <stdio.h>
#include <string.h>
#include "jvmti.h"
#include "jvmti_common.h"
extern "C" {
static jvmtiEnv *jvmti = NULL;
static jmethodID mid_B = NULL;
static jrawMonitorID monitor = NULL;
static volatile bool bp_sync_reached = false;
static void JNICALL
Breakpoint(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread,
jmethodID method, jlocation location) {
jvmtiError err;
if (method != mid_B) {
fatal(jni, "Breakpoint: Failed with wrong location: expected in method TestTask.B()");
}
LOG("Breakpoint: In method TestTask.B() before sync section\n");
{
RawMonitorLocker rml(jvmti, jni, monitor);
bp_sync_reached = true;
rml.wait(0);
}
LOG("Breakpoint: In method TestTask.B() after sync section\n");
jint force_return = 0;
err = jvmti->GetLocalInt(thread, 0 /* top frame */, 0 /* slot #0 */, &force_return);
check_jvmti_status(jni, err, "Breakpoint: Failed in JVMTI GetLocalInt");
if (force_return != 0) {
jobject ret_obj = NULL;
err = jvmti->ClearBreakpoint(mid_B, 0);
check_jvmti_status(jni, err, "Breakpoint: Failed in JVMTI ClearBreakpoint");
err = jvmti->GetLocalObject(thread, 0 /* top frame */, 1 /* slot #1 */, &ret_obj);
check_jvmti_status(jni, err, "Breakpoint: Failed in JVMTI GetLocalObject");
LOG("Breakpoint: Self early return from method TestTask.B()\n");
err = jvmti->ForceEarlyReturnObject(thread, ret_obj);
LOG("Breakpoint: ForceEarlyReturnObject returned code: %s (%d)\n", TranslateError(err), err);
check_jvmti_status(jni, err, "Breakpoint: Failed in ForceEarlyReturnObject");
}
LOG("Breakpoint: In method TestTask.B() finished\n");
}
jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
static jvmtiCapabilities caps;
static jvmtiEventCallbacks callbacks;
jvmtiError err;
jint res;
LOG("Agent init\n");
res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
if (res != JNI_OK || jvmti == NULL) {
LOG("Agent init: Failed in GetEnv!\n");
return JNI_ERR;
}
err = jvmti->GetPotentialCapabilities(&caps);
if (err != JVMTI_ERROR_NONE) {
LOG("Agent init: Failed in GetPotentialCapabilities: %s (%d)\n", TranslateError(err), err);
return JNI_ERR;
}
err = jvmti->AddCapabilities(&caps);
if (err != JVMTI_ERROR_NONE) {
LOG("Agent init: Failed in AddCapabilities: %s (%d)\n", TranslateError(err), err);
return JNI_ERR;
}
err = jvmti->GetCapabilities(&caps);
if (err != JVMTI_ERROR_NONE) {
LOG("Agent init: Failed in GetCapabilities: %s (%d)\n", TranslateError(err), err);
return JNI_ERR;
}
if (!caps.can_generate_breakpoint_events) {
LOG("Agent init: Failed: Breakpoint event is not implemented\n");
return JNI_ERR;
}
callbacks.Breakpoint = &Breakpoint;
err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
if (err != JVMTI_ERROR_NONE) {
LOG("Agent init: Failed in SetEventCallbacks: %s (%d)\n", TranslateError(err), err);
return JNI_ERR;
}
monitor = create_raw_monitor(jvmti, "Raw monitor to test");
return JNI_OK;
}
extern JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
return Agent_Initialize(jvm, options, reserved);
}
JNIEXPORT void JNICALL
Java_ForceEarlyReturnTest_prepareAgent(JNIEnv *jni, jclass cls, jclass task_clazz) {
jvmtiError err;
LOG("Main: prepareAgent started\n");
if (jvmti == NULL) {
fatal(jni, "prepareAgent: Failed as JVMTI client was not properly loaded!\n");
}
mid_B = jni->GetStaticMethodID(task_clazz, "B", "(ZLjava/lang/String;)Ljava/lang/String;");
if (mid_B == NULL) {
fatal(jni, "prepareAgent: Failed to find Method ID for method: TestTask.B()\n");
}
err = jvmti->SetBreakpoint(mid_B, 0); // location: 0
check_jvmti_status(jni, err, "prepareAgent: Failed in JVMTI SetBreakpoint");
set_event_notification_mode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_BREAKPOINT, NULL);
LOG("Main: prepareAgent finished\n");
}
JNIEXPORT void JNICALL
Java_ForceEarlyReturnTest_suspendThread(JNIEnv *jni, jclass cls, jthread thread) {
LOG("Main: suspendThread\n");
suspend_thread(jvmti, jni, thread);
}
JNIEXPORT void JNICALL
Java_ForceEarlyReturnTest_resumeThread(JNIEnv *jni, jclass cls, jthread thread) {
LOG("Main: resumeThread\n");
resume_thread(jvmti, jni, thread);
}
JNIEXPORT jint JNICALL
Java_ForceEarlyReturnTest_forceEarlyReturnV(JNIEnv *jni, jclass cls, jthread thread) {
jvmtiError err = jvmti->ForceEarlyReturnVoid(thread);
LOG("Main: forceEarlyReturn: ForceEarlyReturnVoid returned code: %s (%d)\n", TranslateError(err), err);
return (jint)err;
}
JNIEXPORT jint JNICALL
Java_ForceEarlyReturnTest_forceEarlyReturnI(JNIEnv *jni, jclass cls, jthread thread, jint val) {
jvmtiError err = jvmti->ForceEarlyReturnInt(thread, val);
LOG("Main: forceEarlyReturn: ForceEarlyReturnInt returned code: %s (%d)\n", TranslateError(err), err);
return (jint)err;
}
JNIEXPORT jint JNICALL
Java_ForceEarlyReturnTest_forceEarlyReturnO(JNIEnv *jni, jclass cls, jthread thread, jobject obj) {
jvmtiError err = jvmti->ForceEarlyReturnObject(thread, obj);
LOG("Main: forceEarlyReturn: ForceEarlyReturnObject returned code: %s (%d)\n", TranslateError(err), err);
return (jint)err;
}
JNIEXPORT void JNICALL
Java_ForceEarlyReturnTest_ensureAtBreakpoint(JNIEnv *jni, jclass cls) {
bool need_stop = false;
LOG("Main: ensureAtBreakpoint\n");
while (!need_stop) {
RawMonitorLocker rml(jvmti, jni, monitor);
need_stop = bp_sync_reached;
sleep_ms(1); // 1 millisecond
}
}
JNIEXPORT void JNICALL
Java_ForceEarlyReturnTest_notifyAtBreakpoint(JNIEnv *jni, jclass cls) {
LOG("Main: notifyAtBreakpoint\n");
RawMonitorLocker rml(jvmti, jni, monitor);
bp_sync_reached = false;
rml.notify_all();
}
} // extern "C"

@ -87,10 +87,6 @@ test_unsupported_jvmti_functions(jvmtiEnv *jvmti, JNIEnv *jni, jthread vthread)
LOG("Testing JVMTI functions which should not accept a virtual thread argument\n");
LOG("Testing ForceEarlyReturnVoid\n");
err = jvmti->ForceEarlyReturnVoid(vthread);
check_jvmti_error_opaque_frame(jni, "ForceEarlyReturnVoid", err);
LOG("Testing GetThreadCpuTime\n");
err = jvmti->GetThreadCpuTime(vthread, &nanos);
check_jvmti_error_unsupported_operation(jni, "GetThreadCpuTime", err);