8325187: JVMTI GetThreadState says virtual thread is JVMTI_THREAD_STATE_INTERRUPTED when it no longer is

Reviewed-by: dholmes, alanb
This commit is contained in:
Serguei Spitsyn 2024-03-19 08:27:55 +00:00
parent 053ff76e14
commit 6eea5d6755
7 changed files with 208 additions and 4 deletions
src/hotspot/share
test/hotspot/jtreg/serviceability/jvmti/vthread/InterruptRawMonitor

@ -1541,6 +1541,7 @@ int java_lang_Thread::_jvmti_thread_state_offset;
int java_lang_Thread::_jvmti_VTMS_transition_disable_count_offset;
int java_lang_Thread::_jvmti_is_in_VTMS_transition_offset;
int java_lang_Thread::_interrupted_offset;
int java_lang_Thread::_interruptLock_offset;
int java_lang_Thread::_tid_offset;
int java_lang_Thread::_continuation_offset;
int java_lang_Thread::_park_blocker_offset;
@ -1554,6 +1555,7 @@ JFR_ONLY(int java_lang_Thread::_jfr_epoch_offset;)
macro(_inheritedAccessControlContext_offset, k, vmSymbols::inheritedAccessControlContext_name(), accesscontrolcontext_signature, false); \
macro(_eetop_offset, k, "eetop", long_signature, false); \
macro(_interrupted_offset, k, "interrupted", bool_signature, false); \
macro(_interruptLock_offset, k, "interruptLock", object_signature, false); \
macro(_tid_offset, k, "tid", long_signature, false); \
macro(_park_blocker_offset, k, "parkBlocker", object_signature, false); \
macro(_continuation_offset, k, "cont", continuation_signature, false); \
@ -1636,6 +1638,9 @@ oop java_lang_Thread::holder(oop java_thread) {
// Note: may return null if the thread is still attaching
return java_thread->obj_field(_holder_offset);
}
oop java_lang_Thread::interrupt_lock(oop java_thread) {
return java_thread->obj_field(_interruptLock_offset);
}
bool java_lang_Thread::interrupted(oop java_thread) {
// Make sure the caller can safely access oops.

@ -355,6 +355,7 @@ class java_lang_Thread : AllStatic {
static int _jvmti_VTMS_transition_disable_count_offset;
static int _jvmti_is_in_VTMS_transition_offset;
static int _interrupted_offset;
static int _interruptLock_offset;
static int _tid_offset;
static int _continuation_offset;
static int _park_blocker_offset;
@ -374,6 +375,8 @@ class java_lang_Thread : AllStatic {
static void release_set_thread(oop java_thread, JavaThread* thread);
// FieldHolder
static oop holder(oop java_thread);
// interruptLock
static oop interrupt_lock(oop java_thread);
// Interrupted status
static bool interrupted(oop java_thread);
static void set_interrupted(oop java_thread, bool val);

@ -251,7 +251,7 @@ int JvmtiRawMonitor::simple_wait(Thread* self, jlong millis) {
{
// This transition must be after we exited the monitor.
ThreadInVMfromNative tivmfn(jt);
if (jt->is_interrupted(true)) {
if (jt->get_and_clear_interrupted()) {
ret = M_INTERRUPTED;
} else {
ThreadBlockInVM tbivm(jt);
@ -262,7 +262,7 @@ int JvmtiRawMonitor::simple_wait(Thread* self, jlong millis) {
}
// Return to VM before post-check of interrupt state
}
if (jt->is_interrupted(true)) {
if (jt->get_and_clear_interrupted()) {
ret = M_INTERRUPTED;
}
}
@ -401,7 +401,7 @@ int JvmtiRawMonitor::raw_wait(jlong millis, Thread* self) {
break;
}
}
if (jt->is_interrupted(true)) {
if (jt->get_and_clear_interrupted()) {
ret = M_INTERRUPTED;
}
} else { // Non-JavaThread re-enter

@ -189,6 +189,16 @@ void JavaThread::set_jvmti_vthread(oop p) {
_jvmti_vthread.replace(p);
}
// If there is a virtual thread mounted then return vthread() oop.
// Otherwise, return threadObj().
oop JavaThread::vthread_or_thread() const {
oop result = vthread();
if (result == nullptr) {
result = threadObj();
}
return result;
}
oop JavaThread::scopedValueCache() const {
return _scopedValueCache.resolve();
}
@ -544,7 +554,6 @@ void JavaThread::interrupt() {
_ParkEvent->unpark();
}
bool JavaThread::is_interrupted(bool clear_interrupted) {
debug_only(check_for_dangling_thread_pointer(this);)
@ -579,7 +588,37 @@ bool JavaThread::is_interrupted(bool clear_interrupted) {
java_lang_Thread::set_interrupted(threadObj(), false);
WINDOWS_ONLY(osthread()->set_interrupted(false);)
}
return interrupted;
}
// This is only for use by JVMTI RawMonitorWait. It emulates the actions of
// the Java code in Object::wait which are not present in RawMonitorWait.
bool JavaThread::get_and_clear_interrupted() {
if (!is_interrupted(false)) {
return false;
}
oop thread_oop = vthread_or_thread();
bool is_virtual = java_lang_VirtualThread::is_instance(thread_oop);
if (!is_virtual) {
return is_interrupted(true);
}
// Virtual thread: clear interrupt status for both virtual and
// carrier threads under the interruptLock protection.
JavaThread* current = JavaThread::current();
HandleMark hm(current);
Handle thread_h(current, thread_oop);
ObjectLocker lock(Handle(current, java_lang_Thread::interrupt_lock(thread_h())), current);
// re-check the interrupt status under the interruptLock protection
bool interrupted = java_lang_Thread::interrupted(thread_h());
if (interrupted) {
assert(this == Thread::current(), "only the current thread can clear");
java_lang_Thread::set_interrupted(thread_h(), false); // clear for virtual
java_lang_Thread::set_interrupted(threadObj(), false); // clear for carrier
WINDOWS_ONLY(osthread()->set_interrupted(false);)
}
return interrupted;
}

@ -524,6 +524,7 @@ private:
void clear_scopedValueBindings();
oop jvmti_vthread() const;
void set_jvmti_vthread(oop p);
oop vthread_or_thread() const;
// Prepare thread and add to priority queue. If a priority is
// not specified, use the priority of the thread object. Threads_lock
@ -1150,6 +1151,10 @@ public:
void interrupt();
bool is_interrupted(bool clear_interrupted);
// This is only for use by JVMTI RawMonitorWait. It emulates the actions of
// the Java code in Object::wait which are not present in RawMonitorWait.
bool get_and_clear_interrupted();
private:
LockStack _lock_stack;

@ -0,0 +1,56 @@
/*
* Copyright (c) 2024, 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
* @bug 8325187
* @summary Verifies JVMTI InterruptThread works for virtual threads.
* @run main/othervm/native -agentlib:InterruptRawMonitor InterruptRawMonitor
*
* @test id=virtual
* @bug 8325187
* @summary Verifies JVMTI InterruptThread works for virtual threads.
* @run main/othervm/native -agentlib:InterruptRawMonitor InterruptRawMonitor -v
*/
import java.util.concurrent.atomic.AtomicBoolean;
public class InterruptRawMonitor {
private static final String AGENT_LIB = "InterruptRawMonitor";
static native void test();
static native void waitForCondition(Thread t);
public static void main(String[] args) throws Exception {
Thread thread;
if (args.length > 0 && "-v".equals(args[0])) {
thread = Thread.ofVirtual().unstarted(InterruptRawMonitor::test);
} else {
thread = Thread.ofPlatform().unstarted(InterruptRawMonitor::test);
}
System.out.println(thread);
thread.start();
waitForCondition(thread);
thread.interrupt();
thread.join();
}
}

@ -0,0 +1,96 @@
/*
* Copyright (c) 2024, 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 <string.h>
#include "jvmti.h"
#include "jvmti_common.hpp"
extern "C" {
static jvmtiEnv *jvmti = nullptr;
static jrawMonitorID monitor = nullptr;
static bool is_waiting = false;
static void check_thread_not_interrupted(JNIEnv *jni, int check_idx) {
jint state = get_thread_state(jvmti, jni, nullptr);
LOG("\ntest: check #%d: Thread State: (0x%x) %s\n",
check_idx, state, TranslateState(state));
if ((state & JVMTI_THREAD_STATE_INTERRUPTED) != 0) {
fatal(jni, "Failed: JVMTI_THREAD_STATE_INTERRUPTED bit expected to be cleared");
}
}
JNIEXPORT void JNICALL
Java_InterruptRawMonitor_waitForCondition(JNIEnv *jni, jclass clazz, jthread thread) {
jint state = 0;
RawMonitorLocker rml(jvmti, jni, monitor);
while (!is_waiting) {
state = get_thread_state(jvmti, jni, thread);
LOG("main: waitForCondition: target Thread State: (0x%x) %s\n",
state, TranslateState(state));
rml.wait(10);
}
state = get_thread_state(jvmti, jni, thread);
LOG("main: waitForCondition: target Thread State: (0x%x) %s\n\n",
state, TranslateState(state));
}
JNIEXPORT void JNICALL
Java_InterruptRawMonitor_test(JNIEnv *jni, jclass clazz) {
RawMonitorLocker rml(jvmti, jni, monitor);
check_thread_not_interrupted(jni, 0);
is_waiting = true;
// expected to be interrupted
jvmtiError err = jvmti->RawMonitorWait(monitor, 0);
LOG("test: JVMTI RawMonitorWait returned expected error code: (%d) %s\n",
err, TranslateError(err));
if (err != JVMTI_ERROR_INTERRUPT) {
fatal(jni, "Failed: expected JVMTI_ERROR_INTERRUPT from RawMonitorWait");
}
check_thread_not_interrupted(jni, 1);
rml.wait(10); // expected to be non-interrupted
check_thread_not_interrupted(jni, 2);
}
extern JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
LOG("Agent_OnLoad started\n");
if (jvm->GetEnv((void **)(&jvmti), JVMTI_VERSION) != JNI_OK) {
return JNI_ERR;
}
monitor = create_raw_monitor(jvmti, "Test Monitor");
LOG("test: JVMTI_THREAD_STATE_INTERRUPTED bit: 0x%x\n", JVMTI_THREAD_STATE_INTERRUPTED);
LOG("Agent_OnLoad finished\n");
return JNI_OK;
}
} // extern "C"