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:
parent
053ff76e14
commit
6eea5d6755
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;
|
||||
|
||||
|
56
test/hotspot/jtreg/serviceability/jvmti/vthread/InterruptRawMonitor/InterruptRawMonitor.java
Normal file
56
test/hotspot/jtreg/serviceability/jvmti/vthread/InterruptRawMonitor/InterruptRawMonitor.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
96
test/hotspot/jtreg/serviceability/jvmti/vthread/InterruptRawMonitor/libInterruptRawMonitor.cpp
Normal file
96
test/hotspot/jtreg/serviceability/jvmti/vthread/InterruptRawMonitor/libInterruptRawMonitor.cpp
Normal file
@ -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"
|
Loading…
x
Reference in New Issue
Block a user