8304303: implement VirtualThread class notifyJvmti methods as C2 intrinsics

Reviewed-by: vlivanov, lmesnik
This commit is contained in:
Serguei Spitsyn 2023-03-20 19:55:16 +00:00
parent 2d0d057d66
commit bc0ed730f2
20 changed files with 325 additions and 174 deletions

View File

@ -218,10 +218,8 @@ JVM_DefineModule
JVM_SetBootLoaderUnnamedModule
# Virtual thread notifications for JVMTI
JVM_VirtualThreadMountBegin
JVM_VirtualThreadMountEnd
JVM_VirtualThreadUnmountBegin
JVM_VirtualThreadUnmountEnd
JVM_VirtualThreadMount
JVM_VirtualThreadUnmount
JVM_VirtualThreadHideFrames
# Scoped values

View File

@ -1933,34 +1933,23 @@ void java_lang_ThreadGroup::serialize_offsets(SerializeClosure* f) {
// java_lang_VirtualThread
int java_lang_VirtualThread::static_notify_jvmti_events_offset;
int java_lang_VirtualThread::static_vthread_scope_offset;
int java_lang_VirtualThread::_carrierThread_offset;
int java_lang_VirtualThread::_continuation_offset;
int java_lang_VirtualThread::_state_offset;
#define VTHREAD_FIELDS_DO(macro) \
macro(static_notify_jvmti_events_offset, k, "notifyJvmtiEvents", bool_signature, true); \
macro(static_vthread_scope_offset, k, "VTHREAD_SCOPE", continuationscope_signature, true); \
macro(_carrierThread_offset, k, "carrierThread", thread_signature, false); \
macro(_continuation_offset, k, "cont", continuation_signature, false); \
macro(_state_offset, k, "state", int_signature, false)
static bool vthread_notify_jvmti_events = JNI_FALSE;
void java_lang_VirtualThread::compute_offsets() {
InstanceKlass* k = vmClasses::VirtualThread_klass();
VTHREAD_FIELDS_DO(FIELD_COMPUTE_OFFSET);
}
void java_lang_VirtualThread::init_static_notify_jvmti_events() {
if (vthread_notify_jvmti_events) {
InstanceKlass* ik = vmClasses::VirtualThread_klass();
oop base = ik->static_field_base_raw();
base->release_bool_field_put(static_notify_jvmti_events_offset, vthread_notify_jvmti_events);
}
}
bool java_lang_VirtualThread::is_instance(oop obj) {
return obj != nullptr && is_subclass(obj->klass());
}
@ -2013,15 +2002,6 @@ void java_lang_VirtualThread::serialize_offsets(SerializeClosure* f) {
}
#endif
bool java_lang_VirtualThread::notify_jvmti_events() {
return vthread_notify_jvmti_events == JNI_TRUE;
}
void java_lang_VirtualThread::set_notify_jvmti_events(bool enable) {
vthread_notify_jvmti_events = enable;
}
// java_lang_Throwable
int java_lang_Throwable::_backtrace_offset;
@ -5387,6 +5367,5 @@ int InjectedField::compute_offset() {
void javaClasses_init() {
JavaClasses::compute_offsets();
JavaClasses::check_offsets();
java_lang_VirtualThread::init_static_notify_jvmti_events();
FilteredFieldsMap::initialize(); // must be done after computing offsets.
}

View File

@ -510,7 +510,6 @@ class java_lang_ThreadGroup : AllStatic {
class java_lang_VirtualThread : AllStatic {
private:
static int static_notify_jvmti_events_offset;
static int static_vthread_scope_offset;
static int _carrierThread_offset;
static int _continuation_offset;
@ -548,9 +547,6 @@ class java_lang_VirtualThread : AllStatic {
static oop continuation(oop vthread);
static int state(oop vthread);
static JavaThreadStatus map_state_to_thread_status(int state);
static bool notify_jvmti_events();
static void set_notify_jvmti_events(bool enable);
static void init_static_notify_jvmti_events();
};

View File

@ -583,6 +583,11 @@ class methodHandle;
do_intrinsic(_Continuation_doYield, jdk_internal_vm_Continuation, doYield_name, continuationDoYield_signature, F_SN) \
do_alias( continuationDoYield_signature, void_int_signature) \
\
/* java/lang/VirtualThread */ \
do_intrinsic(_notifyJvmtiMount, java_lang_VirtualThread, notifyJvmtiMount_name, bool_bool_void_signature, F_RN) \
do_intrinsic(_notifyJvmtiUnmount, java_lang_VirtualThread, notifyJvmtiUnmount_name, bool_bool_void_signature, F_RN) \
do_intrinsic(_notifyJvmtiHideFrames, java_lang_VirtualThread, notifyJvmtiHideFrames_name, bool_void_signature, F_RN) \
\
/* support for UnsafeConstants */ \
do_class(jdk_internal_misc_UnsafeConstants, "jdk/internal/misc/UnsafeConstants") \
\

View File

@ -412,6 +412,9 @@
template(run_finalization_name, "runFinalization") \
template(dispatchUncaughtException_name, "dispatchUncaughtException") \
template(loadClass_name, "loadClass") \
template(notifyJvmtiMount_name, "notifyJvmtiMount") \
template(notifyJvmtiUnmount_name, "notifyJvmtiUnmount") \
template(notifyJvmtiHideFrames_name, "notifyJvmtiHideFrames") \
template(doYield_name, "doYield") \
template(enter_name, "enter") \
template(enterSpecial_name, "enterSpecial") \
@ -550,6 +553,7 @@
template(void_float_signature, "()F") \
template(void_double_signature, "()D") \
template(bool_void_signature, "(Z)V") \
template(bool_bool_void_signature, "(ZZ)V") \
template(int_void_signature, "(I)V") \
template(int_int_signature, "(I)I") \
template(char_char_signature, "(C)C") \

View File

@ -1144,16 +1144,10 @@ JVM_GetEnclosingMethodInfo(JNIEnv* env, jclass ofClass);
* Virtual thread support.
*/
JNIEXPORT void JNICALL
JVM_VirtualThreadMountBegin(JNIEnv* env, jobject vthread, jboolean first_mount);
JVM_VirtualThreadMount(JNIEnv* env, jobject vthread, jboolean hide, jboolean first_mount);
JNIEXPORT void JNICALL
JVM_VirtualThreadMountEnd(JNIEnv* env, jobject vthread, jboolean first_mount);
JNIEXPORT void JNICALL
JVM_VirtualThreadUnmountBegin(JNIEnv* env, jobject vthread, jboolean last_unmount);
JNIEXPORT void JNICALL
JVM_VirtualThreadUnmountEnd(JNIEnv* env, jobject vthread, jboolean last_unmount);
JVM_VirtualThreadUnmount(JNIEnv* env, jobject vthread, jboolean hide, jboolean last_unmount);
JNIEXPORT void JNICALL
JVM_VirtualThreadHideFrames(JNIEnv* env, jobject vthread, jboolean hide);

View File

@ -771,6 +771,11 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method) {
case vmIntrinsics::_IndexPartiallyInUpperRange:
return EnableVectorSupport;
case vmIntrinsics::_blackhole:
#if INCLUDE_JVMTI
case vmIntrinsics::_notifyJvmtiMount:
case vmIntrinsics::_notifyJvmtiUnmount:
case vmIntrinsics::_notifyJvmtiHideFrames:
#endif
break;
default:

View File

@ -50,6 +50,7 @@
#include "opto/runtime.hpp"
#include "opto/rootnode.hpp"
#include "opto/subnode.hpp"
#include "prims/jvmtiThreadState.hpp"
#include "prims/unsafe.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/objectMonitor.hpp"
@ -478,6 +479,14 @@ bool LibraryCallKit::try_to_inline(int predicate) {
case vmIntrinsics::_scopedValueCache: return inline_native_scopedValueCache();
case vmIntrinsics::_setScopedValueCache: return inline_native_setScopedValueCache();
#if INCLUDE_JVMTI
case vmIntrinsics::_notifyJvmtiMount: return inline_native_notify_jvmti_funcs(CAST_FROM_FN_PTR(address, OptoRuntime::notify_jvmti_mount()),
"notifyJvmtiMount");
case vmIntrinsics::_notifyJvmtiUnmount: return inline_native_notify_jvmti_funcs(CAST_FROM_FN_PTR(address, OptoRuntime::notify_jvmti_unmount()),
"notifyJvmtiUnmount");
case vmIntrinsics::_notifyJvmtiHideFrames: return inline_native_notify_jvmti_hide();
#endif
#ifdef JFR_HAVE_INTRINSICS
case vmIntrinsics::_counterTime: return inline_native_time_funcs(CAST_FROM_FN_PTR(address, JfrTime::time_function()), "counterTime");
case vmIntrinsics::_getEventWriter: return inline_native_getEventWriter();
@ -2841,6 +2850,75 @@ bool LibraryCallKit::inline_native_time_funcs(address funcAddr, const char* func
return true;
}
#if INCLUDE_JVMTI
// When notifications are disabled then just update the VTMS transition bit and return.
// Otherwise, the bit is updated in the given function call implementing JVMTI notification protocol.
bool LibraryCallKit::inline_native_notify_jvmti_funcs(address funcAddr, const char* funcName) {
if (!DoJVMTIVirtualThreadTransitions) {
return true;
}
IdealKit ideal(this);
Node* ONE = ideal.ConI(1);
Node* hide = _gvn.transform(argument(1)); // hide argument: true for begin and false for end of VTMS transition
Node* addr = makecon(TypeRawPtr::make((address)&JvmtiVTMSTransitionDisabler::_VTMS_notify_jvmti_events));
Node* notify_jvmti_enabled = ideal.load(ideal.ctrl(), addr, TypeInt::BOOL, T_BOOLEAN, Compile::AliasIdxRaw);
ideal.if_then(notify_jvmti_enabled, BoolTest::eq, ONE); {
// if notifyJvmti enabled then make a call to the given SharedRuntime function
const TypeFunc* tf = OptoRuntime::notify_jvmti_Type();
Node* vt_oop = _gvn.transform(must_be_not_null(argument(0), true)); // VirtualThread this argument
Node* cond = _gvn.transform(argument(2)); // firstMount or lastUnmount argument
sync_kit(ideal);
make_runtime_call(RC_NO_LEAF, tf, funcAddr, funcName, TypePtr::BOTTOM, vt_oop, hide, cond);
ideal.sync_kit(this);
} ideal.else_(); {
// set hide value to the VTMS transition bit in current JavaThread
Node* thread = ideal.thread();
Node* addr = basic_plus_adr(thread, in_bytes(JavaThread::is_in_VTMS_transition_offset()));
const TypePtr *addr_type = _gvn.type(addr)->isa_ptr();
sync_kit(ideal);
access_store_at(nullptr, addr, addr_type, hide, _gvn.type(hide), T_BOOLEAN, IN_NATIVE | MO_UNORDERED);
ideal.sync_kit(this);
} ideal.end_if();
final_sync(ideal);
return true;
}
// If notifications are enabled then just update the temporary VTMS transition bit.
bool LibraryCallKit::inline_native_notify_jvmti_hide() {
if (!DoJVMTIVirtualThreadTransitions) {
return true;
}
IdealKit ideal(this);
Node* ONE = ideal.ConI(1);
Node* addr = makecon(TypeRawPtr::make((address)&JvmtiVTMSTransitionDisabler::_VTMS_notify_jvmti_events));
Node* notify_jvmti_enabled = ideal.load(ideal.ctrl(), addr, TypeInt::BOOL, T_BOOLEAN, Compile::AliasIdxRaw);
ideal.if_then(notify_jvmti_enabled, BoolTest::eq, ONE); {
// set the VTMS temporary transition bit in current JavaThread
Node* thread = ideal.thread();
Node* hide = _gvn.transform(argument(1)); // hide argument for temporary VTMS transition notification
Node* addr = basic_plus_adr(thread, in_bytes(JavaThread::is_in_tmp_VTMS_transition_offset()));
const TypePtr *addr_type = _gvn.type(addr)->isa_ptr();
sync_kit(ideal);
access_store_at(nullptr, addr, addr_type, hide, _gvn.type(hide), T_BOOLEAN, IN_NATIVE | MO_UNORDERED);
ideal.sync_kit(this);
} ideal.end_if();
final_sync(ideal);
return true;
}
#endif // INCLUDE_JVMTI
#ifdef JFR_HAVE_INTRINSICS
/**

View File

@ -245,6 +245,11 @@ class LibraryCallKit : public GraphKit {
bool inline_native_setScopedValueCache();
bool inline_native_time_funcs(address method, const char* funcName);
#if INCLUDE_JVMTI
bool inline_native_notify_jvmti_funcs(address funcAddr, const char* funcName);
bool inline_native_notify_jvmti_hide();
#endif
#ifdef JFR_HAVE_INTRINSICS
bool inline_native_classID();
bool inline_native_getEventWriter();

View File

@ -109,6 +109,10 @@ address OptoRuntime::_rethrow_Java = nullptr;
address OptoRuntime::_slow_arraycopy_Java = nullptr;
address OptoRuntime::_register_finalizer_Java = nullptr;
#if INCLUDE_JVMTI
address OptoRuntime::_notify_jvmti_mount = nullptr;
address OptoRuntime::_notify_jvmti_unmount = nullptr;
#endif
ExceptionBlob* OptoRuntime::_exception_blob;
@ -148,6 +152,10 @@ bool OptoRuntime::generate(ciEnv* env) {
gen(env, _multianewarray4_Java , multianewarray4_Type , multianewarray4_C , 0 , true, false);
gen(env, _multianewarray5_Java , multianewarray5_Type , multianewarray5_C , 0 , true, false);
gen(env, _multianewarrayN_Java , multianewarrayN_Type , multianewarrayN_C , 0 , true, false);
#if INCLUDE_JVMTI
gen(env, _notify_jvmti_mount , notify_jvmti_Type , SharedRuntime::notify_jvmti_mount, 0 , true, false);
gen(env, _notify_jvmti_unmount , notify_jvmti_Type , SharedRuntime::notify_jvmti_unmount, 0 , true, false);
#endif
gen(env, _complete_monitor_locking_Java , complete_monitor_enter_Type , SharedRuntime::complete_monitor_locking_C, 0, false, false);
gen(env, _monitor_notify_Java , monitor_notify_Type , monitor_notify_C , 0 , false, false);
gen(env, _monitor_notifyAll_Java , monitor_notify_Type , monitor_notifyAll_C , 0 , false, false);
@ -1644,6 +1652,24 @@ const TypeFunc *OptoRuntime::class_id_load_barrier_Type() {
}
#endif
#if INCLUDE_JVMTI
const TypeFunc *OptoRuntime::notify_jvmti_Type() {
// create input type (domain)
const Type **fields = TypeTuple::fields(3);
fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // VirtualThread oop
fields[TypeFunc::Parms+1] = TypeInt::BOOL; // jboolean
fields[TypeFunc::Parms+2] = TypeInt::BOOL; // jboolean
const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+3,fields);
// no result type needed
fields = TypeTuple::fields(1);
fields[TypeFunc::Parms+0] = NULL; // void
const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields);
return TypeFunc::make(domain,range);
}
#endif
//-----------------------------------------------------------------------------
// Dtrace support. entry and exit probes have the same signature
const TypeFunc *OptoRuntime::dtrace_method_entry_exit_Type() {

View File

@ -135,6 +135,10 @@ class OptoRuntime : public AllStatic {
static address _slow_arraycopy_Java;
static address _register_finalizer_Java;
#if INCLUDE_JVMTI
static address _notify_jvmti_mount;
static address _notify_jvmti_unmount;
#endif
//
// Implementation of runtime methods
@ -208,6 +212,10 @@ private:
static address slow_arraycopy_Java() { return _slow_arraycopy_Java; }
static address register_finalizer_Java() { return _register_finalizer_Java; }
#if INCLUDE_JVMTI
static address notify_jvmti_mount() { return _notify_jvmti_mount; }
static address notify_jvmti_unmount() { return _notify_jvmti_unmount; }
#endif
static ExceptionBlob* exception_blob() { return _exception_blob; }
@ -294,6 +302,9 @@ private:
static const TypeFunc* register_finalizer_Type();
JFR_ONLY(static const TypeFunc* class_id_load_barrier_Type();)
#if INCLUDE_JVMTI
static const TypeFunc* notify_jvmti_Type();
#endif
// Dtrace support
static const TypeFunc* dtrace_method_entry_exit_Type();

View File

@ -3917,123 +3917,60 @@ JVM_LEAF(jint, JVM_FindSignal(const char *name))
return os::get_signal_number(name);
JVM_END
JVM_ENTRY(void, JVM_VirtualThreadMountBegin(JNIEnv* env, jobject vthread, jboolean first_mount))
// If notifications are disabled then just update the VTMS transition bit and return.
// Otherwise, the bit is updated in the given jvmtiVTMSTransitionDisabler function call.
JVM_ENTRY(void, JVM_VirtualThreadMount(JNIEnv* env, jobject vthread, jboolean hide, jboolean first_mount))
#if INCLUDE_JVMTI
if (!DoJVMTIVirtualThreadTransitions) {
assert(!JvmtiExport::can_support_virtual_threads(), "sanity check");
return;
}
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
assert(!thread->is_in_VTMS_transition(), "sanity check");
JvmtiVTMSTransitionDisabler::start_VTMS_transition(vthread, /* is_mount */ true);
#else
fatal("Should only be called with JVMTI enabled");
#endif
JVM_END
JVM_ENTRY(void, JVM_VirtualThreadMountEnd(JNIEnv* env, jobject vthread, jboolean first_mount))
#if INCLUDE_JVMTI
if (!DoJVMTIVirtualThreadTransitions) {
assert(!JvmtiExport::can_support_virtual_threads(), "sanity check");
if (!JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) {
thread->set_is_in_VTMS_transition(hide);
return;
}
oop vt = JNIHandles::resolve(vthread);
thread->rebind_to_jvmti_thread_state_of(vt);
{
MutexLocker mu(JvmtiThreadState_lock);
JvmtiThreadState* state = thread->jvmti_thread_state();
if (state != nullptr && state->is_pending_interp_only_mode()) {
JvmtiEventController::enter_interp_only_mode();
}
}
assert(thread->is_in_VTMS_transition(), "sanity check");
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
JvmtiVTMSTransitionDisabler::finish_VTMS_transition(vthread, /* is_mount */ true);
if (first_mount) {
// thread start
if (JvmtiExport::can_support_virtual_threads()) {
JvmtiEventController::thread_started(thread);
if (JvmtiExport::should_post_vthread_start()) {
JvmtiExport::post_vthread_start(vthread);
}
} else { // compatibility for vthread unaware agents: legacy thread_start
if (PostVirtualThreadCompatibleLifecycleEvents &&
JvmtiExport::should_post_thread_life()) {
// JvmtiEventController::thread_started is called here
JvmtiExport::post_thread_start(thread);
}
}
}
if (JvmtiExport::should_post_vthread_mount()) {
JvmtiExport::post_vthread_mount(vthread);
if (hide) {
JvmtiVTMSTransitionDisabler::VTMS_mount_begin(vthread, first_mount);
} else {
JvmtiVTMSTransitionDisabler::VTMS_mount_end(vthread, first_mount);
}
#else
fatal("Should only be called with JVMTI enabled");
#endif
JVM_END
JVM_ENTRY(void, JVM_VirtualThreadUnmountBegin(JNIEnv* env, jobject vthread, jboolean last_unmount))
// If notifications are disabled then just update the VTMS transition bit and return.
// Otherwise, the bit is updated in the given jvmtiVTMSTransitionDisabler function call below.
JVM_ENTRY(void, JVM_VirtualThreadUnmount(JNIEnv* env, jobject vthread, jboolean hide, jboolean last_unmount))
#if INCLUDE_JVMTI
if (!DoJVMTIVirtualThreadTransitions) {
assert(!JvmtiExport::can_support_virtual_threads(), "sanity check");
return;
}
HandleMark hm(thread);
Handle ct(thread, thread->threadObj());
if (JvmtiExport::should_post_vthread_unmount()) {
JvmtiExport::post_vthread_unmount(vthread);
}
if (last_unmount) {
if (JvmtiExport::can_support_virtual_threads()) {
if (JvmtiExport::should_post_vthread_end()) {
JvmtiExport::post_vthread_end(vthread);
}
} else { // compatibility for vthread unaware agents: legacy thread_end
if (PostVirtualThreadCompatibleLifecycleEvents &&
JvmtiExport::should_post_thread_life()) {
JvmtiExport::post_thread_end(thread);
}
}
}
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
assert(!thread->is_in_VTMS_transition(), "sanity check");
JvmtiVTMSTransitionDisabler::start_VTMS_transition(vthread, /* is_mount */ false);
if (last_unmount && thread->jvmti_thread_state() != nullptr) {
JvmtiExport::cleanup_thread(thread);
thread->set_jvmti_thread_state(nullptr);
oop vt = JNIHandles::resolve(vthread);
java_lang_Thread::set_jvmti_thread_state(vt, nullptr);
}
thread->rebind_to_jvmti_thread_state_of(ct());
#else
fatal("Should only be called with JVMTI enabled");
#endif
JVM_END
JVM_ENTRY(void, JVM_VirtualThreadUnmountEnd(JNIEnv* env, jobject vthread, jboolean last_unmount))
#if INCLUDE_JVMTI
if (!DoJVMTIVirtualThreadTransitions) {
assert(!JvmtiExport::can_support_virtual_threads(), "sanity check");
if (!JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) {
thread->set_is_in_VTMS_transition(hide);
return;
}
assert(thread->is_in_VTMS_transition(), "sanity check");
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
JvmtiVTMSTransitionDisabler::finish_VTMS_transition(vthread, /* is_mount */ false);
if (hide) {
JvmtiVTMSTransitionDisabler::VTMS_unmount_begin(vthread, last_unmount);
} else {
JvmtiVTMSTransitionDisabler::VTMS_unmount_end(vthread, last_unmount);
}
#else
fatal("Should only be called with JVMTI enabled");
#endif
JVM_END
// If notifications are enabled then just update the temporary VTMS transition bit.
JVM_ENTRY(void, JVM_VirtualThreadHideFrames(JNIEnv* env, jobject vthread, jboolean hide))
#if INCLUDE_JVMTI
if (!DoJVMTIVirtualThreadTransitions) {
assert(!JvmtiExport::can_support_virtual_threads(), "sanity check");
return;
}
if (!JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) {
return;
}
assert(!thread->is_in_VTMS_transition(), "sanity check");
assert(thread->is_in_tmp_VTMS_transition() != (bool)hide, "sanity check");
thread->toggle_is_in_tmp_VTMS_transition();

View File

@ -379,11 +379,7 @@ JvmtiExport::get_jvmti_interface(JavaVM *jvm, void **penv, jint version) {
}
if (Continuations::enabled()) {
// Virtual threads support. There is a performance impact when VTMS transitions are enabled.
java_lang_VirtualThread::set_notify_jvmti_events(true);
if (JvmtiEnv::get_phase() == JVMTI_PHASE_LIVE) {
ThreadInVMfromNative __tiv(JavaThread::current());
java_lang_VirtualThread::init_static_notify_jvmti_events();
}
JvmtiVTMSTransitionDisabler::set_VTMS_notify_jvmti_events(true);
}
if (JvmtiEnv::get_phase() == JVMTI_PHASE_LIVE) {

View File

@ -33,7 +33,7 @@
#include "prims/jvmtiThreadState.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/jniHandles.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/safepointVerifiers.hpp"
#include "runtime/stackFrameStream.inline.hpp"
#include "runtime/vframe.hpp"
@ -219,12 +219,14 @@ volatile int JvmtiVTMSTransitionDisabler::_VTMS_transition_count = 0;
// VTMS transitions for one virtual thread are disabled while it is positive
volatile int JvmtiVTMSTransitionDisabler::_VTMS_transition_disable_for_one_count = 0;
// VTMs transitions for all virtual threads are disabled while it is positive
// VTMS transitions for all virtual threads are disabled while it is positive
volatile int JvmtiVTMSTransitionDisabler::_VTMS_transition_disable_for_all_count = 0;
// There is an active suspender or resumer.
volatile bool JvmtiVTMSTransitionDisabler::_SR_mode = false;
// Notifications from VirtualThread about VTMS events are enabled.
bool JvmtiVTMSTransitionDisabler::_VTMS_notify_jvmti_events = false;
#ifdef ASSERT
void
@ -509,6 +511,94 @@ JvmtiVTMSTransitionDisabler::finish_VTMS_transition(jthread vthread, bool is_mou
#endif
}
void
JvmtiVTMSTransitionDisabler::VTMS_mount_begin(jobject vthread, jboolean first_mount) {
JavaThread* thread = JavaThread::current();
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
assert(!thread->is_in_VTMS_transition(), "sanity check");
start_VTMS_transition(vthread, /* is_mount */ true);
}
void
JvmtiVTMSTransitionDisabler::VTMS_mount_end(jobject vthread, jboolean first_mount) {
JavaThread* thread = JavaThread::current();
oop vt = JNIHandles::resolve(vthread);
thread->rebind_to_jvmti_thread_state_of(vt);
{
MutexLocker mu(JvmtiThreadState_lock);
JvmtiThreadState* state = thread->jvmti_thread_state();
if (state != nullptr && state->is_pending_interp_only_mode()) {
JvmtiEventController::enter_interp_only_mode();
}
}
assert(thread->is_in_VTMS_transition(), "sanity check");
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
finish_VTMS_transition(vthread, /* is_mount */ true);
if (first_mount) {
// thread start
if (JvmtiExport::can_support_virtual_threads()) {
JvmtiEventController::thread_started(thread);
if (JvmtiExport::should_post_vthread_start()) {
JvmtiExport::post_vthread_start(vthread);
}
} else { // compatibility for vthread unaware agents: legacy thread_start
if (PostVirtualThreadCompatibleLifecycleEvents &&
JvmtiExport::should_post_thread_life()) {
// JvmtiEventController::thread_started is called here
JvmtiExport::post_thread_start(thread);
}
}
}
if (JvmtiExport::should_post_vthread_mount()) {
JvmtiExport::post_vthread_mount(vthread);
}
}
void
JvmtiVTMSTransitionDisabler::VTMS_unmount_begin(jobject vthread, jboolean last_unmount) {
JavaThread* thread = JavaThread::current();
HandleMark hm(thread);
Handle ct(thread, thread->threadObj());
if (JvmtiExport::should_post_vthread_unmount()) {
JvmtiExport::post_vthread_unmount(vthread);
}
if (last_unmount) {
if (JvmtiExport::can_support_virtual_threads()) {
if (JvmtiExport::should_post_vthread_end()) {
JvmtiExport::post_vthread_end(vthread);
}
} else { // compatibility for vthread unaware agents: legacy thread_end
if (PostVirtualThreadCompatibleLifecycleEvents &&
JvmtiExport::should_post_thread_life()) {
JvmtiExport::post_thread_end(thread);
}
}
}
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
assert(!thread->is_in_VTMS_transition(), "sanity check");
start_VTMS_transition(vthread, /* is_mount */ false);
if (last_unmount && thread->jvmti_thread_state() != nullptr) {
JvmtiExport::cleanup_thread(thread);
thread->set_jvmti_thread_state(nullptr);
oop vt = JNIHandles::resolve(vthread);
java_lang_Thread::set_jvmti_thread_state(vt, nullptr);
}
thread->rebind_to_jvmti_thread_state_of(ct());
}
void
JvmtiVTMSTransitionDisabler::VTMS_unmount_end(jobject vthread, jboolean last_unmount) {
JavaThread* thread = JavaThread::current();
assert(thread->is_in_VTMS_transition(), "sanity check");
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
finish_VTMS_transition(vthread, /* is_mount */ false);
}
//
// Virtual Threads Suspend/Resume management
//

View File

@ -94,6 +94,10 @@ class JvmtiVTMSTransitionDisabler {
void VTMS_transition_enable_for_all();
public:
static bool _VTMS_notify_jvmti_events; // enable notifications from VirtualThread about VTMS events
static bool VTMS_notify_jvmti_events() { return _VTMS_notify_jvmti_events; }
static void set_VTMS_notify_jvmti_events(bool val) { _VTMS_notify_jvmti_events = val; }
// parameter is_SR: suspender or resumer
JvmtiVTMSTransitionDisabler(bool is_SR = false);
JvmtiVTMSTransitionDisabler(jthread thread);
@ -101,6 +105,11 @@ class JvmtiVTMSTransitionDisabler {
static void start_VTMS_transition(jthread vthread, bool is_mount);
static void finish_VTMS_transition(jthread vthread, bool is_mount);
static void VTMS_mount_begin(jobject vthread, jboolean first_mount);
static void VTMS_mount_end(jobject vthread, jboolean first_mount);
static void VTMS_unmount_begin(jobject vthread, jboolean last_unmount);
static void VTMS_unmount_end(jobject vthread, jboolean last_unmount);
};
///////////////////////////////////////////////////////////////

View File

@ -811,6 +811,11 @@ private:
static ByteSize cont_fastpath_offset() { return byte_offset_of(JavaThread, _cont_fastpath); }
static ByteSize held_monitor_count_offset() { return byte_offset_of(JavaThread, _held_monitor_count); }
#if INCLUDE_JVMTI
static ByteSize is_in_VTMS_transition_offset() { return byte_offset_of(JavaThread, _is_in_VTMS_transition); }
static ByteSize is_in_tmp_VTMS_transition_offset() { return byte_offset_of(JavaThread, _is_in_tmp_VTMS_transition); }
#endif
// Returns the jni environment for this thread
JNIEnv* jni_environment() { return &_jni_environment; }

View File

@ -53,6 +53,7 @@
#include "oops/oop.inline.hpp"
#include "prims/forte.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/jvmtiThreadState.hpp"
#include "prims/methodHandles.hpp"
#include "prims/nativeLookup.hpp"
#include "runtime/atomic.hpp"
@ -62,6 +63,7 @@
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/java.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/jniHandles.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/stackWatermarkSet.hpp"
#include "runtime/stubRoutines.hpp"
@ -623,6 +625,28 @@ void SharedRuntime::throw_and_post_jvmti_exception(JavaThread* current, Symbol*
throw_and_post_jvmti_exception(current, h_exception);
}
#if INCLUDE_JVMTI
JRT_ENTRY(void, SharedRuntime::notify_jvmti_mount(oopDesc* vt, jboolean hide, jboolean first_mount, JavaThread* current))
jobject vthread = JNIHandles::make_local(const_cast<oopDesc*>(vt));
if (hide) {
JvmtiVTMSTransitionDisabler::VTMS_mount_begin(vthread, first_mount);
} else {
JvmtiVTMSTransitionDisabler::VTMS_mount_end(vthread, first_mount);
}
JRT_END
JRT_ENTRY(void, SharedRuntime::notify_jvmti_unmount(oopDesc* vt, jboolean hide, jboolean last_unmount, JavaThread* current))
jobject vthread = JNIHandles::make_local(const_cast<oopDesc*>(vt));
if (hide) {
JvmtiVTMSTransitionDisabler::VTMS_unmount_begin(vthread, last_unmount);
} else {
JvmtiVTMSTransitionDisabler::VTMS_unmount_end(vthread, last_unmount);
}
JRT_END
#endif // INCLUDE_JVMTI
// The interpreter code to call this tracing function is only
// called/generated when UL is on for redefine, class and has the right level
// and tags. Since obsolete methods are never compiled, we don't have

View File

@ -264,6 +264,12 @@ class SharedRuntime: AllStatic {
static void throw_and_post_jvmti_exception(JavaThread* current, Handle h_exception);
static void throw_and_post_jvmti_exception(JavaThread* current, Symbol* name, const char *message = nullptr);
#if INCLUDE_JVMTI
// Functions for JVMTI notifications
static void notify_jvmti_mount(oopDesc* vt, jboolean hide, jboolean first_mount, JavaThread* current);
static void notify_jvmti_unmount(oopDesc* vt, jboolean hide, jboolean last_unmount, JavaThread* current);
#endif
// RedefineClasses() tracing support for obsolete method entry
static int rc_trace_method_entry(JavaThread* thread, Method* m);

View File

@ -56,6 +56,7 @@ import jdk.internal.vm.ThreadContainers;
import jdk.internal.vm.annotation.ChangesCurrentThread;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Hidden;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import jdk.internal.vm.annotation.JvmtiMountTransition;
import sun.nio.ch.Interruptible;
import sun.security.action.GetPropertyAction;
@ -210,7 +211,7 @@ final class VirtualThread extends BaseVirtualThread {
}
// notify JVMTI before mount
if (notifyJvmtiEvents) notifyJvmtiMountBegin(firstRun);
notifyJvmtiMount(true, firstRun);
try {
cont.run();
@ -287,11 +288,10 @@ final class VirtualThread extends BaseVirtualThread {
@ChangesCurrentThread
private void run(Runnable task) {
assert state == RUNNING;
boolean notifyJvmti = notifyJvmtiEvents;
// first mount
mount();
if (notifyJvmti) notifyJvmtiMountEnd(true);
notifyJvmtiMount(false, true);
// emit JFR event if enabled
if (VirtualThreadStartEvent.isTurnedOn()) {
@ -319,7 +319,7 @@ final class VirtualThread extends BaseVirtualThread {
} finally {
// last unmount
if (notifyJvmti) notifyJvmtiUnmountBegin(true);
notifyJvmtiUnmount(true, true);
unmount();
// final state
@ -381,35 +381,27 @@ final class VirtualThread extends BaseVirtualThread {
/**
* Sets the current thread to the current carrier thread.
* @return true if JVMTI was notified
*/
@ChangesCurrentThread
@JvmtiMountTransition
private boolean switchToCarrierThread() {
boolean notifyJvmti = notifyJvmtiEvents;
if (notifyJvmti) {
notifyJvmtiHideFrames(true);
}
private void switchToCarrierThread() {
notifyJvmtiHideFrames(true);
Thread carrier = this.carrierThread;
assert Thread.currentThread() == this
&& carrier == Thread.currentCarrierThread();
carrier.setCurrentThread(carrier);
return notifyJvmti;
}
/**
* Sets the current thread to the given virtual thread.
* If {@code notifyJvmti} is true then JVMTI is notified.
*/
@ChangesCurrentThread
@JvmtiMountTransition
private void switchToVirtualThread(VirtualThread vthread, boolean notifyJvmti) {
private void switchToVirtualThread(VirtualThread vthread) {
Thread carrier = vthread.carrierThread;
assert carrier == Thread.currentCarrierThread();
carrier.setCurrentThread(vthread);
if (notifyJvmti) {
notifyJvmtiHideFrames(false);
}
notifyJvmtiHideFrames(false);
}
/**
@ -419,17 +411,15 @@ final class VirtualThread extends BaseVirtualThread {
*/
@ChangesCurrentThread
private boolean yieldContinuation() {
boolean notifyJvmti = notifyJvmtiEvents;
// unmount
if (notifyJvmti) notifyJvmtiUnmountBegin(false);
notifyJvmtiUnmount(true, false);
unmount();
try {
return Continuation.yield(VTHREAD_SCOPE);
} finally {
// re-mount
mount();
if (notifyJvmti) notifyJvmtiMountEnd(false);
notifyJvmtiMount(false, false);
}
}
@ -446,7 +436,7 @@ final class VirtualThread extends BaseVirtualThread {
setState(PARKED);
// notify JVMTI that unmount has completed, thread is parked
if (notifyJvmtiEvents) notifyJvmtiUnmountEnd(false);
notifyJvmtiUnmount(false, false);
// may have been unparked while parking
if (parkPermit && compareAndSetState(PARKED, RUNNABLE)) {
@ -462,7 +452,7 @@ final class VirtualThread extends BaseVirtualThread {
setState(RUNNABLE);
// notify JVMTI that unmount has completed, thread is runnable
if (notifyJvmtiEvents) notifyJvmtiUnmountEnd(false);
notifyJvmtiUnmount(false, false);
// external submit if there are no tasks in the local task queue
if (currentThread() instanceof CarrierThread ct && ct.getQueuedTaskCount() == 0) {
@ -483,7 +473,7 @@ final class VirtualThread extends BaseVirtualThread {
assert (state() == TERMINATED) && (carrierThread == null);
if (executed) {
if (notifyJvmtiEvents) notifyJvmtiUnmountEnd(true);
notifyJvmtiUnmount(false, true);
}
// notify anyone waiting for this virtual thread to terminate
@ -651,11 +641,11 @@ final class VirtualThread extends BaseVirtualThread {
@ChangesCurrentThread
private Future<?> scheduleUnpark(Runnable unparker, long nanos) {
// need to switch to current carrier thread to avoid nested parking
boolean notifyJvmti = switchToCarrierThread();
switchToCarrierThread();
try {
return UNPARKER.schedule(unparker, nanos, NANOSECONDS);
} finally {
switchToVirtualThread(this, notifyJvmti);
switchToVirtualThread(this);
}
}
@ -666,11 +656,11 @@ final class VirtualThread extends BaseVirtualThread {
private void cancel(Future<?> future) {
if (!future.isDone()) {
// need to switch to current carrier thread to avoid nested parking
boolean notifyJvmti = switchToCarrierThread();
switchToCarrierThread();
try {
future.cancel(false);
} finally {
switchToVirtualThread(this, notifyJvmti);
switchToVirtualThread(this);
}
}
}
@ -690,11 +680,11 @@ final class VirtualThread extends BaseVirtualThread {
int s = state();
if (s == PARKED && compareAndSetState(PARKED, RUNNABLE)) {
if (currentThread instanceof VirtualThread vthread) {
boolean notifyJvmti = vthread.switchToCarrierThread();
vthread.switchToCarrierThread();
try {
submitRunContinuation();
} finally {
switchToVirtualThread(vthread, notifyJvmti);
switchToVirtualThread(vthread);
}
} else {
submitRunContinuation();
@ -1055,20 +1045,15 @@ final class VirtualThread extends BaseVirtualThread {
// -- JVM TI support --
private static volatile boolean notifyJvmtiEvents; // set by VM
@IntrinsicCandidate
@JvmtiMountTransition
private native void notifyJvmtiMountBegin(boolean firstMount);
private native void notifyJvmtiMount(boolean hide, boolean firstMount);
@IntrinsicCandidate
@JvmtiMountTransition
private native void notifyJvmtiMountEnd(boolean firstMount);
@JvmtiMountTransition
private native void notifyJvmtiUnmountBegin(boolean lastUnmount);
@JvmtiMountTransition
private native void notifyJvmtiUnmountEnd(boolean lastUnmount);
private native void notifyJvmtiUnmount(boolean hide, boolean lastUnmount);
@IntrinsicCandidate
@JvmtiMountTransition
private native void notifyJvmtiHideFrames(boolean hide);

View File

@ -32,11 +32,9 @@
#define VIRTUAL_THREAD "Ljava/lang/VirtualThread;"
static JNINativeMethod methods[] = {
{ "notifyJvmtiMountBegin", "(Z)V", (void *)&JVM_VirtualThreadMountBegin },
{ "notifyJvmtiMountEnd", "(Z)V", (void *)&JVM_VirtualThreadMountEnd },
{ "notifyJvmtiUnmountBegin", "(Z)V", (void *)&JVM_VirtualThreadUnmountBegin },
{ "notifyJvmtiUnmountEnd", "(Z)V", (void *)&JVM_VirtualThreadUnmountEnd },
{ "notifyJvmtiHideFrames", "(Z)V", (void *)&JVM_VirtualThreadHideFrames },
{ "notifyJvmtiMount", "(ZZ)V", (void *)&JVM_VirtualThreadMount },
{ "notifyJvmtiUnmount", "(ZZ)V", (void *)&JVM_VirtualThreadUnmount },
{ "notifyJvmtiHideFrames", "(Z)V", (void *)&JVM_VirtualThreadHideFrames },
};
JNIEXPORT void JNICALL