From 1227a275a1c1e82b9a6410843f32534d7e841f54 Mon Sep 17 00:00:00 2001 From: Serguei Spitsyn Date: Tue, 2 May 2023 02:40:50 +0000 Subject: [PATCH] 8306028: separate ThreadStart/ThreadEnd events posting code in JVMTI VTMS transitions 8304444: Reappearance of NULL in jvmtiThreadState.cpp Reviewed-by: pchilanomate, lmesnik --- make/data/hotspot-symbols/symbols-unix | 4 +- src/hotspot/share/classfile/vmIntrinsics.hpp | 8 +- src/hotspot/share/classfile/vmSymbols.hpp | 2 + src/hotspot/share/include/jvm.h | 10 +- src/hotspot/share/opto/c2compiler.cpp | 8 +- src/hotspot/share/opto/library_call.cpp | 23 +-- src/hotspot/share/opto/library_call.hpp | 2 +- src/hotspot/share/opto/runtime.cpp | 45 +++--- src/hotspot/share/opto/runtime.hpp | 14 +- src/hotspot/share/prims/jvm.cpp | 64 +++++--- src/hotspot/share/prims/jvmtiThreadState.cpp | 142 ++++++++++++------ src/hotspot/share/prims/jvmtiThreadState.hpp | 18 ++- src/hotspot/share/runtime/sharedRuntime.cpp | 30 ++-- src/hotspot/share/runtime/sharedRuntime.hpp | 6 +- .../classes/java/lang/VirtualThread.java | 31 ++-- .../share/native/libjava/VirtualThread.c | 10 +- 16 files changed, 265 insertions(+), 152 deletions(-) diff --git a/make/data/hotspot-symbols/symbols-unix b/make/data/hotspot-symbols/symbols-unix index c08ce4590fc..fb7644b5303 100644 --- a/make/data/hotspot-symbols/symbols-unix +++ b/make/data/hotspot-symbols/symbols-unix @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 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 @@ -217,6 +217,8 @@ JVM_DefineModule JVM_SetBootLoaderUnnamedModule # Virtual thread notifications for JVMTI +JVM_VirtualThreadStart +JVM_VirtualThreadEnd JVM_VirtualThreadMount JVM_VirtualThreadUnmount JVM_VirtualThreadHideFrames diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 96c9f78577f..86d5cc9ce5f 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -584,9 +584,11 @@ class methodHandle; 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) \ + do_intrinsic(_notifyJvmtiVThreadStart, java_lang_VirtualThread, notifyJvmtiStart_name, void_method_signature, F_RN) \ + do_intrinsic(_notifyJvmtiVThreadEnd, java_lang_VirtualThread, notifyJvmtiEnd_name, void_method_signature, F_RN) \ + do_intrinsic(_notifyJvmtiVThreadMount, java_lang_VirtualThread, notifyJvmtiMount_name, bool_void_signature, F_RN) \ + do_intrinsic(_notifyJvmtiVThreadUnmount, java_lang_VirtualThread, notifyJvmtiUnmount_name, bool_void_signature, F_RN) \ + do_intrinsic(_notifyJvmtiVThreadHideFrames, java_lang_VirtualThread, notifyJvmtiHideFrames_name, bool_void_signature, F_RN) \ \ /* support for UnsafeConstants */ \ do_class(jdk_internal_misc_UnsafeConstants, "jdk/internal/misc/UnsafeConstants") \ diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index 16141c3ed47..df5ea30484b 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -412,6 +412,8 @@ template(run_finalization_name, "runFinalization") \ template(dispatchUncaughtException_name, "dispatchUncaughtException") \ template(loadClass_name, "loadClass") \ + template(notifyJvmtiStart_name, "notifyJvmtiStart") \ + template(notifyJvmtiEnd_name, "notifyJvmtiEnd") \ template(notifyJvmtiMount_name, "notifyJvmtiMount") \ template(notifyJvmtiUnmount_name, "notifyJvmtiUnmount") \ template(notifyJvmtiHideFrames_name, "notifyJvmtiHideFrames") \ diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index d18c8d1f6de..453e72459ed 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -1144,10 +1144,16 @@ JVM_GetEnclosingMethodInfo(JNIEnv* env, jclass ofClass); * Virtual thread support. */ JNIEXPORT void JNICALL -JVM_VirtualThreadMount(JNIEnv* env, jobject vthread, jboolean hide, jboolean first_mount); +JVM_VirtualThreadStart(JNIEnv* env, jobject vthread); JNIEXPORT void JNICALL -JVM_VirtualThreadUnmount(JNIEnv* env, jobject vthread, jboolean hide, jboolean last_unmount); +JVM_VirtualThreadEnd(JNIEnv* env, jobject vthread); + +JNIEXPORT void JNICALL +JVM_VirtualThreadMount(JNIEnv* env, jobject vthread, jboolean hide); + +JNIEXPORT void JNICALL +JVM_VirtualThreadUnmount(JNIEnv* env, jobject vthread, jboolean hide); JNIEXPORT void JNICALL JVM_VirtualThreadHideFrames(JNIEnv* env, jobject vthread, jboolean hide); diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 560937d9fa8..e26c992d558 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -775,9 +775,11 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method) { return EnableVectorSupport; case vmIntrinsics::_blackhole: #if INCLUDE_JVMTI - case vmIntrinsics::_notifyJvmtiMount: - case vmIntrinsics::_notifyJvmtiUnmount: - case vmIntrinsics::_notifyJvmtiHideFrames: + case vmIntrinsics::_notifyJvmtiVThreadStart: + case vmIntrinsics::_notifyJvmtiVThreadEnd: + case vmIntrinsics::_notifyJvmtiVThreadMount: + case vmIntrinsics::_notifyJvmtiVThreadUnmount: + case vmIntrinsics::_notifyJvmtiVThreadHideFrames: #endif break; diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 86b1c26d048..fd3b813eded 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -481,11 +481,15 @@ bool LibraryCallKit::try_to_inline(int predicate) { 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(); + case vmIntrinsics::_notifyJvmtiVThreadStart: return inline_native_notify_jvmti_funcs(CAST_FROM_FN_PTR(address, OptoRuntime::notify_jvmti_vthread_start()), + "notifyJvmtiStart", true, false); + case vmIntrinsics::_notifyJvmtiVThreadEnd: return inline_native_notify_jvmti_funcs(CAST_FROM_FN_PTR(address, OptoRuntime::notify_jvmti_vthread_end()), + "notifyJvmtiEnd", false, true); + case vmIntrinsics::_notifyJvmtiVThreadMount: return inline_native_notify_jvmti_funcs(CAST_FROM_FN_PTR(address, OptoRuntime::notify_jvmti_vthread_mount()), + "notifyJvmtiMount", false, false); + case vmIntrinsics::_notifyJvmtiVThreadUnmount: return inline_native_notify_jvmti_funcs(CAST_FROM_FN_PTR(address, OptoRuntime::notify_jvmti_vthread_unmount()), + "notifyJvmtiUnmount", false, false); + case vmIntrinsics::_notifyJvmtiVThreadHideFrames: return inline_native_notify_jvmti_hide(); #endif #ifdef JFR_HAVE_INTRINSICS @@ -2873,25 +2877,24 @@ bool LibraryCallKit::inline_native_time_funcs(address funcAddr, const char* func // 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) { +bool LibraryCallKit::inline_native_notify_jvmti_funcs(address funcAddr, const char* funcName, bool is_start, bool is_end) { 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* hide = is_start ? ideal.ConI(0) : (is_end ? ideal.ConI(1) : _gvn.transform(argument(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); { // if notifyJvmti enabled then make a call to the given SharedRuntime function - const TypeFunc* tf = OptoRuntime::notify_jvmti_Type(); + const TypeFunc* tf = OptoRuntime::notify_jvmti_vthread_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); + make_runtime_call(RC_NO_LEAF, tf, funcAddr, funcName, TypePtr::BOTTOM, vt_oop, hide); ideal.sync_kit(this); } ideal.else_(); { // set hide value to the VTMS transition bit in current JavaThread and VirtualThread object diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index ac9e8ab6a47..46dd51bf654 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -246,7 +246,7 @@ class LibraryCallKit : public GraphKit { 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_funcs(address funcAddr, const char* funcName, bool is_start, bool is_end); bool inline_native_notify_jvmti_hide(); #endif diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index 9a57d1743b6..e473fa68f0f 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -111,8 +111,10 @@ address OptoRuntime::_slow_arraycopy_Java = nullptr; address OptoRuntime::_register_finalizer_Java = nullptr; #if INCLUDE_JVMTI address OptoRuntime::_notify_jvmti_object_alloc = nullptr; -address OptoRuntime::_notify_jvmti_mount = nullptr; -address OptoRuntime::_notify_jvmti_unmount = nullptr; +address OptoRuntime::_notify_jvmti_vthread_start = nullptr; +address OptoRuntime::_notify_jvmti_vthread_end = nullptr; +address OptoRuntime::_notify_jvmti_vthread_mount = nullptr; +address OptoRuntime::_notify_jvmti_vthread_unmount = nullptr; #endif ExceptionBlob* OptoRuntime::_exception_blob; @@ -155,8 +157,10 @@ bool OptoRuntime::generate(ciEnv* env) { gen(env, _multianewarrayN_Java , multianewarrayN_Type , multianewarrayN_C , 0 , true, false); #if INCLUDE_JVMTI gen(env, _notify_jvmti_object_alloc , notify_jvmti_object_alloc_Type, SharedRuntime::notify_jvmti_object_alloc, 0, true, false); - 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); + gen(env, _notify_jvmti_vthread_start , notify_jvmti_vthread_Type , SharedRuntime::notify_jvmti_vthread_start, 0, true, false); + gen(env, _notify_jvmti_vthread_end , notify_jvmti_vthread_Type , SharedRuntime::notify_jvmti_vthread_end, 0, true, false); + gen(env, _notify_jvmti_vthread_mount , notify_jvmti_vthread_Type , SharedRuntime::notify_jvmti_vthread_mount, 0, true, false); + gen(env, _notify_jvmti_vthread_unmount , notify_jvmti_vthread_Type , SharedRuntime::notify_jvmti_vthread_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); @@ -491,6 +495,21 @@ const TypeFunc *OptoRuntime::notify_jvmti_object_alloc_Type() { return TypeFunc::make(domain, range); } + +const TypeFunc *OptoRuntime::notify_jvmti_vthread_Type() { + // create input type (domain) + const Type **fields = TypeTuple::fields(2); + fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // VirtualThread oop + fields[TypeFunc::Parms+1] = TypeInt::BOOL; // jboolean + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2,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 const TypeFunc *OptoRuntime::athrow_Type() { @@ -1670,24 +1689,6 @@ 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() { diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp index cc52e02a318..f54ce4241eb 100644 --- a/src/hotspot/share/opto/runtime.hpp +++ b/src/hotspot/share/opto/runtime.hpp @@ -137,8 +137,10 @@ class OptoRuntime : public AllStatic { static address _register_finalizer_Java; #if INCLUDE_JVMTI static address _notify_jvmti_object_alloc; - static address _notify_jvmti_mount; - static address _notify_jvmti_unmount; + static address _notify_jvmti_vthread_start; + static address _notify_jvmti_vthread_end; + static address _notify_jvmti_vthread_mount; + static address _notify_jvmti_vthread_unmount; #endif // @@ -215,8 +217,10 @@ private: static address register_finalizer_Java() { return _register_finalizer_Java; } #if INCLUDE_JVMTI static address notify_jvmti_object_alloc() { return _notify_jvmti_object_alloc; } - static address notify_jvmti_mount() { return _notify_jvmti_mount; } - static address notify_jvmti_unmount() { return _notify_jvmti_unmount; } + static address notify_jvmti_vthread_start() { return _notify_jvmti_vthread_start; } + static address notify_jvmti_vthread_end() { return _notify_jvmti_vthread_end; } + static address notify_jvmti_vthread_mount() { return _notify_jvmti_vthread_mount; } + static address notify_jvmti_vthread_unmount() { return _notify_jvmti_vthread_unmount; } #endif static ExceptionBlob* exception_blob() { return _exception_blob; } @@ -306,7 +310,7 @@ private: JFR_ONLY(static const TypeFunc* class_id_load_barrier_Type();) #if INCLUDE_JVMTI static const TypeFunc* notify_jvmti_object_alloc_Type(); - static const TypeFunc* notify_jvmti_Type(); + static const TypeFunc* notify_jvmti_vthread_Type(); #endif // Dtrace support diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 2a3420a8fbb..e9ee6b6f90a 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -3910,24 +3910,53 @@ JVM_LEAF(jint, JVM_FindSignal(const char *name)) return os::get_signal_number(name); JVM_END -// 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)) +JVM_ENTRY(void, JVM_VirtualThreadStart(JNIEnv* env, jobject vthread)) #if INCLUDE_JVMTI if (!DoJVMTIVirtualThreadTransitions) { assert(!JvmtiExport::can_support_virtual_threads(), "sanity check"); return; } - if (!JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { - thread->set_is_in_VTMS_transition(hide); - oop vt = JNIHandles::resolve_external_guard(vthread); - java_lang_Thread::set_is_in_VTMS_transition(vt, hide); + if (JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { + JvmtiVTMSTransitionDisabler::VTMS_vthread_start(vthread); + } else { + // set VTMS transition bit value in JavaThread and java.lang.VirtualThread object + JvmtiVTMSTransitionDisabler::set_is_in_VTMS_transition(thread, vthread, false); + } +#else + fatal("Should only be called with JVMTI enabled"); +#endif +JVM_END + +JVM_ENTRY(void, JVM_VirtualThreadEnd(JNIEnv* env, jobject vthread)) +#if INCLUDE_JVMTI + if (!DoJVMTIVirtualThreadTransitions) { + assert(!JvmtiExport::can_support_virtual_threads(), "sanity check"); return; } - if (hide) { - JvmtiVTMSTransitionDisabler::VTMS_mount_begin(vthread, first_mount); + if (JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { + JvmtiVTMSTransitionDisabler::VTMS_vthread_end(vthread); } else { - JvmtiVTMSTransitionDisabler::VTMS_mount_end(vthread, first_mount); + // set VTMS transition bit value in JavaThread and java.lang.VirtualThread object + JvmtiVTMSTransitionDisabler::set_is_in_VTMS_transition(thread, vthread, true); + } +#else + fatal("Should only be called with JVMTI enabled"); +#endif +JVM_END + +// 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)) +#if INCLUDE_JVMTI + if (!DoJVMTIVirtualThreadTransitions) { + assert(!JvmtiExport::can_support_virtual_threads(), "sanity check"); + return; + } + if (JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { + JvmtiVTMSTransitionDisabler::VTMS_vthread_mount(vthread, hide); + } else { + // set VTMS transition bit value in JavaThread and java.lang.VirtualThread object + JvmtiVTMSTransitionDisabler::set_is_in_VTMS_transition(thread, vthread, hide); } #else fatal("Should only be called with JVMTI enabled"); @@ -3936,22 +3965,17 @@ JVM_END // 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)) +JVM_ENTRY(void, JVM_VirtualThreadUnmount(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()) { - thread->set_is_in_VTMS_transition(hide); - oop vt = JNIHandles::resolve_external_guard(vthread); - java_lang_Thread::set_is_in_VTMS_transition(vt, hide); - return; - } - if (hide) { - JvmtiVTMSTransitionDisabler::VTMS_unmount_begin(vthread, last_unmount); + if (JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { + JvmtiVTMSTransitionDisabler::VTMS_vthread_unmount(vthread, hide); } else { - JvmtiVTMSTransitionDisabler::VTMS_unmount_end(vthread, last_unmount); + // set VTMS transition bit value in JavaThread and java.lang.VirtualThread object + JvmtiVTMSTransitionDisabler::set_is_in_VTMS_transition(thread, vthread, hide); } #else fatal("Should only be called with JVMTI enabled"); diff --git a/src/hotspot/share/prims/jvmtiThreadState.cpp b/src/hotspot/share/prims/jvmtiThreadState.cpp index f33581c6cca..40168637d79 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiThreadState.cpp @@ -292,7 +292,7 @@ JvmtiVTMSTransitionDisabler::~JvmtiVTMSTransitionDisabler() { // disable VTMS transitions for one virtual thread void JvmtiVTMSTransitionDisabler::VTMS_transition_disable_for_one() { - assert(_thread != NULL, "sanity check"); + assert(_thread != nullptr, "sanity check"); JavaThread* thread = JavaThread::current(); HandleMark hm(thread); Handle vth = Handle(thread, JNIHandles::resolve_external_guard(_thread)); @@ -511,8 +511,95 @@ JvmtiVTMSTransitionDisabler::finish_VTMS_transition(jthread vthread, bool is_mou #endif } +// set VTMS transition bit value in JavaThread and java.lang.VirtualThread object +void JvmtiVTMSTransitionDisabler::set_is_in_VTMS_transition(JavaThread* thread, jobject vthread, bool in_trans) { + oop vt = JNIHandles::resolve_external_guard(vthread); + java_lang_Thread::set_is_in_VTMS_transition(vt, in_trans); + thread->set_is_in_VTMS_transition(in_trans); +} + void -JvmtiVTMSTransitionDisabler::VTMS_mount_begin(jobject vthread, jboolean first_mount) { +JvmtiVTMSTransitionDisabler::VTMS_vthread_start(jobject vthread) { + VTMS_mount_end(vthread); + JavaThread* thread = JavaThread::current(); + + assert(!thread->is_in_VTMS_transition(), "sanity check"); + assert(!thread->is_in_tmp_VTMS_transition(), "sanity check"); + + 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); + } + } + // post VirtualThreadMount event after VirtualThreadStart + if (JvmtiExport::should_post_vthread_mount()) { + JvmtiExport::post_vthread_mount(vthread); + } +} + +void +JvmtiVTMSTransitionDisabler::VTMS_vthread_end(jobject vthread) { + JavaThread* thread = JavaThread::current(); + + assert(!thread->is_in_VTMS_transition(), "sanity check"); + assert(!thread->is_in_tmp_VTMS_transition(), "sanity check"); + + // post VirtualThreadUnmount event before VirtualThreadEnd + if (JvmtiExport::should_post_vthread_unmount()) { + JvmtiExport::post_vthread_unmount(vthread); + } + 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); + } + } + if (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); + } + VTMS_unmount_begin(vthread); +} + +void +JvmtiVTMSTransitionDisabler::VTMS_vthread_mount(jobject vthread, bool hide) { + if (hide) { + VTMS_mount_begin(vthread); + } else { + VTMS_mount_end(vthread); + if (JvmtiExport::should_post_vthread_mount()) { + JvmtiExport::post_vthread_mount(vthread); + } + } +} + +void +JvmtiVTMSTransitionDisabler::VTMS_vthread_unmount(jobject vthread, bool hide) { + if (hide) { + if (JvmtiExport::should_post_vthread_unmount()) { + JvmtiExport::post_vthread_unmount(vthread); + } + VTMS_unmount_begin(vthread); + } else { + VTMS_unmount_end(vthread); + } +} + +void +JvmtiVTMSTransitionDisabler::VTMS_mount_begin(jobject vthread) { JavaThread* thread = JavaThread::current(); assert(!thread->is_in_tmp_VTMS_transition(), "sanity check"); assert(!thread->is_in_VTMS_transition(), "sanity check"); @@ -520,7 +607,7 @@ JvmtiVTMSTransitionDisabler::VTMS_mount_begin(jobject vthread, jboolean first_mo } void -JvmtiVTMSTransitionDisabler::VTMS_mount_end(jobject vthread, jboolean first_mount) { +JvmtiVTMSTransitionDisabler::VTMS_mount_end(jobject vthread) { JavaThread* thread = JavaThread::current(); oop vt = JNIHandles::resolve(vthread); @@ -536,62 +623,21 @@ JvmtiVTMSTransitionDisabler::VTMS_mount_end(jobject vthread, jboolean first_moun 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) { +JvmtiVTMSTransitionDisabler::VTMS_unmount_begin(jobject vthread) { 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()); + start_VTMS_transition(vthread, /* is_mount */ false); + thread->rebind_to_jvmti_thread_state_of(thread->threadObj()); } void -JvmtiVTMSTransitionDisabler::VTMS_unmount_end(jobject vthread, jboolean last_unmount) { +JvmtiVTMSTransitionDisabler::VTMS_unmount_end(jobject vthread) { JavaThread* thread = JavaThread::current(); assert(thread->is_in_VTMS_transition(), "sanity check"); assert(!thread->is_in_tmp_VTMS_transition(), "sanity check"); diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp index a4e9f25a6ca..c48096b90d4 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.hpp @@ -105,13 +105,23 @@ class JvmtiVTMSTransitionDisabler { JvmtiVTMSTransitionDisabler(jthread thread); ~JvmtiVTMSTransitionDisabler(); + // set VTMS transition bit value in JavaThread and java.lang.VirtualThread object + static void set_is_in_VTMS_transition(JavaThread* thread, jobject vthread, bool in_trans); + 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); + static void VTMS_vthread_start(jobject vthread); + static void VTMS_vthread_end(jobject vthread); + + static void VTMS_vthread_mount(jobject vthread, bool hide); + static void VTMS_vthread_unmount(jobject vthread, bool hide); + + static void VTMS_mount_begin(jobject vthread); + static void VTMS_mount_end(jobject vthread); + + static void VTMS_unmount_begin(jobject vthread); + static void VTMS_unmount_end(jobject vthread); }; /////////////////////////////////////////////////////////////// diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 91cba5dd3ea..0efc2f7ffae 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -636,27 +636,29 @@ JRT_ENTRY(void, SharedRuntime::notify_jvmti_object_alloc(oopDesc* o, JavaThread* current->set_vm_result(h()); JRT_END -JRT_ENTRY(void, SharedRuntime::notify_jvmti_mount(oopDesc* vt, jboolean hide, jboolean first_mount, JavaThread* current)) +JRT_ENTRY(void, SharedRuntime::notify_jvmti_vthread_start(oopDesc* vt, jboolean hide, JavaThread* current)) + assert(hide == JNI_FALSE, "must be VTMS transition finish"); jobject vthread = JNIHandles::make_local(const_cast(vt)); - - if (hide) { - JvmtiVTMSTransitionDisabler::VTMS_mount_begin(vthread, first_mount); - } else { - JvmtiVTMSTransitionDisabler::VTMS_mount_end(vthread, first_mount); - } - + JvmtiVTMSTransitionDisabler::VTMS_vthread_start(vthread); JNIHandles::destroy_local(vthread); JRT_END -JRT_ENTRY(void, SharedRuntime::notify_jvmti_unmount(oopDesc* vt, jboolean hide, jboolean last_unmount, JavaThread* current)) +JRT_ENTRY(void, SharedRuntime::notify_jvmti_vthread_end(oopDesc* vt, jboolean hide, JavaThread* current)) + assert(hide == JNI_TRUE, "must be VTMS transition start"); jobject vthread = JNIHandles::make_local(const_cast(vt)); + JvmtiVTMSTransitionDisabler::VTMS_vthread_end(vthread); + JNIHandles::destroy_local(vthread); +JRT_END - if (hide) { - JvmtiVTMSTransitionDisabler::VTMS_unmount_begin(vthread, last_unmount); - } else { - JvmtiVTMSTransitionDisabler::VTMS_unmount_end(vthread, last_unmount); - } +JRT_ENTRY(void, SharedRuntime::notify_jvmti_vthread_mount(oopDesc* vt, jboolean hide, JavaThread* current)) + jobject vthread = JNIHandles::make_local(const_cast(vt)); + JvmtiVTMSTransitionDisabler::VTMS_vthread_mount(vthread, hide); + JNIHandles::destroy_local(vthread); +JRT_END +JRT_ENTRY(void, SharedRuntime::notify_jvmti_vthread_unmount(oopDesc* vt, jboolean hide, JavaThread* current)) + jobject vthread = JNIHandles::make_local(const_cast(vt)); + JvmtiVTMSTransitionDisabler::VTMS_vthread_unmount(vthread, hide); JNIHandles::destroy_local(vthread); JRT_END #endif // INCLUDE_JVMTI diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp index 3b0e90eec3f..e38e8ab4fcc 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -267,8 +267,10 @@ class SharedRuntime: AllStatic { #if INCLUDE_JVMTI static void notify_jvmti_object_alloc(oopDesc* o, JavaThread* current); // 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); + static void notify_jvmti_vthread_start(oopDesc* vt, jboolean hide, JavaThread* current); + static void notify_jvmti_vthread_end(oopDesc* vt, jboolean hide, JavaThread* current); + static void notify_jvmti_vthread_mount(oopDesc* vt, jboolean hide, JavaThread* current); + static void notify_jvmti_vthread_unmount(oopDesc* vt, jboolean hide, JavaThread* current); #endif // RedefineClasses() tracing support for obsolete method entry diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index b6530e1b929..746f93cdfac 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -205,22 +205,19 @@ final class VirtualThread extends BaseVirtualThread { } // set state to RUNNING - boolean firstRun; int initialState = state(); if (initialState == STARTED && compareAndSetState(STARTED, RUNNING)) { // first run - firstRun = true; } else if (initialState == RUNNABLE && compareAndSetState(RUNNABLE, RUNNING)) { // consume parking permit setParkPermit(false); - firstRun = false; } else { // not runnable return; } // notify JVMTI before mount - notifyJvmtiMount(/*hide*/true, firstRun); + notifyJvmtiMount(/*hide*/true); try { cont.run(); @@ -300,7 +297,7 @@ final class VirtualThread extends BaseVirtualThread { // first mount mount(); - notifyJvmtiMount(/*hide*/false, /*first*/true); + notifyJvmtiStart(); // emit JFR event if enabled if (VirtualThreadStartEvent.isTurnedOn()) { @@ -328,7 +325,7 @@ final class VirtualThread extends BaseVirtualThread { } finally { // last unmount - notifyJvmtiUnmount(/*hide*/true, /*last*/true); + notifyJvmtiEnd(); unmount(); // final state @@ -438,14 +435,14 @@ final class VirtualThread extends BaseVirtualThread { @ChangesCurrentThread private boolean yieldContinuation() { // unmount - notifyJvmtiUnmount(/*hide*/true, /*last*/false); + notifyJvmtiUnmount(/*hide*/true); unmount(); try { return Continuation.yield(VTHREAD_SCOPE); } finally { // re-mount mount(); - notifyJvmtiMount(/*hide*/false, /*first*/false); + notifyJvmtiMount(/*hide*/false); } } @@ -462,7 +459,7 @@ final class VirtualThread extends BaseVirtualThread { setState(PARKED); // notify JVMTI that unmount has completed, thread is parked - notifyJvmtiUnmount(/*hide*/false, /*last*/false); + notifyJvmtiUnmount(/*hide*/false); // may have been unparked while parking if (parkPermit && compareAndSetState(PARKED, RUNNABLE)) { @@ -478,7 +475,7 @@ final class VirtualThread extends BaseVirtualThread { setState(RUNNABLE); // notify JVMTI that unmount has completed, thread is runnable - notifyJvmtiUnmount(/*hide*/false, /*last*/false); + notifyJvmtiUnmount(/*hide*/false); // external submit if there are no tasks in the local task queue if (currentThread() instanceof CarrierThread ct && ct.getQueuedTaskCount() == 0) { @@ -508,7 +505,7 @@ final class VirtualThread extends BaseVirtualThread { assert (state() == TERMINATED) && (carrierThread == null); if (executed) { - notifyJvmtiUnmount(/*hide*/false, /*last*/true); + notifyJvmtiUnmount(/*hide*/false); } // notify anyone waiting for this virtual thread to terminate @@ -1086,11 +1083,19 @@ final class VirtualThread extends BaseVirtualThread { @IntrinsicCandidate @JvmtiMountTransition - private native void notifyJvmtiMount(boolean hide, boolean firstMount); + private native void notifyJvmtiStart(); @IntrinsicCandidate @JvmtiMountTransition - private native void notifyJvmtiUnmount(boolean hide, boolean lastUnmount); + private native void notifyJvmtiEnd(); + + @IntrinsicCandidate + @JvmtiMountTransition + private native void notifyJvmtiMount(boolean hide); + + @IntrinsicCandidate + @JvmtiMountTransition + private native void notifyJvmtiUnmount(boolean hide); @IntrinsicCandidate @JvmtiMountTransition diff --git a/src/java.base/share/native/libjava/VirtualThread.c b/src/java.base/share/native/libjava/VirtualThread.c index 9e79486e356..a0b3e082087 100644 --- a/src/java.base/share/native/libjava/VirtualThread.c +++ b/src/java.base/share/native/libjava/VirtualThread.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -32,9 +32,11 @@ #define VIRTUAL_THREAD "Ljava/lang/VirtualThread;" static JNINativeMethod methods[] = { - { "notifyJvmtiMount", "(ZZ)V", (void *)&JVM_VirtualThreadMount }, - { "notifyJvmtiUnmount", "(ZZ)V", (void *)&JVM_VirtualThreadUnmount }, - { "notifyJvmtiHideFrames", "(Z)V", (void *)&JVM_VirtualThreadHideFrames }, + { "notifyJvmtiStart", "()V", (void *)&JVM_VirtualThreadStart }, + { "notifyJvmtiEnd", "()V", (void *)&JVM_VirtualThreadEnd }, + { "notifyJvmtiMount", "(Z)V", (void *)&JVM_VirtualThreadMount }, + { "notifyJvmtiUnmount", "(Z)V", (void *)&JVM_VirtualThreadUnmount }, + { "notifyJvmtiHideFrames", "(Z)V", (void *)&JVM_VirtualThreadHideFrames }, }; JNIEXPORT void JNICALL