diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index ee80dbbc45c..614a0199beb 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1684,8 +1684,8 @@ int java_lang_Thread::_name_offset; int java_lang_Thread::_contextClassLoader_offset; int java_lang_Thread::_eetop_offset; 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::_vthread_transition_disable_count_offset; +int java_lang_Thread::_is_in_vthread_transition_offset; int java_lang_Thread::_interrupted_offset; int java_lang_Thread::_interruptLock_offset; int java_lang_Thread::_tid_offset; @@ -1745,34 +1745,34 @@ void java_lang_Thread::set_jvmti_thread_state(oop java_thread, JvmtiThreadState* java_thread->address_field_put(_jvmti_thread_state_offset, (address)state); } -int java_lang_Thread::VTMS_transition_disable_count(oop java_thread) { - return java_thread->int_field(_jvmti_VTMS_transition_disable_count_offset); +int java_lang_Thread::vthread_transition_disable_count(oop java_thread) { + jint* addr = java_thread->field_addr(_vthread_transition_disable_count_offset); + return AtomicAccess::load(addr); } -void java_lang_Thread::inc_VTMS_transition_disable_count(oop java_thread) { - assert(JvmtiVTMSTransition_lock->owned_by_self(), "Must be locked"); - int val = VTMS_transition_disable_count(java_thread); - java_thread->int_field_put(_jvmti_VTMS_transition_disable_count_offset, val + 1); +void java_lang_Thread::inc_vthread_transition_disable_count(oop java_thread) { + assert(VThreadTransition_lock->owned_by_self(), "Must be locked"); + jint* addr = java_thread->field_addr(_vthread_transition_disable_count_offset); + int val = AtomicAccess::load(addr); + AtomicAccess::store(addr, val + 1); } -void java_lang_Thread::dec_VTMS_transition_disable_count(oop java_thread) { - assert(JvmtiVTMSTransition_lock->owned_by_self(), "Must be locked"); - int val = VTMS_transition_disable_count(java_thread); - assert(val > 0, "VTMS_transition_disable_count should never be negative"); - java_thread->int_field_put(_jvmti_VTMS_transition_disable_count_offset, val - 1); +void java_lang_Thread::dec_vthread_transition_disable_count(oop java_thread) { + assert(VThreadTransition_lock->owned_by_self(), "Must be locked"); + jint* addr = java_thread->field_addr(_vthread_transition_disable_count_offset); + int val = AtomicAccess::load(addr); + AtomicAccess::store(addr, val - 1); } -bool java_lang_Thread::is_in_VTMS_transition(oop java_thread) { - return java_thread->bool_field_volatile(_jvmti_is_in_VTMS_transition_offset); +bool java_lang_Thread::is_in_vthread_transition(oop java_thread) { + jboolean* addr = java_thread->field_addr(_is_in_vthread_transition_offset); + return AtomicAccess::load(addr); } -void java_lang_Thread::set_is_in_VTMS_transition(oop java_thread, bool val) { - assert(is_in_VTMS_transition(java_thread) != val, "already %s transition", val ? "inside" : "outside"); - java_thread->bool_field_put_volatile(_jvmti_is_in_VTMS_transition_offset, val); -} - -int java_lang_Thread::is_in_VTMS_transition_offset() { - return _jvmti_is_in_VTMS_transition_offset; +void java_lang_Thread::set_is_in_vthread_transition(oop java_thread, bool val) { + assert(is_in_vthread_transition(java_thread) != val, "already %s transition", val ? "inside" : "outside"); + jboolean* addr = java_thread->field_addr(_is_in_vthread_transition_offset); + AtomicAccess::store(addr, (jboolean)val); } void java_lang_Thread::clear_scopedValueBindings(oop java_thread) { diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index 699dd39b887..33dc912404c 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -375,8 +375,8 @@ class java_lang_Class : AllStatic { #define THREAD_INJECTED_FIELDS(macro) \ macro(java_lang_Thread, jvmti_thread_state, intptr_signature, false) \ - macro(java_lang_Thread, jvmti_VTMS_transition_disable_count, int_signature, false) \ - macro(java_lang_Thread, jvmti_is_in_VTMS_transition, bool_signature, false) \ + macro(java_lang_Thread, vthread_transition_disable_count, int_signature, false) \ + macro(java_lang_Thread, is_in_vthread_transition, bool_signature, false) \ JFR_ONLY(macro(java_lang_Thread, jfr_epoch, short_signature, false)) class java_lang_Thread : AllStatic { @@ -390,8 +390,8 @@ class java_lang_Thread : AllStatic { static int _contextClassLoader_offset; static int _eetop_offset; static int _jvmti_thread_state_offset; - static int _jvmti_VTMS_transition_disable_count_offset; - static int _jvmti_is_in_VTMS_transition_offset; + static int _vthread_transition_disable_count_offset; + static int _is_in_vthread_transition_offset; static int _interrupted_offset; static int _interruptLock_offset; static int _tid_offset; @@ -444,12 +444,15 @@ class java_lang_Thread : AllStatic { static JvmtiThreadState* jvmti_thread_state(oop java_thread); static void set_jvmti_thread_state(oop java_thread, JvmtiThreadState* state); - static int VTMS_transition_disable_count(oop java_thread); - static void inc_VTMS_transition_disable_count(oop java_thread); - static void dec_VTMS_transition_disable_count(oop java_thread); - static bool is_in_VTMS_transition(oop java_thread); - static void set_is_in_VTMS_transition(oop java_thread, bool val); - static int is_in_VTMS_transition_offset(); + + static int vthread_transition_disable_count(oop java_thread); + static void inc_vthread_transition_disable_count(oop java_thread); + static void dec_vthread_transition_disable_count(oop java_thread); + static int vthread_transition_disable_count_offset() { return _vthread_transition_disable_count_offset; } + + static bool is_in_vthread_transition(oop java_thread); + static void set_is_in_vthread_transition(oop java_thread, bool val); + static int is_in_vthread_transition_offset() { return _is_in_vthread_transition_offset; } // Clear all scoped value bindings on error static void clear_scopedValueBindings(oop java_thread); diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 0895418ef84..6f9c2326a45 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -649,10 +649,10 @@ class methodHandle; do_intrinsic(_Continuation_unpin, jdk_internal_vm_Continuation, unpin_name, void_method_signature, F_SN) \ \ /* java/lang/VirtualThread */ \ - 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(_vthreadEndFirstTransition, java_lang_VirtualThread, endFirstTransition_name, void_method_signature, F_RN) \ + do_intrinsic(_vthreadStartFinalTransition, java_lang_VirtualThread, startFinalTransition_name, void_method_signature, F_RN) \ + do_intrinsic(_vthreadStartTransition, java_lang_VirtualThread, startTransition_name, bool_void_signature, F_RN) \ + do_intrinsic(_vthreadEndTransition, java_lang_VirtualThread, endTransition_name, bool_void_signature, F_RN) \ do_intrinsic(_notifyJvmtiVThreadDisableSuspend, java_lang_VirtualThread, notifyJvmtiDisableSuspend_name, bool_void_signature, F_SN) \ \ /* support for UnsafeConstants */ \ diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index d22700b8d1f..8388b98faae 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -395,10 +395,10 @@ class SerializeClosure; 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(startTransition_name, "startTransition") \ + template(endTransition_name, "endTransition") \ + template(startFinalTransition_name, "startFinalTransition") \ + template(endFirstTransition_name, "endFirstTransition") \ template(notifyJvmtiDisableSuspend_name, "notifyJvmtiDisableSuspend") \ template(doYield_name, "doYield") \ template(enter_name, "enter") \ @@ -497,8 +497,8 @@ class SerializeClosure; template(java_lang_Boolean_signature, "Ljava/lang/Boolean;") \ template(url_code_signer_array_void_signature, "(Ljava/net/URL;[Ljava/security/CodeSigner;)V") \ template(jvmti_thread_state_name, "jvmti_thread_state") \ - template(jvmti_VTMS_transition_disable_count_name, "jvmti_VTMS_transition_disable_count") \ - template(jvmti_is_in_VTMS_transition_name, "jvmti_is_in_VTMS_transition") \ + template(vthread_transition_disable_count_name, "vthread_transition_disable_count") \ + template(is_in_vthread_transition_name, "is_in_vthread_transition") \ template(module_entry_name, "module_entry") \ template(resolved_references_name, "") \ template(init_lock_name, "") \ diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 04776f4c16c..0314c5227d2 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -1346,18 +1346,16 @@ void AOTCodeAddressTable::init_extrs() { SET_ADDRESS(_extrs, OptoRuntime::multianewarray4_C); SET_ADDRESS(_extrs, OptoRuntime::multianewarray5_C); SET_ADDRESS(_extrs, OptoRuntime::multianewarrayN_C); -#if INCLUDE_JVMTI - SET_ADDRESS(_extrs, SharedRuntime::notify_jvmti_vthread_start); - SET_ADDRESS(_extrs, SharedRuntime::notify_jvmti_vthread_end); - SET_ADDRESS(_extrs, SharedRuntime::notify_jvmti_vthread_mount); - SET_ADDRESS(_extrs, SharedRuntime::notify_jvmti_vthread_unmount); -#endif SET_ADDRESS(_extrs, OptoRuntime::complete_monitor_locking_C); SET_ADDRESS(_extrs, OptoRuntime::monitor_notify_C); SET_ADDRESS(_extrs, OptoRuntime::monitor_notifyAll_C); SET_ADDRESS(_extrs, OptoRuntime::rethrow_C); SET_ADDRESS(_extrs, OptoRuntime::slow_arraycopy_C); SET_ADDRESS(_extrs, OptoRuntime::register_finalizer_C); + SET_ADDRESS(_extrs, OptoRuntime::vthread_end_first_transition_C); + SET_ADDRESS(_extrs, OptoRuntime::vthread_start_final_transition_C); + SET_ADDRESS(_extrs, OptoRuntime::vthread_start_transition_C); + SET_ADDRESS(_extrs, OptoRuntime::vthread_end_transition_C); #if defined(AARCH64) SET_ADDRESS(_extrs, JavaThread::verify_cross_modify_fence_failure); #endif // AARCH64 diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index ce76a95fda7..dccdcacef71 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -1100,16 +1100,16 @@ JVM_GetEnclosingMethodInfo(JNIEnv* env, jclass ofClass); * Virtual thread support. */ JNIEXPORT void JNICALL -JVM_VirtualThreadStart(JNIEnv* env, jobject vthread); +JVM_VirtualThreadEndFirstTransition(JNIEnv* env, jobject vthread); JNIEXPORT void JNICALL -JVM_VirtualThreadEnd(JNIEnv* env, jobject vthread); +JVM_VirtualThreadStartFinalTransition(JNIEnv* env, jobject vthread); JNIEXPORT void JNICALL -JVM_VirtualThreadMount(JNIEnv* env, jobject vthread, jboolean hide); +JVM_VirtualThreadStartTransition(JNIEnv* env, jobject vthread, jboolean is_mount); JNIEXPORT void JNICALL -JVM_VirtualThreadUnmount(JNIEnv* env, jobject vthread, jboolean hide); +JVM_VirtualThreadEndTransition(JNIEnv* env, jobject vthread, jboolean is_mount); JNIEXPORT void JNICALL JVM_VirtualThreadDisableSuspend(JNIEnv* env, jclass clazz, jboolean enter); diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 7ef16f6e32c..7f009eba5f1 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -37,6 +37,7 @@ #include "runtime/continuationEntry.hpp" #include "runtime/deoptimization.hpp" #include "runtime/flags/jvmFlag.hpp" +#include "runtime/mountUnmountDisabler.hpp" #include "runtime/objectMonitor.hpp" #include "runtime/osThread.hpp" #include "runtime/sharedRuntime.hpp" @@ -259,13 +260,13 @@ nonstatic_field(JavaThread, _om_cache, OMCache) \ nonstatic_field(JavaThread, _cont_entry, ContinuationEntry*) \ nonstatic_field(JavaThread, _unlocked_inflated_monitor, ObjectMonitor*) \ - JVMTI_ONLY(nonstatic_field(JavaThread, _is_in_VTMS_transition, bool)) \ + nonstatic_field(JavaThread, _is_in_vthread_transition, bool) \ JVMTI_ONLY(nonstatic_field(JavaThread, _is_disable_suspend, bool)) \ \ nonstatic_field(ContinuationEntry, _pin_count, uint32_t) \ nonstatic_field(LockStack, _top, uint32_t) \ \ - JVMTI_ONLY(static_field(JvmtiVTMSTransitionDisabler, _VTMS_notify_jvmti_events, bool)) \ + static_field(MountUnmountDisabler, _notify_jvmti_events, bool) \ \ static_field(java_lang_Class, _klass_offset, int) \ static_field(java_lang_Class, _array_klass_offset, int) \ @@ -435,7 +436,7 @@ JFR_ONLY(nonstatic_field(Thread, _jfr_thread_local, JfrThreadLocal)) \ \ static_field(java_lang_Thread, _tid_offset, int) \ - static_field(java_lang_Thread, _jvmti_is_in_VTMS_transition_offset, int) \ + static_field(java_lang_Thread, _is_in_vthread_transition_offset, int) \ JFR_ONLY(static_field(java_lang_Thread, _jfr_epoch_offset, int)) \ \ JFR_ONLY(nonstatic_field(JfrThreadLocal, _vthread_id, traceid)) \ @@ -877,10 +878,6 @@ declare_function(SharedRuntime::enable_stack_reserved_zone) \ declare_function(SharedRuntime::frem) \ declare_function(SharedRuntime::drem) \ - JVMTI_ONLY(declare_function(SharedRuntime::notify_jvmti_vthread_start)) \ - JVMTI_ONLY(declare_function(SharedRuntime::notify_jvmti_vthread_end)) \ - JVMTI_ONLY(declare_function(SharedRuntime::notify_jvmti_vthread_mount)) \ - JVMTI_ONLY(declare_function(SharedRuntime::notify_jvmti_vthread_unmount)) \ \ declare_function(os::dll_load) \ declare_function(os::dll_lookup) \ diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index acc28964627..ead1b78cdea 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -859,11 +859,11 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_VectorBinaryLibOp: return EnableVectorSupport && Matcher::supports_vector_calling_convention(); case vmIntrinsics::_blackhole: + case vmIntrinsics::_vthreadEndFirstTransition: + case vmIntrinsics::_vthreadStartFinalTransition: + case vmIntrinsics::_vthreadStartTransition: + case vmIntrinsics::_vthreadEndTransition: #if INCLUDE_JVMTI - case vmIntrinsics::_notifyJvmtiVThreadStart: - case vmIntrinsics::_notifyJvmtiVThreadEnd: - case vmIntrinsics::_notifyJvmtiVThreadMount: - case vmIntrinsics::_notifyJvmtiVThreadUnmount: case vmIntrinsics::_notifyJvmtiVThreadDisableSuspend: #endif break; diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index dc53cb08396..6d4a9104580 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -55,6 +55,7 @@ #include "prims/jvmtiThreadState.hpp" #include "prims/unsafe.hpp" #include "runtime/jniHandles.inline.hpp" +#include "runtime/mountUnmountDisabler.hpp" #include "runtime/objectMonitor.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" @@ -479,15 +480,15 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_Continuation_pin: return inline_native_Continuation_pinning(false); case vmIntrinsics::_Continuation_unpin: return inline_native_Continuation_pinning(true); + case vmIntrinsics::_vthreadEndFirstTransition: return inline_native_vthread_end_transition(CAST_FROM_FN_PTR(address, OptoRuntime::vthread_end_first_transition_Java()), + "endFirstTransition", true); + case vmIntrinsics::_vthreadStartFinalTransition: return inline_native_vthread_start_transition(CAST_FROM_FN_PTR(address, OptoRuntime::vthread_start_final_transition_Java()), + "startFinalTransition", true); + case vmIntrinsics::_vthreadStartTransition: return inline_native_vthread_start_transition(CAST_FROM_FN_PTR(address, OptoRuntime::vthread_start_transition_Java()), + "startTransition", false); + case vmIntrinsics::_vthreadEndTransition: return inline_native_vthread_end_transition(CAST_FROM_FN_PTR(address, OptoRuntime::vthread_end_transition_Java()), + "endTransition", false); #if INCLUDE_JVMTI - 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::_notifyJvmtiVThreadDisableSuspend: return inline_native_notify_jvmti_sync(); #endif @@ -3042,46 +3043,80 @@ 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, bool is_start, bool is_end) { - if (!DoJVMTIVirtualThreadTransitions) { - return true; - } +//--------------------inline_native_vthread_start_transition-------------------- +// inline void startTransition(boolean is_mount); +// inline void startFinalTransition(); +// Pseudocode of implementation: +// +// java_lang_Thread::set_is_in_vthread_transition(vthread, true); +// carrier->set_is_in_vthread_transition(true); +// OrderAccess::storeload(); +// int disable_requests = java_lang_Thread::vthread_transition_disable_count(vthread) +// + global_vthread_transition_disable_count(); +// if (disable_requests > 0) { +// slow path: runtime call +// } +bool LibraryCallKit::inline_native_vthread_start_transition(address funcAddr, const char* funcName, bool is_final_transition) { Node* vt_oop = _gvn.transform(must_be_not_null(argument(0), true)); // VirtualThread this argument IdealKit ideal(this); - Node* ONE = ideal.ConI(1); - 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); + Node* thread = ideal.thread(); + Node* jt_addr = basic_plus_adr(thread, in_bytes(JavaThread::is_in_vthread_transition_offset())); + Node* vt_addr = basic_plus_adr(vt_oop, java_lang_Thread::is_in_vthread_transition_offset()); + access_store_at(nullptr, jt_addr, _gvn.type(jt_addr)->is_ptr(), ideal.ConI(1), TypeInt::BOOL, T_BOOLEAN, IN_NATIVE | MO_UNORDERED); + access_store_at(nullptr, vt_addr, _gvn.type(vt_addr)->is_ptr(), ideal.ConI(1), TypeInt::BOOL, T_BOOLEAN, IN_NATIVE | MO_UNORDERED); + insert_mem_bar(Op_MemBarVolatile); + ideal.sync_kit(this); - ideal.if_then(notify_jvmti_enabled, BoolTest::eq, ONE); { + Node* global_disable_addr = makecon(TypeRawPtr::make((address)MountUnmountDisabler::global_vthread_transition_disable_count_address())); + Node* global_disable = ideal.load(ideal.ctrl(), global_disable_addr, TypeInt::INT, T_INT, Compile::AliasIdxRaw, true /*require_atomic_access*/); + Node* vt_disable_addr = basic_plus_adr(vt_oop, java_lang_Thread::vthread_transition_disable_count_offset()); + Node* vt_disable = ideal.load(ideal.ctrl(), vt_disable_addr, TypeInt::INT, T_INT, Compile::AliasIdxRaw, true /*require_atomic_access*/); + Node* disabled = _gvn.transform(new AddINode(global_disable, vt_disable)); + + ideal.if_then(disabled, BoolTest::ne, ideal.ConI(0)); { sync_kit(ideal); - // if notifyJvmti enabled then make a call to the given SharedRuntime function - const TypeFunc* tf = OptoRuntime::notify_jvmti_vthread_Type(); - make_runtime_call(RC_NO_LEAF, tf, funcAddr, funcName, TypePtr::BOTTOM, vt_oop, hide); + Node* is_mount = is_final_transition ? ideal.ConI(0) : _gvn.transform(argument(1)); + const TypeFunc* tf = OptoRuntime::vthread_transition_Type(); + make_runtime_call(RC_NO_LEAF, tf, funcAddr, funcName, TypePtr::BOTTOM, vt_oop, is_mount); ideal.sync_kit(this); - } ideal.else_(); { - // set hide value to the VTMS transition bit in current JavaThread and VirtualThread object - Node* thread = ideal.thread(); - Node* jt_addr = basic_plus_adr(thread, in_bytes(JavaThread::is_in_VTMS_transition_offset())); - Node* vt_addr = basic_plus_adr(vt_oop, java_lang_Thread::is_in_VTMS_transition_offset()); + } + ideal.end_if(); - sync_kit(ideal); - access_store_at(nullptr, jt_addr, _gvn.type(jt_addr)->is_ptr(), hide, _gvn.type(hide), T_BOOLEAN, IN_NATIVE | MO_UNORDERED); - access_store_at(nullptr, vt_addr, _gvn.type(vt_addr)->is_ptr(), hide, _gvn.type(hide), T_BOOLEAN, IN_NATIVE | MO_UNORDERED); - - ideal.sync_kit(this); - } ideal.end_if(); final_sync(ideal); - return true; } +bool LibraryCallKit::inline_native_vthread_end_transition(address funcAddr, const char* funcName, bool is_first_transition) { + Node* vt_oop = _gvn.transform(must_be_not_null(argument(0), true)); // VirtualThread this argument + IdealKit ideal(this); + + Node* _notify_jvmti_addr = makecon(TypeRawPtr::make((address)MountUnmountDisabler::notify_jvmti_events_address())); + Node* _notify_jvmti = ideal.load(ideal.ctrl(), _notify_jvmti_addr, TypeInt::BOOL, T_BOOLEAN, Compile::AliasIdxRaw); + + ideal.if_then(_notify_jvmti, BoolTest::eq, ideal.ConI(1)); { + sync_kit(ideal); + Node* is_mount = is_first_transition ? ideal.ConI(1) : _gvn.transform(argument(1)); + const TypeFunc* tf = OptoRuntime::vthread_transition_Type(); + make_runtime_call(RC_NO_LEAF, tf, funcAddr, funcName, TypePtr::BOTTOM, vt_oop, is_mount); + ideal.sync_kit(this); + } ideal.else_(); { + Node* thread = ideal.thread(); + Node* jt_addr = basic_plus_adr(thread, in_bytes(JavaThread::is_in_vthread_transition_offset())); + Node* vt_addr = basic_plus_adr(vt_oop, java_lang_Thread::is_in_vthread_transition_offset()); + + sync_kit(ideal); + access_store_at(nullptr, jt_addr, _gvn.type(jt_addr)->is_ptr(), ideal.ConI(0), TypeInt::BOOL, T_BOOLEAN, IN_NATIVE | MO_UNORDERED); + access_store_at(nullptr, vt_addr, _gvn.type(vt_addr)->is_ptr(), ideal.ConI(0), TypeInt::BOOL, T_BOOLEAN, IN_NATIVE | MO_UNORDERED); + ideal.sync_kit(this); + } ideal.end_if(); + + final_sync(ideal); + return true; +} + +#if INCLUDE_JVMTI + // Always update the is_disable_suspend bit. bool LibraryCallKit::inline_native_notify_jvmti_sync() { if (!DoJVMTIVirtualThreadTransitions) { diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 7dbb57c1e5c..bfe29814dec 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -275,9 +275,11 @@ class LibraryCallKit : public GraphKit { bool inline_native_Continuation_pinning(bool unpin); bool inline_native_time_funcs(address method, const char* funcName); + + bool inline_native_vthread_start_transition(address funcAddr, const char* funcName, bool is_final_transition); + bool inline_native_vthread_end_transition(address funcAddr, const char* funcName, bool is_first_transition); + #if INCLUDE_JVMTI - bool inline_native_notify_jvmti_funcs(address funcAddr, const char* funcName, bool is_start, bool is_end); - bool inline_native_notify_jvmti_hide(); bool inline_native_notify_jvmti_sync(); #endif diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index 32acbe57951..6dbbfb0a130 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -66,6 +66,7 @@ #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaCalls.hpp" +#include "runtime/mountUnmountDisabler.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/signature.hpp" #include "runtime/stackWatermarkSet.hpp" @@ -92,12 +93,9 @@ #define C2_STUB_FIELD_NAME(name) _ ## name ## _Java #define C2_STUB_FIELD_DEFINE(name, f, t, r) \ address OptoRuntime:: C2_STUB_FIELD_NAME(name) = nullptr; -#define C2_JVMTI_STUB_FIELD_DEFINE(name) \ - address OptoRuntime:: STUB_FIELD_NAME(name) = nullptr; -C2_STUBS_DO(C2_BLOB_FIELD_DEFINE, C2_STUB_FIELD_DEFINE, C2_JVMTI_STUB_FIELD_DEFINE) +C2_STUBS_DO(C2_BLOB_FIELD_DEFINE, C2_STUB_FIELD_DEFINE) #undef C2_BLOB_FIELD_DEFINE #undef C2_STUB_FIELD_DEFINE -#undef C2_JVMTI_STUB_FIELD_DEFINE // This should be called in an assertion at the start of OptoRuntime routines // which are entered from compiled code (all of them) @@ -153,23 +151,9 @@ static bool check_compiled_frame(JavaThread* thread) { pass_retpc); \ if (C2_STUB_FIELD_NAME(name) == nullptr) { return false; } \ -#define C2_JVMTI_STUB_C_FUNC(name) CAST_FROM_FN_PTR(address, SharedRuntime::name) - -#define GEN_C2_JVMTI_STUB(name) \ - STUB_FIELD_NAME(name) = \ - generate_stub(env, \ - notify_jvmti_vthread_Type, \ - C2_JVMTI_STUB_C_FUNC(name), \ - C2_STUB_NAME(name), \ - C2_STUB_ID(name), \ - 0, \ - true, \ - false); \ - if (STUB_FIELD_NAME(name) == nullptr) { return false; } \ - bool OptoRuntime::generate(ciEnv* env) { - C2_STUBS_DO(GEN_C2_BLOB, GEN_C2_STUB, GEN_C2_JVMTI_STUB) + C2_STUBS_DO(GEN_C2_BLOB, GEN_C2_STUB) return true; } @@ -182,8 +166,6 @@ bool OptoRuntime::generate(ciEnv* env) { #undef C2_STUB_NAME #undef GEN_C2_STUB -#undef C2_JVMTI_STUB_C_FUNC -#undef GEN_C2_JVMTI_STUB // #undef gen const TypeFunc* OptoRuntime::_new_instance_Type = nullptr; @@ -257,12 +239,10 @@ const TypeFunc* OptoRuntime::_updateBytesCRC32C_Type = nullptr; const TypeFunc* OptoRuntime::_updateBytesAdler32_Type = nullptr; const TypeFunc* OptoRuntime::_osr_end_Type = nullptr; const TypeFunc* OptoRuntime::_register_finalizer_Type = nullptr; +const TypeFunc* OptoRuntime::_vthread_transition_Type = nullptr; #if INCLUDE_JFR const TypeFunc* OptoRuntime::_class_id_load_barrier_Type = nullptr; #endif // INCLUDE_JFR -#if INCLUDE_JVMTI -const TypeFunc* OptoRuntime::_notify_jvmti_vthread_Type = nullptr; -#endif // INCLUDE_JVMTI const TypeFunc* OptoRuntime::_dtrace_method_entry_exit_Type = nullptr; const TypeFunc* OptoRuntime::_dtrace_object_alloc_Type = nullptr; @@ -572,6 +552,26 @@ JRT_BLOCK_ENTRY(void, OptoRuntime::monitor_notifyAll_C(oopDesc* obj, JavaThread* JRT_BLOCK_END; JRT_END +JRT_ENTRY(void, OptoRuntime::vthread_end_first_transition_C(oopDesc* vt, jboolean is_mount, JavaThread* current)) + MountUnmountDisabler::end_transition(current, vt, true /*is_mount*/, true /*is_thread_start*/); +JRT_END + +JRT_ENTRY(void, OptoRuntime::vthread_start_final_transition_C(oopDesc* vt, jboolean is_mount, JavaThread* current)) + java_lang_Thread::set_is_in_vthread_transition(vt, false); + current->set_is_in_vthread_transition(false); + MountUnmountDisabler::start_transition(current, vt, false /*is_mount */, true /*is_thread_end*/); +JRT_END + +JRT_ENTRY(void, OptoRuntime::vthread_start_transition_C(oopDesc* vt, jboolean is_mount, JavaThread* current)) + java_lang_Thread::set_is_in_vthread_transition(vt, false); + current->set_is_in_vthread_transition(false); + MountUnmountDisabler::start_transition(current, vt, is_mount, false /*is_thread_end*/); +JRT_END + +JRT_ENTRY(void, OptoRuntime::vthread_end_transition_C(oopDesc* vt, jboolean is_mount, JavaThread* current)) + MountUnmountDisabler::end_transition(current, vt, is_mount, false /*is_thread_start*/); +JRT_END + static const TypeFunc* make_new_instance_Type() { // create input type (domain) const Type **fields = TypeTuple::fields(1); @@ -587,8 +587,7 @@ static const TypeFunc* make_new_instance_Type() { return TypeFunc::make(domain, range); } -#if INCLUDE_JVMTI -static const TypeFunc* make_notify_jvmti_vthread_Type() { +static const TypeFunc* make_vthread_transition_Type() { // create input type (domain) const Type **fields = TypeTuple::fields(2); fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // VirtualThread oop @@ -602,7 +601,6 @@ static const TypeFunc* make_notify_jvmti_vthread_Type() { return TypeFunc::make(domain,range); } -#endif static const TypeFunc* make_athrow_Type() { // create input type (domain) @@ -2336,12 +2334,10 @@ void OptoRuntime::initialize_types() { _updateBytesAdler32_Type = make_updateBytesAdler32_Type(); _osr_end_Type = make_osr_end_Type(); _register_finalizer_Type = make_register_finalizer_Type(); + _vthread_transition_Type = make_vthread_transition_Type(); JFR_ONLY( _class_id_load_barrier_Type = make_class_id_load_barrier_Type(); ) -#if INCLUDE_JVMTI - _notify_jvmti_vthread_Type = make_notify_jvmti_vthread_Type(); -#endif // INCLUDE_JVMTI _dtrace_method_entry_exit_Type = make_dtrace_method_entry_exit_Type(); _dtrace_object_alloc_Type = make_dtrace_object_alloc_Type(); } diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp index eb2b49311cf..b8cdd9a962a 100644 --- a/src/hotspot/share/opto/runtime.hpp +++ b/src/hotspot/share/opto/runtime.hpp @@ -115,15 +115,12 @@ class OptoRuntime : public AllStatic { #define C2_STUB_FIELD_NAME(name) _ ## name ## _Java #define C2_STUB_FIELD_DECLARE(name, f, t, r) \ static address C2_STUB_FIELD_NAME(name) ; -#define C2_JVMTI_STUB_FIELD_DECLARE(name) \ - static address STUB_FIELD_NAME(name); - C2_STUBS_DO(C2_BLOB_FIELD_DECLARE, C2_STUB_FIELD_DECLARE, C2_JVMTI_STUB_FIELD_DECLARE) + C2_STUBS_DO(C2_BLOB_FIELD_DECLARE, C2_STUB_FIELD_DECLARE) #undef C2_BLOB_FIELD_DECLARE #undef C2_STUB_FIELD_NAME #undef C2_STUB_FIELD_DECLARE -#undef C2_JVMTI_STUB_FIELD_DECLARE // static TypeFunc* data members static const TypeFunc* _new_instance_Type; @@ -197,12 +194,10 @@ class OptoRuntime : public AllStatic { static const TypeFunc* _updateBytesAdler32_Type; static const TypeFunc* _osr_end_Type; static const TypeFunc* _register_finalizer_Type; + static const TypeFunc* _vthread_transition_Type; #if INCLUDE_JFR static const TypeFunc* _class_id_load_barrier_Type; #endif // INCLUDE_JFR -#if INCLUDE_JVMTI - static const TypeFunc* _notify_jvmti_vthread_Type; -#endif // INCLUDE_JVMTI static const TypeFunc* _dtrace_method_entry_exit_Type; static const TypeFunc* _dtrace_object_alloc_Type; @@ -239,6 +234,11 @@ public: static void monitor_notify_C(oopDesc* obj, JavaThread* current); static void monitor_notifyAll_C(oopDesc* obj, JavaThread* current); + static void vthread_end_first_transition_C(oopDesc* vt, jboolean hide, JavaThread* current); + static void vthread_start_final_transition_C(oopDesc* vt, jboolean hide, JavaThread* current); + static void vthread_start_transition_C(oopDesc* vt, jboolean hide, JavaThread* current); + static void vthread_end_transition_C(oopDesc* vt, jboolean hide, JavaThread* current); + private: // Implicit exception support @@ -293,12 +293,11 @@ 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_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 address vthread_end_first_transition_Java() { return _vthread_end_first_transition_Java; } + static address vthread_start_final_transition_Java() { return _vthread_start_final_transition_Java; } + static address vthread_start_transition_Java() { return _vthread_start_transition_Java; } + static address vthread_end_transition_Java() { return _vthread_end_transition_Java; } static UncommonTrapBlob* uncommon_trap_blob() { return _uncommon_trap_blob; } static ExceptionBlob* exception_blob() { return _exception_blob; } @@ -718,6 +717,27 @@ private: return _register_finalizer_Type; } + static inline const TypeFunc* vthread_transition_Type() { + assert(_vthread_transition_Type != nullptr, "should be initialized"); + return _vthread_transition_Type; + } + + static inline const TypeFunc* vthread_end_first_transition_Type() { + return vthread_transition_Type(); + } + + static inline const TypeFunc* vthread_start_final_transition_Type() { + return vthread_transition_Type(); + } + + static inline const TypeFunc* vthread_start_transition_Type() { + return vthread_transition_Type(); + } + + static inline const TypeFunc* vthread_end_transition_Type() { + return vthread_transition_Type(); + } + #if INCLUDE_JFR static inline const TypeFunc* class_id_load_barrier_Type() { assert(_class_id_load_barrier_Type != nullptr, "should be initialized"); @@ -725,13 +745,6 @@ private: } #endif // INCLUDE_JFR -#if INCLUDE_JVMTI - static inline const TypeFunc* notify_jvmti_vthread_Type() { - assert(_notify_jvmti_vthread_Type != nullptr, "should be initialized"); - return _notify_jvmti_vthread_Type; - } -#endif - // Dtrace support. entry and exit probes have the same signature static inline const TypeFunc* dtrace_method_entry_exit_Type() { assert(_dtrace_method_entry_exit_Type != nullptr, "should be initialized"); diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index a868f6337e2..16d9efde410 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -84,6 +84,7 @@ #include "runtime/javaThread.hpp" #include "runtime/jfieldIDWorkaround.hpp" #include "runtime/jniHandles.inline.hpp" +#include "runtime/mountUnmountDisabler.hpp" #include "runtime/os.inline.hpp" #include "runtime/osThread.hpp" #include "runtime/perfData.hpp" @@ -3661,68 +3662,24 @@ JVM_LEAF(jint, JVM_FindSignal(const char *name)) return os::get_signal_number(name); JVM_END -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()) { - 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); - } -#endif +JVM_ENTRY(void, JVM_VirtualThreadEndFirstTransition(JNIEnv* env, jobject vthread)) + oop vt = JNIHandles::resolve_external_guard(vthread); + MountUnmountDisabler::end_transition(thread, vt, true /*is_mount*/, true /*is_thread_start*/); 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 (JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { - JvmtiVTMSTransitionDisabler::VTMS_vthread_end(vthread); - } else { - // set VTMS transition bit value in JavaThread and java.lang.VirtualThread object - JvmtiVTMSTransitionDisabler::set_is_in_VTMS_transition(thread, vthread, true); - } -#endif +JVM_ENTRY(void, JVM_VirtualThreadStartFinalTransition(JNIEnv* env, jobject vthread)) + oop vt = JNIHandles::resolve_external_guard(vthread); + MountUnmountDisabler::start_transition(thread, vt, false /*is_mount */, true /*is_thread_end*/); 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); - } -#endif +JVM_ENTRY(void, JVM_VirtualThreadStartTransition(JNIEnv* env, jobject vthread, jboolean is_mount)) + oop vt = JNIHandles::resolve_external_guard(vthread); + MountUnmountDisabler::start_transition(thread, vt, is_mount, false /*is_thread_end*/); 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)) -#if INCLUDE_JVMTI - if (!DoJVMTIVirtualThreadTransitions) { - assert(!JvmtiExport::can_support_virtual_threads(), "sanity check"); - return; - } - if (JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { - JvmtiVTMSTransitionDisabler::VTMS_vthread_unmount(vthread, hide); - } else { - // set VTMS transition bit value in JavaThread and java.lang.VirtualThread object - JvmtiVTMSTransitionDisabler::set_is_in_VTMS_transition(thread, vthread, hide); - } -#endif +JVM_ENTRY(void, JVM_VirtualThreadEndTransition(JNIEnv* env, jobject vthread, jboolean is_mount)) + oop vt = JNIHandles::resolve_external_guard(vthread); + MountUnmountDisabler::end_transition(thread, vt, is_mount, false /*is_thread_start*/); JVM_END // Notification from VirtualThread about disabling JVMTI Suspend in a sync critical section. @@ -3772,6 +3729,7 @@ JVM_ENTRY(jobject, JVM_TakeVirtualThreadListToUnblock(JNIEnv* env, jclass ignore parkEvent->park(); } JVM_END + /* * Return the current class's class file version. The low order 16 bits of the * returned jint contain the class's major version. The high order 16 bits diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp index 5642cd9ff8f..e0863c07f4f 100644 --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -66,6 +66,7 @@ #include "runtime/javaThread.inline.hpp" #include "runtime/jfieldIDWorkaround.hpp" #include "runtime/jniHandles.inline.hpp" +#include "runtime/mountUnmountDisabler.hpp" #include "runtime/objectMonitor.inline.hpp" #include "runtime/os.hpp" #include "runtime/osThread.hpp" @@ -147,7 +148,7 @@ jvmtiError JvmtiEnv::SetThreadLocalStorage(jthread thread, const void* data) { JavaThread* current = JavaThread::current(); JvmtiThreadState* state = nullptr; - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current); JavaThread* java_thread = nullptr; @@ -200,7 +201,7 @@ JvmtiEnv::GetThreadLocalStorage(jthread thread, void** data_ptr) { VM_ENTRY_BASE(jvmtiError, JvmtiEnv::GetThreadLocalStorage , current_thread) DEBUG_ONLY(VMNativeEntryWrapper __vew;) - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -561,7 +562,7 @@ JvmtiEnv::SetNativeMethodPrefixes(jint prefix_count, char** prefixes) { // size_of_callbacks - pre-checked to be greater than or equal to 0 jvmtiError JvmtiEnv::SetEventCallbacks(const jvmtiEventCallbacks* callbacks, jint size_of_callbacks) { - JvmtiVTMSTransitionDisabler disabler; + MountUnmountDisabler disabler; JvmtiEventController::set_event_callbacks(this, callbacks, size_of_callbacks); return JVMTI_ERROR_NONE; } /* end SetEventCallbacks */ @@ -585,7 +586,7 @@ JvmtiEnv::SetEventNotificationMode(jvmtiEventMode mode, jvmtiEvent event_type, j if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) { record_class_file_load_hook_enabled(); } - JvmtiVTMSTransitionDisabler disabler; + MountUnmountDisabler disabler; if (event_thread == nullptr) { // Can be called at Agent_OnLoad() time with event_thread == nullptr @@ -867,7 +868,7 @@ JvmtiEnv::GetJLocationFormat(jvmtiJlocationFormat* format_ptr) { jvmtiError JvmtiEnv::GetThreadState(jthread thread, jint* thread_state_ptr) { JavaThread* current_thread = JavaThread::current(); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -939,7 +940,7 @@ JvmtiEnv::SuspendThread(jthread thread) { jvmtiError err; { - JvmtiVTMSTransitionDisabler disabler(true); + MountUnmountDisabler disabler(true); ThreadsListHandle tlh(current); JavaThread* java_thread = nullptr; oop thread_oop = nullptr; @@ -949,7 +950,7 @@ JvmtiEnv::SuspendThread(jthread thread) { return err; } - // Do not use JvmtiVTMSTransitionDisabler in context of self suspend to avoid deadlocks. + // Do not use MountUnmountDisabler in context of self suspend to avoid deadlocks. if (java_thread != current) { err = suspend_thread(thread_oop, java_thread, /* single_suspend */ true); return err; @@ -974,7 +975,7 @@ JvmtiEnv::SuspendThreadList(jint request_count, const jthread* request_list, jvm int self_idx = -1; { - JvmtiVTMSTransitionDisabler disabler(true); + MountUnmountDisabler disabler(true); ThreadsListHandle tlh(current); for (int i = 0; i < request_count; i++) { @@ -1007,7 +1008,7 @@ JvmtiEnv::SuspendThreadList(jint request_count, const jthread* request_list, jvm } } // Self suspend after all other suspends if necessary. - // Do not use JvmtiVTMSTransitionDisabler in context of self suspend to avoid deadlocks. + // Do not use MountUnmountDisabler in context of self suspend to avoid deadlocks. if (self_tobj() != nullptr) { // there should not be any error for current java_thread results[self_idx] = suspend_thread(self_tobj(), current, /* single_suspend */ true); @@ -1028,7 +1029,7 @@ JvmtiEnv::SuspendAllVirtualThreads(jint except_count, const jthread* except_list { ResourceMark rm(current); - JvmtiVTMSTransitionDisabler disabler(true); + MountUnmountDisabler disabler(true); ThreadsListHandle tlh(current); GrowableArray* elist = new GrowableArray(except_count); @@ -1078,7 +1079,7 @@ JvmtiEnv::SuspendAllVirtualThreads(jint except_count, const jthread* except_list } } // Self suspend after all other suspends if necessary. - // Do not use JvmtiVTMSTransitionDisabler in context of self suspend to avoid deadlocks. + // Do not use MountUnmountDisabler in context of self suspend to avoid deadlocks. if (self_tobj() != nullptr) { suspend_thread(self_tobj(), current, /* single_suspend */ false); } @@ -1089,7 +1090,7 @@ JvmtiEnv::SuspendAllVirtualThreads(jint except_count, const jthread* except_list // thread - NOT protected by ThreadsListHandle and NOT pre-checked jvmtiError JvmtiEnv::ResumeThread(jthread thread) { - JvmtiVTMSTransitionDisabler disabler(true); + MountUnmountDisabler disabler(true); JavaThread* current = JavaThread::current(); ThreadsListHandle tlh(current); @@ -1111,7 +1112,7 @@ jvmtiError JvmtiEnv::ResumeThreadList(jint request_count, const jthread* request_list, jvmtiError* results) { oop thread_oop = nullptr; JavaThread* java_thread = nullptr; - JvmtiVTMSTransitionDisabler disabler(true); + MountUnmountDisabler disabler(true); ThreadsListHandle tlh; for (int i = 0; i < request_count; i++) { @@ -1150,7 +1151,7 @@ JvmtiEnv::ResumeAllVirtualThreads(jint except_count, const jthread* except_list) return err; } ResourceMark rm; - JvmtiVTMSTransitionDisabler disabler(true); + MountUnmountDisabler disabler(true); GrowableArray* elist = new GrowableArray(except_count); // Collect threads from except_list for which suspended status must be restored (only for VirtualThread case) @@ -1196,7 +1197,7 @@ jvmtiError JvmtiEnv::StopThread(jthread thread, jobject exception) { JavaThread* current_thread = JavaThread::current(); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; oop thread_oop = nullptr; @@ -1234,7 +1235,7 @@ JvmtiEnv::InterruptThread(jthread thread) { JavaThread* current_thread = JavaThread::current(); HandleMark hm(current_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -1280,7 +1281,7 @@ JvmtiEnv::GetThreadInfo(jthread thread, jvmtiThreadInfo* info_ptr) { JavaThread* java_thread = nullptr; oop thread_oop = nullptr; - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); // if thread is null the current thread is used @@ -1369,7 +1370,7 @@ JvmtiEnv::GetOwnedMonitorInfo(jthread thread, jint* owned_monitor_count_ptr, job JavaThread* calling_thread = JavaThread::current(); HandleMark hm(calling_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(calling_thread); JavaThread* java_thread = nullptr; @@ -1424,7 +1425,7 @@ JvmtiEnv::GetOwnedMonitorStackDepthInfo(jthread thread, jint* monitor_info_count JavaThread* calling_thread = JavaThread::current(); HandleMark hm(calling_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(calling_thread); JavaThread* java_thread = nullptr; @@ -1707,7 +1708,7 @@ JvmtiEnv::GetThreadListStackTraces(jint thread_count, const jthread* thread_list *stack_info_ptr = op.stack_info(); } } else { - JvmtiVTMSTransitionDisabler disabler; + MountUnmountDisabler disabler; // JVMTI get stack traces at safepoint. VM_GetThreadListStackTraces op(this, thread_count, thread_list, max_frame_count); @@ -1740,7 +1741,7 @@ JvmtiEnv::PopFrame(jthread thread) { if (thread == nullptr) { return JVMTI_ERROR_INVALID_THREAD; } - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -1795,7 +1796,7 @@ JvmtiEnv::GetFrameLocation(jthread thread, jint depth, jmethodID* method_ptr, jl jvmtiError JvmtiEnv::NotifyFramePop(jthread thread, jint depth) { ResourceMark rm; - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); JavaThread* current = JavaThread::current(); ThreadsListHandle tlh(current); @@ -1823,7 +1824,7 @@ JvmtiEnv::NotifyFramePop(jthread thread, jint depth) { jvmtiError JvmtiEnv::ClearAllFramePops(jthread thread) { ResourceMark rm; - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); JavaThread* current = JavaThread::current(); ThreadsListHandle tlh(current); @@ -2084,7 +2085,7 @@ JvmtiEnv::GetLocalObject(jthread thread, jint depth, jint slot, jobject* value_p // doit_prologue(), but after doit() is finished with it. ResourceMark rm(current_thread); HandleMark hm(current_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -2125,7 +2126,7 @@ JvmtiEnv::GetLocalInstance(jthread thread, jint depth, jobject* value_ptr){ // doit_prologue(), but after doit() is finished with it. ResourceMark rm(current_thread); HandleMark hm(current_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -2167,7 +2168,7 @@ JvmtiEnv::GetLocalInt(jthread thread, jint depth, jint slot, jint* value_ptr) { // doit_prologue(), but after doit() is finished with it. ResourceMark rm(current_thread); HandleMark hm(current_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -2209,7 +2210,7 @@ JvmtiEnv::GetLocalLong(jthread thread, jint depth, jint slot, jlong* value_ptr) // doit_prologue(), but after doit() is finished with it. ResourceMark rm(current_thread); HandleMark hm(current_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -2251,7 +2252,7 @@ JvmtiEnv::GetLocalFloat(jthread thread, jint depth, jint slot, jfloat* value_ptr // doit_prologue(), but after doit() is finished with it. ResourceMark rm(current_thread); HandleMark hm(current_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -2293,7 +2294,7 @@ JvmtiEnv::GetLocalDouble(jthread thread, jint depth, jint slot, jdouble* value_p // doit_prologue(), but after doit() is finished with it. ResourceMark rm(current_thread); HandleMark hm(current_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -2334,7 +2335,7 @@ JvmtiEnv::SetLocalObject(jthread thread, jint depth, jint slot, jobject value) { // doit_prologue(), but after doit() is finished with it. ResourceMark rm(current_thread); HandleMark hm(current_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -2371,7 +2372,7 @@ JvmtiEnv::SetLocalInt(jthread thread, jint depth, jint slot, jint value) { // doit_prologue(), but after doit() is finished with it. ResourceMark rm(current_thread); HandleMark hm(current_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -2408,7 +2409,7 @@ JvmtiEnv::SetLocalLong(jthread thread, jint depth, jint slot, jlong value) { // doit_prologue(), but after doit() is finished with it. ResourceMark rm(current_thread); HandleMark hm(current_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -2445,7 +2446,7 @@ JvmtiEnv::SetLocalFloat(jthread thread, jint depth, jint slot, jfloat value) { // doit_prologue(), but after doit() is finished with it. ResourceMark rm(current_thread); HandleMark hm(current_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -2482,7 +2483,7 @@ JvmtiEnv::SetLocalDouble(jthread thread, jint depth, jint slot, jdouble value) { // doit_prologue(), but after doit() is finished with it. ResourceMark rm(current_thread); HandleMark hm(current_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -2574,7 +2575,7 @@ JvmtiEnv::ClearBreakpoint(Method* method, jlocation location) { jvmtiError JvmtiEnv::SetFieldAccessWatch(fieldDescriptor* fdesc_ptr) { - JvmtiVTMSTransitionDisabler disabler; + MountUnmountDisabler disabler; // make sure we haven't set this watch before if (fdesc_ptr->is_field_access_watched()) return JVMTI_ERROR_DUPLICATE; fdesc_ptr->set_is_field_access_watched(true); @@ -2587,7 +2588,7 @@ JvmtiEnv::SetFieldAccessWatch(fieldDescriptor* fdesc_ptr) { jvmtiError JvmtiEnv::ClearFieldAccessWatch(fieldDescriptor* fdesc_ptr) { - JvmtiVTMSTransitionDisabler disabler; + MountUnmountDisabler disabler; // make sure we have a watch to clear if (!fdesc_ptr->is_field_access_watched()) return JVMTI_ERROR_NOT_FOUND; fdesc_ptr->set_is_field_access_watched(false); @@ -2600,7 +2601,7 @@ JvmtiEnv::ClearFieldAccessWatch(fieldDescriptor* fdesc_ptr) { jvmtiError JvmtiEnv::SetFieldModificationWatch(fieldDescriptor* fdesc_ptr) { - JvmtiVTMSTransitionDisabler disabler; + MountUnmountDisabler disabler; // make sure we haven't set this watch before if (fdesc_ptr->is_field_modification_watched()) return JVMTI_ERROR_DUPLICATE; fdesc_ptr->set_is_field_modification_watched(true); @@ -2613,7 +2614,7 @@ JvmtiEnv::SetFieldModificationWatch(fieldDescriptor* fdesc_ptr) { jvmtiError JvmtiEnv::ClearFieldModificationWatch(fieldDescriptor* fdesc_ptr) { - JvmtiVTMSTransitionDisabler disabler; + MountUnmountDisabler disabler; // make sure we have a watch to clear if (!fdesc_ptr->is_field_modification_watched()) return JVMTI_ERROR_NOT_FOUND; fdesc_ptr->set_is_field_modification_watched(false); diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp index 2b17eddbe94..4894a4dd21a 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.cpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp @@ -51,6 +51,7 @@ #include "runtime/javaThread.inline.hpp" #include "runtime/jfieldIDWorkaround.hpp" #include "runtime/jniHandles.inline.hpp" +#include "runtime/mountUnmountDisabler.hpp" #include "runtime/objectMonitor.inline.hpp" #include "runtime/osThread.hpp" #include "runtime/signature.hpp" @@ -697,7 +698,7 @@ JvmtiEnvBase::check_and_skip_hidden_frames(bool is_in_VTMS_transition, javaVFram javaVFrame* JvmtiEnvBase::check_and_skip_hidden_frames(JavaThread* jt, javaVFrame* jvf) { - jvf = check_and_skip_hidden_frames(jt->is_in_VTMS_transition(), jvf); + jvf = check_and_skip_hidden_frames(jt->is_in_vthread_transition(), jvf); return jvf; } @@ -719,7 +720,7 @@ JvmtiEnvBase::get_vthread_jvf(oop vthread) { return nullptr; } vframeStream vfs(java_thread); - assert(!java_thread->is_in_VTMS_transition(), "invariant"); + assert(!java_thread->is_in_vthread_transition(), "invariant"); jvf = vfs.at_end() ? nullptr : vfs.asJavaVFrame(); jvf = check_and_skip_hidden_frames(false, jvf); } else { @@ -1693,8 +1694,7 @@ private: // jt->jvmti_vthread() for VTMS transition protocol. void correct_jvmti_thread_states() { for (JavaThread* jt : ThreadsListHandle()) { - if (jt->is_in_VTMS_transition()) { - jt->set_VTMS_transition_mark(true); + if (jt->is_in_vthread_transition()) { continue; // no need in JvmtiThreadState correction below if in transition } correct_jvmti_thread_state(jt); @@ -1711,7 +1711,7 @@ public: if (_enable) { correct_jvmti_thread_states(); } - JvmtiVTMSTransitionDisabler::set_VTMS_notify_jvmti_events(_enable); + MountUnmountDisabler::set_notify_jvmti_events(_enable); } }; @@ -1722,7 +1722,7 @@ JvmtiEnvBase::enable_virtual_threads_notify_jvmti() { if (!Continuations::enabled()) { return false; } - if (JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { + if (MountUnmountDisabler::notify_jvmti_events()) { return false; // already enabled } VM_SetNotifyJvmtiEventsMode op(true); @@ -1738,10 +1738,10 @@ JvmtiEnvBase::disable_virtual_threads_notify_jvmti() { if (!Continuations::enabled()) { return false; } - if (!JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { + if (!MountUnmountDisabler::notify_jvmti_events()) { return false; // already disabled } - JvmtiVTMSTransitionDisabler disabler(true); // ensure there are no other disablers + MountUnmountDisabler disabler(true); // ensure there are no other disablers VM_SetNotifyJvmtiEventsMode op(false); VMThread::execute(&op); return true; @@ -1769,7 +1769,6 @@ JvmtiEnvBase::suspend_thread(oop thread_oop, JavaThread* java_thread, bool singl // Platform thread or mounted vthread cases. assert(java_thread != nullptr, "sanity check"); - assert(!java_thread->is_in_VTMS_transition(), "sanity check"); // Don't allow hidden thread suspend request. if (java_thread->is_hidden_from_external_view()) { @@ -1828,7 +1827,6 @@ JvmtiEnvBase::resume_thread(oop thread_oop, JavaThread* java_thread, bool single // Platform thread or mounted vthread cases. assert(java_thread != nullptr, "sanity check"); - assert(!java_thread->is_in_VTMS_transition(), "sanity check"); // Don't allow hidden thread resume request. if (java_thread->is_hidden_from_external_view()) { @@ -2008,12 +2006,12 @@ class AdapterClosure : public HandshakeClosure { }; // Supports platform and virtual threads. -// JvmtiVTMSTransitionDisabler is always set by this function. +// MountUnmountDisabler is always set by this function. void JvmtiHandshake::execute(JvmtiUnitedHandshakeClosure* hs_cl, jthread target) { JavaThread* current = JavaThread::current(); HandleMark hm(current); - JvmtiVTMSTransitionDisabler disabler(target); + MountUnmountDisabler disabler(target); ThreadsListHandle tlh(current); JavaThread* java_thread = nullptr; oop thread_obj = nullptr; @@ -2030,7 +2028,7 @@ JvmtiHandshake::execute(JvmtiUnitedHandshakeClosure* hs_cl, jthread target) { // Supports platform and virtual threads. // A virtual thread is always identified by the target_h oop handle. // The target_jt is always nullptr for an unmounted virtual thread. -// JvmtiVTMSTransitionDisabler has to be set before call to this function. +// MountUnmountDisabler has to be set before call to this function. void JvmtiHandshake::execute(JvmtiUnitedHandshakeClosure* hs_cl, ThreadsListHandle* tlh, JavaThread* target_jt, Handle target_h) { @@ -2038,7 +2036,7 @@ JvmtiHandshake::execute(JvmtiUnitedHandshakeClosure* hs_cl, ThreadsListHandle* t bool is_virtual = java_lang_VirtualThread::is_instance(target_h()); bool self = target_jt == current; - assert(!Continuations::enabled() || self || !is_virtual || current->is_VTMS_transition_disabler(), "sanity check"); + assert(!Continuations::enabled() || self || !is_virtual || current->is_vthread_transition_disabler(), "sanity check"); hs_cl->set_target_jt(target_jt); // can be needed in the virtual thread case hs_cl->set_is_virtual(is_virtual); // can be needed in the virtual thread case @@ -2211,7 +2209,7 @@ JvmtiEnvBase::force_early_return(jthread thread, jvalue value, TosState tos) { JavaThread* current_thread = JavaThread::current(); HandleMark hm(current_thread); - JvmtiVTMSTransitionDisabler disabler(thread); + MountUnmountDisabler disabler(thread); ThreadsListHandle tlh(current_thread); JavaThread* java_thread = nullptr; @@ -2612,7 +2610,7 @@ PrintStackTraceClosure::do_thread_impl(Thread *target) { "is_VTMS_transition_disabler: %d, is_in_VTMS_transition = %d\n", tname, java_thread->name(), java_thread->is_exiting(), java_thread->is_suspended(), java_thread->is_carrier_thread_suspended(), is_vt_suspended, - java_thread->is_VTMS_transition_disabler(), java_thread->is_in_VTMS_transition()); + java_thread->is_vthread_transition_disabler(), java_thread->is_in_vthread_transition()); if (java_thread->has_last_Java_frame()) { RegisterMap reg_map(java_thread, diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index 14b7d886bce..02f39460ff6 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -61,6 +61,7 @@ #include "runtime/javaThread.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/keepStackGCProcessed.hpp" +#include "runtime/mountUnmountDisabler.hpp" #include "runtime/objectMonitor.inline.hpp" #include "runtime/os.hpp" #include "runtime/osThread.hpp" @@ -412,7 +413,7 @@ JvmtiExport::get_jvmti_interface(JavaVM *jvm, void **penv, jint version) { if (Continuations::enabled()) { // Virtual threads support for agents loaded into running VM. // There is a performance impact when VTMS transitions are enabled. - if (!JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { + if (!MountUnmountDisabler::notify_jvmti_events()) { JvmtiEnvBase::enable_virtual_threads_notify_jvmti(); } } @@ -426,7 +427,7 @@ JvmtiExport::get_jvmti_interface(JavaVM *jvm, void **penv, jint version) { if (Continuations::enabled()) { // Virtual threads support for agents loaded at startup. // There is a performance impact when VTMS transitions are enabled. - JvmtiVTMSTransitionDisabler::set_VTMS_notify_jvmti_events(true); + MountUnmountDisabler::set_notify_jvmti_events(true, true /*is_onload*/); } return JNI_OK; @@ -1639,7 +1640,7 @@ void JvmtiExport::post_vthread_end(jobject vthread) { JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventVirtualThreadEnd callback = env->callbacks()->VirtualThreadEnd; if (callback != nullptr) { - (*callback)(env->jvmti_external(), jem.jni_env(), vthread); + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread()); } } } @@ -2924,13 +2925,13 @@ void JvmtiExport::vthread_post_monitor_waited(JavaThread *current, ObjectMonitor Handle vthread(current, current->vthread()); // Finish the VTMS transition temporarily to post the event. - JvmtiVTMSTransitionDisabler::VTMS_vthread_mount((jthread)vthread.raw_value(), false); + MountUnmountDisabler::end_transition(current, vthread(), true /*is_mount*/, false /*is_thread_start*/); // Post event. JvmtiExport::post_monitor_waited(current, obj_mntr, timed_out); // Go back to VTMS transition state. - JvmtiVTMSTransitionDisabler::VTMS_vthread_unmount((jthread)vthread.raw_value(), true); + MountUnmountDisabler::start_transition(current, vthread(), false /*is_mount*/, false /*is_thread_start*/); } void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) { diff --git a/src/hotspot/share/prims/jvmtiExtensions.cpp b/src/hotspot/share/prims/jvmtiExtensions.cpp index 603d62eff85..855c7bd4eba 100644 --- a/src/hotspot/share/prims/jvmtiExtensions.cpp +++ b/src/hotspot/share/prims/jvmtiExtensions.cpp @@ -29,6 +29,7 @@ #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/jniHandles.inline.hpp" +#include "runtime/mountUnmountDisabler.hpp" // the list of extension functions GrowableArray* JvmtiExtensions::_ext_functions; @@ -77,7 +78,7 @@ static jvmtiError JNICALL GetVirtualThread(const jvmtiEnv* env, ...) { va_end(ap); ThreadInVMfromNative tiv(current_thread); - JvmtiVTMSTransitionDisabler disabler; + MountUnmountDisabler disabler; ThreadsListHandle tlh(current_thread); jvmtiError err; @@ -135,7 +136,7 @@ static jvmtiError JNICALL GetCarrierThread(const jvmtiEnv* env, ...) { MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current_thread)); ThreadInVMfromNative tiv(current_thread); - JvmtiVTMSTransitionDisabler disabler; + MountUnmountDisabler disabler; ThreadsListHandle tlh(current_thread); JavaThread* java_thread; diff --git a/src/hotspot/share/prims/jvmtiTagMap.cpp b/src/hotspot/share/prims/jvmtiTagMap.cpp index c923f91f69d..90a3461f321 100644 --- a/src/hotspot/share/prims/jvmtiTagMap.cpp +++ b/src/hotspot/share/prims/jvmtiTagMap.cpp @@ -57,6 +57,7 @@ #include "runtime/javaCalls.hpp" #include "runtime/javaThread.inline.hpp" #include "runtime/jniHandles.inline.hpp" +#include "runtime/mountUnmountDisabler.hpp" #include "runtime/mutex.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" @@ -3028,7 +3029,7 @@ void JvmtiTagMap::iterate_over_reachable_objects(jvmtiHeapRootCallback heap_root jvmtiObjectReferenceCallback object_ref_callback, const void* user_data) { // VTMS transitions must be disabled before the EscapeBarrier. - JvmtiVTMSTransitionDisabler disabler; + MountUnmountDisabler disabler; JavaThread* jt = JavaThread::current(); EscapeBarrier eb(true, jt); @@ -3056,7 +3057,7 @@ void JvmtiTagMap::iterate_over_objects_reachable_from_object(jobject object, Arena dead_object_arena(mtServiceability); GrowableArray dead_objects(&dead_object_arena, 10, 0, 0); - JvmtiVTMSTransitionDisabler disabler; + MountUnmountDisabler disabler; { MutexLocker ml(Heap_lock); @@ -3076,7 +3077,7 @@ void JvmtiTagMap::follow_references(jint heap_filter, const void* user_data) { // VTMS transitions must be disabled before the EscapeBarrier. - JvmtiVTMSTransitionDisabler disabler; + MountUnmountDisabler disabler; oop obj = JNIHandles::resolve(object); JavaThread* jt = JavaThread::current(); diff --git a/src/hotspot/share/prims/jvmtiThreadState.cpp b/src/hotspot/share/prims/jvmtiThreadState.cpp index 00a48dec111..fc965e568f7 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiThreadState.cpp @@ -209,479 +209,6 @@ JvmtiThreadState::periodic_clean_up() { } } -// -// Virtual Threads Mount State transition (VTMS transition) mechanism -// - -// 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 -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; - -// The JvmtiVTMSTransitionDisabler sync protocol is enabled if this count > 0. -volatile int JvmtiVTMSTransitionDisabler::_sync_protocol_enabled_count = 0; - -// JvmtiVTMSTraansitionDisabler sync protocol is enabled permanently after seeing a suspender. -volatile bool JvmtiVTMSTransitionDisabler::_sync_protocol_enabled_permanently = false; - -#ifdef ASSERT -void -JvmtiVTMSTransitionDisabler::print_info() { - log_error(jvmti)("_VTMS_transition_disable_for_one_count: %d\n", _VTMS_transition_disable_for_one_count); - log_error(jvmti)("_VTMS_transition_disable_for_all_count: %d\n\n", _VTMS_transition_disable_for_all_count); - int attempts = 10000; - for (JavaThreadIteratorWithHandle jtiwh; JavaThread *java_thread = jtiwh.next(); ) { - if (java_thread->VTMS_transition_mark()) { - log_error(jvmti)("jt: %p VTMS_transition_mark: %d\n", - (void*)java_thread, java_thread->VTMS_transition_mark()); - } - ResourceMark rm; - // Handshake with target. - PrintStackTraceClosure pstc; - Handshake::execute(&pstc, java_thread); - } -} -#endif - -// disable VTMS transitions for one virtual thread -// disable VTMS transitions for all threads if thread is nullptr or a platform thread -JvmtiVTMSTransitionDisabler::JvmtiVTMSTransitionDisabler(jthread thread) - : _is_SR(false), - _is_virtual(false), - _is_self(false), - _thread(thread) -{ - if (!Continuations::enabled()) { - return; // JvmtiVTMSTransitionDisabler is no-op without virtual threads - } - if (Thread::current_or_null() == nullptr) { - return; // Detached thread, can be a call from Agent_OnLoad. - } - JavaThread* current = JavaThread::current(); - oop thread_oop = JNIHandles::resolve_external_guard(thread); - _is_virtual = java_lang_VirtualThread::is_instance(thread_oop); - - if (thread == nullptr || - (!_is_virtual && thread_oop == current->threadObj()) || - (_is_virtual && thread_oop == current->vthread())) { - _is_self = true; - return; // no need for current thread to disable and enable transitions for itself - } - if (!sync_protocol_enabled_permanently()) { - JvmtiVTMSTransitionDisabler::inc_sync_protocol_enabled_count(); - } - - // Target can be virtual or platform thread. - // If target is a platform thread then we have to disable VTMS transitions for all threads. - // It is by several reasons: - // - carrier threads can mount virtual threads which may cause incorrect behavior - // - there is no mechanism to disable transitions for a specific carrier thread yet - if (_is_virtual) { - VTMS_transition_disable_for_one(); // disable VTMS transitions for one virtual thread - } else { - VTMS_transition_disable_for_all(); // disable VTMS transitions for all virtual threads - } -} - -// disable VTMS transitions for all virtual threads -JvmtiVTMSTransitionDisabler::JvmtiVTMSTransitionDisabler(bool is_SR) - : _is_SR(is_SR), - _is_virtual(false), - _is_self(false), - _thread(nullptr) -{ - if (!Continuations::enabled()) { - return; // JvmtiVTMSTransitionDisabler is no-op without virtual threads - } - if (Thread::current_or_null() == nullptr) { - return; // Detached thread, can be a call from Agent_OnLoad. - } - if (!sync_protocol_enabled_permanently()) { - JvmtiVTMSTransitionDisabler::inc_sync_protocol_enabled_count(); - if (is_SR) { - AtomicAccess::store(&_sync_protocol_enabled_permanently, true); - } - } - VTMS_transition_disable_for_all(); -} - -JvmtiVTMSTransitionDisabler::~JvmtiVTMSTransitionDisabler() { - if (!Continuations::enabled()) { - return; // JvmtiVTMSTransitionDisabler is a no-op without virtual threads - } - if (Thread::current_or_null() == nullptr) { - return; // Detached thread, can be a call from Agent_OnLoad. - } - if (_is_self) { - return; // no need for current thread to disable and enable transitions for itself - } - if (_is_virtual) { - VTMS_transition_enable_for_one(); // enable VTMS transitions for one virtual thread - } else { - VTMS_transition_enable_for_all(); // enable VTMS transitions for all virtual threads - } - if (!sync_protocol_enabled_permanently()) { - JvmtiVTMSTransitionDisabler::dec_sync_protocol_enabled_count(); - } -} - -// disable VTMS transitions for one virtual thread -void -JvmtiVTMSTransitionDisabler::VTMS_transition_disable_for_one() { - assert(_thread != nullptr, "sanity check"); - JavaThread* thread = JavaThread::current(); - HandleMark hm(thread); - Handle vth = Handle(thread, JNIHandles::resolve_external_guard(_thread)); - assert(java_lang_VirtualThread::is_instance(vth()), "sanity check"); - - MonitorLocker ml(JvmtiVTMSTransition_lock); - - while (_SR_mode) { // suspender or resumer is a JvmtiVTMSTransitionDisabler monopolist - ml.wait(10); // wait while there is an active suspender or resumer - } - AtomicAccess::inc(&_VTMS_transition_disable_for_one_count); - java_lang_Thread::inc_VTMS_transition_disable_count(vth()); - - while (java_lang_Thread::is_in_VTMS_transition(vth())) { - ml.wait(10); // wait while the virtual thread is in transition - } -#ifdef ASSERT - thread->set_is_VTMS_transition_disabler(true); -#endif -} - -// disable VTMS transitions for all virtual threads -void -JvmtiVTMSTransitionDisabler::VTMS_transition_disable_for_all() { - JavaThread* thread = JavaThread::current(); - int attempts = 50000; - { - MonitorLocker ml(JvmtiVTMSTransition_lock); - - assert(!thread->is_in_VTMS_transition(), "VTMS_transition sanity check"); - while (_SR_mode) { // Suspender or resumer is a JvmtiVTMSTransitionDisabler monopolist. - ml.wait(10); // Wait while there is an active suspender or resumer. - } - if (_is_SR) { - _SR_mode = true; - while (_VTMS_transition_disable_for_all_count > 0 || - _VTMS_transition_disable_for_one_count > 0) { - ml.wait(10); // Wait while there is any active jvmtiVTMSTransitionDisabler. - } - } - AtomicAccess::inc(&_VTMS_transition_disable_for_all_count); - - // Block while some mount/unmount transitions are in progress. - // Debug version fails and prints diagnostic information. - for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { - while (jt->VTMS_transition_mark()) { - if (ml.wait(10)) { - attempts--; - } - DEBUG_ONLY(if (attempts == 0) break;) - } - } - assert(!thread->is_VTMS_transition_disabler(), "VTMS_transition sanity check"); -#ifdef ASSERT - if (attempts > 0) { - thread->set_is_VTMS_transition_disabler(true); - } -#endif - } -#ifdef ASSERT - if (attempts == 0) { - print_info(); - fatal("stuck in JvmtiVTMSTransitionDisabler::VTMS_transition_disable"); - } -#endif -} - -// enable VTMS transitions for one virtual thread -void -JvmtiVTMSTransitionDisabler::VTMS_transition_enable_for_one() { - JavaThread* thread = JavaThread::current(); - HandleMark hm(thread); - Handle vth = Handle(thread, JNIHandles::resolve_external_guard(_thread)); - if (!java_lang_VirtualThread::is_instance(vth())) { - return; // no-op if _thread is not a virtual thread - } - MonitorLocker ml(JvmtiVTMSTransition_lock); - java_lang_Thread::dec_VTMS_transition_disable_count(vth()); - AtomicAccess::dec(&_VTMS_transition_disable_for_one_count); - if (_VTMS_transition_disable_for_one_count == 0) { - ml.notify_all(); - } -#ifdef ASSERT - thread->set_is_VTMS_transition_disabler(false); -#endif -} - -// enable VTMS transitions for all virtual threads -void -JvmtiVTMSTransitionDisabler::VTMS_transition_enable_for_all() { - JavaThread* current = JavaThread::current(); - { - MonitorLocker ml(JvmtiVTMSTransition_lock); - assert(_VTMS_transition_disable_for_all_count > 0, "VTMS_transition sanity check"); - - if (_is_SR) { // Disabler is suspender or resumer. - _SR_mode = false; - } - AtomicAccess::dec(&_VTMS_transition_disable_for_all_count); - if (_VTMS_transition_disable_for_all_count == 0 || _is_SR) { - ml.notify_all(); - } -#ifdef ASSERT - current->set_is_VTMS_transition_disabler(false); -#endif - } -} - -void -JvmtiVTMSTransitionDisabler::start_VTMS_transition(jthread vthread, bool is_mount) { - JavaThread* thread = JavaThread::current(); - oop vt = JNIHandles::resolve_external_guard(vthread); - assert(!thread->is_in_VTMS_transition(), "VTMS_transition sanity check"); - - // Avoid using MonitorLocker on performance critical path, use - // two-level synchronization with lock-free operations on state bits. - assert(!thread->VTMS_transition_mark(), "sanity check"); - thread->set_VTMS_transition_mark(true); // Try to enter VTMS transition section optmistically. - java_lang_Thread::set_is_in_VTMS_transition(vt, true); - - if (!sync_protocol_enabled()) { - thread->set_is_in_VTMS_transition(true); - return; - } - HandleMark hm(thread); - Handle vth = Handle(thread, vt); - int attempts = 50000; - - // Do not allow suspends inside VTMS transitions. - // Block while transitions are disabled or there are suspend requests. - int64_t thread_id = java_lang_Thread::thread_id(vth()); // Cannot use oops while blocked. - - if (_VTMS_transition_disable_for_all_count > 0 || - java_lang_Thread::VTMS_transition_disable_count(vth()) > 0 || - thread->is_suspended() || - JvmtiVTSuspender::is_vthread_suspended(thread_id) - ) { - // Slow path: undo unsuccessful optimistic set of the VTMS_transition_mark. - // It can cause an extra waiting cycle for VTMS transition disablers. - thread->set_VTMS_transition_mark(false); - java_lang_Thread::set_is_in_VTMS_transition(vth(), false); - - while (true) { - MonitorLocker ml(JvmtiVTMSTransition_lock); - - // Do not allow suspends inside VTMS transitions. - // Block while transitions are disabled or there are suspend requests. - if (_VTMS_transition_disable_for_all_count > 0 || - java_lang_Thread::VTMS_transition_disable_count(vth()) > 0 || - thread->is_suspended() || - JvmtiVTSuspender::is_vthread_suspended(thread_id) - ) { - // Block while transitions are disabled or there are suspend requests. - if (ml.wait(200)) { - attempts--; - } - DEBUG_ONLY(if (attempts == 0) break;) - continue; // ~ThreadBlockInVM has handshake-based suspend point. - } - thread->set_VTMS_transition_mark(true); - java_lang_Thread::set_is_in_VTMS_transition(vth(), true); - break; - } - } -#ifdef ASSERT - if (attempts == 0) { - log_error(jvmti)("start_VTMS_transition: thread->is_suspended: %d is_vthread_suspended: %d\n\n", - thread->is_suspended(), JvmtiVTSuspender::is_vthread_suspended(thread_id)); - print_info(); - fatal("stuck in JvmtiVTMSTransitionDisabler::start_VTMS_transition"); - } -#endif - // Enter VTMS transition section. - thread->set_is_in_VTMS_transition(true); -} - -void -JvmtiVTMSTransitionDisabler::finish_VTMS_transition(jthread vthread, bool is_mount) { - JavaThread* thread = JavaThread::current(); - - assert(thread->is_in_VTMS_transition(), "sanity check"); - thread->set_is_in_VTMS_transition(false); - oop vt = JNIHandles::resolve_external_guard(vthread); - java_lang_Thread::set_is_in_VTMS_transition(vt, false); - assert(thread->VTMS_transition_mark(), "sanity check"); - thread->set_VTMS_transition_mark(false); - - if (!sync_protocol_enabled()) { - return; - } - int64_t thread_id = java_lang_Thread::thread_id(vt); - - // Unblock waiting VTMS transition disablers. - if (_VTMS_transition_disable_for_one_count > 0 || - _VTMS_transition_disable_for_all_count > 0) { - MonitorLocker ml(JvmtiVTMSTransition_lock); - ml.notify_all(); - } - // In unmount case the carrier thread is attached after unmount transition. - // Check and block it if there was external suspend request. - int attempts = 10000; - if (!is_mount && thread->is_carrier_thread_suspended()) { - while (true) { - MonitorLocker ml(JvmtiVTMSTransition_lock); - - // Block while there are suspend requests. - if ((!is_mount && thread->is_carrier_thread_suspended()) || - (is_mount && JvmtiVTSuspender::is_vthread_suspended(thread_id)) - ) { - // Block while there are suspend requests. - if (ml.wait(200)) { - attempts--; - } - DEBUG_ONLY(if (attempts == 0) break;) - continue; - } - break; - } - } -#ifdef ASSERT - if (attempts == 0) { - log_error(jvmti)("finish_VTMS_transition: thread->is_suspended: %d is_vthread_suspended: %d\n\n", - thread->is_suspended(), JvmtiVTSuspender::is_vthread_suspended(thread_id)); - print_info(); - fatal("stuck in JvmtiVTMSTransitionDisabler::finish_VTMS_transition"); - } -#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_vthread_start(jobject vthread) { - VTMS_mount_end(vthread); - JavaThread* thread = JavaThread::current(); - - assert(!thread->is_in_VTMS_transition(), "sanity check"); - - // If interp_only_mode has been enabled then we must eagerly create JvmtiThreadState - // objects for globally enabled virtual thread filtered events. Otherwise, - // it is an important optimization to create JvmtiThreadState objects lazily. - // This optimization is disabled when watchpoint capabilities are present. It is to - // work around a bug with virtual thread frames which can be not deoptimized in time. - if (JvmtiThreadState::seen_interp_only_mode() || - JvmtiExport::should_post_field_access() || - JvmtiExport::should_post_field_modification()){ - JvmtiEventController::thread_started(thread); - } - if (JvmtiExport::should_post_vthread_start()) { - JvmtiExport::post_vthread_start(vthread); - } - // 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"); - - // post VirtualThreadUnmount event before VirtualThreadEnd - if (JvmtiExport::should_post_vthread_unmount()) { - JvmtiExport::post_vthread_unmount(vthread); - } - if (JvmtiExport::should_post_vthread_end()) { - JvmtiExport::post_vthread_end(vthread); - } - VTMS_unmount_begin(vthread, /* last_unmount */ true); - if (thread->jvmti_thread_state() != nullptr) { - JvmtiExport::cleanup_thread(thread); - assert(thread->jvmti_thread_state() == nullptr, "should be null"); - assert(java_lang_Thread::jvmti_thread_state(JNIHandles::resolve(vthread)) == nullptr, "should be null"); - } - thread->rebind_to_jvmti_thread_state_of(thread->threadObj()); -} - -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, /* last_unmount */ false); - } else { - VTMS_unmount_end(vthread); - } -} - -void -JvmtiVTMSTransitionDisabler::VTMS_mount_begin(jobject vthread) { - JavaThread* thread = JavaThread::current(); - assert(!thread->is_in_VTMS_transition(), "sanity check"); - start_VTMS_transition(vthread, /* is_mount */ true); -} - -void -JvmtiVTMSTransitionDisabler::VTMS_mount_end(jobject vthread) { - JavaThread* thread = JavaThread::current(); - oop vt = JNIHandles::resolve(vthread); - - thread->rebind_to_jvmti_thread_state_of(vt); - - assert(thread->is_in_VTMS_transition(), "sanity check"); - finish_VTMS_transition(vthread, /* is_mount */ true); -} - -void -JvmtiVTMSTransitionDisabler::VTMS_unmount_begin(jobject vthread, bool last_unmount) { - JavaThread* thread = JavaThread::current(); - - assert(!thread->is_in_VTMS_transition(), "sanity check"); - - start_VTMS_transition(vthread, /* is_mount */ false); - if (!last_unmount) { - thread->rebind_to_jvmti_thread_state_of(thread->threadObj()); - } -} - -void -JvmtiVTMSTransitionDisabler::VTMS_unmount_end(jobject vthread) { - JavaThread* thread = JavaThread::current(); - assert(thread->is_in_VTMS_transition(), "sanity check"); - finish_VTMS_transition(vthread, /* is_mount */ false); -} - - // // Virtual Threads Suspend/Resume management // diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp index 17bdae4662e..43b568cf1fc 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.hpp @@ -72,67 +72,6 @@ class JvmtiEnvThreadStateIterator : public StackObj { JvmtiEnvThreadState* next(JvmtiEnvThreadState* ets); }; -/////////////////////////////////////////////////////////////// -// -// class JvmtiVTMSTransitionDisabler -// -// Virtual Thread Mount State Transition (VTMS transition) mechanism -// -class JvmtiVTMSTransitionDisabler : public AnyObj { - private: - static volatile int _VTMS_transition_disable_for_one_count; // transitions for one virtual thread are disabled while it is positive - static volatile int _VTMS_transition_disable_for_all_count; // transitions for all virtual threads are disabled while it is positive - static volatile bool _SR_mode; // there is an active suspender or resumer - static volatile int _sync_protocol_enabled_count; // current number of JvmtiVTMSTransitionDisablers enabled sync protocol - static volatile bool _sync_protocol_enabled_permanently; // seen a suspender: JvmtiVTMSTransitionDisabler protocol is enabled permanently - - bool _is_SR; // is suspender or resumer - bool _is_virtual; // target thread is virtual - bool _is_self; // JvmtiVTMSTransitionDisabler is a no-op for current platform, carrier or virtual thread - jthread _thread; // virtual thread to disable transitions for, no-op if it is a platform thread - - DEBUG_ONLY(static void print_info();) - void VTMS_transition_disable_for_one(); - void VTMS_transition_disable_for_all(); - void VTMS_transition_enable_for_one(); - 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; } - - static void inc_sync_protocol_enabled_count() { AtomicAccess::inc(&_sync_protocol_enabled_count); } - static void dec_sync_protocol_enabled_count() { AtomicAccess::dec(&_sync_protocol_enabled_count); } - static int sync_protocol_enabled_count() { return AtomicAccess::load(&_sync_protocol_enabled_count); } - static bool sync_protocol_enabled_permanently() { return AtomicAccess::load(&_sync_protocol_enabled_permanently); } - - static bool sync_protocol_enabled() { return sync_protocol_enabled_permanently() || sync_protocol_enabled_count() > 0; } - - // parameter is_SR: suspender or resumer - JvmtiVTMSTransitionDisabler(bool is_SR = false); - 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_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, bool last_unmount); - static void VTMS_unmount_end(jobject vthread); -}; - /////////////////////////////////////////////////////////////// // // class VirtualThreadList diff --git a/src/hotspot/share/runtime/continuation.cpp b/src/hotspot/share/runtime/continuation.cpp index 8f1cbe39640..935f304a751 100644 --- a/src/hotspot/share/runtime/continuation.cpp +++ b/src/hotspot/share/runtime/continuation.cpp @@ -35,6 +35,7 @@ #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.inline.hpp" #include "runtime/jniHandles.inline.hpp" +#include "runtime/mountUnmountDisabler.hpp" #include "runtime/osThread.hpp" #include "runtime/vframe.inline.hpp" #include "runtime/vframe_hp.hpp" @@ -56,66 +57,50 @@ JVM_ENTRY(void, CONT_unpin(JNIEnv* env, jclass cls)) { } JVM_END -#if INCLUDE_JVMTI -class JvmtiUnmountBeginMark : public StackObj { +class UnmountBeginMark : public StackObj { Handle _vthread; JavaThread* _current; freeze_result _result; bool _failed; public: - JvmtiUnmountBeginMark(JavaThread* t) : + UnmountBeginMark(JavaThread* t) : _vthread(t, t->vthread()), _current(t), _result(freeze_pinned_native), _failed(false) { - assert(!_current->is_in_VTMS_transition(), "must be"); + assert(!_current->is_in_vthread_transition(), "must be"); - if (JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { - JvmtiVTMSTransitionDisabler::VTMS_vthread_unmount((jthread)_vthread.raw_value(), true); + MountUnmountDisabler::start_transition(_current, _vthread(), false /*is_mount*/, false /*is_thread_start*/); - // Don't preempt if there is a pending popframe or earlyret operation. This can - // be installed in start_VTMS_transition() so we need to check it here. - if (JvmtiExport::can_pop_frame() || JvmtiExport::can_force_early_return()) { - JvmtiThreadState* state = _current->jvmti_thread_state(); - if (_current->has_pending_popframe() || (state != nullptr && state->is_earlyret_pending())) { - _failed = true; - } - } - - // Don't preempt in case there is an async exception installed since - // we would incorrectly throw it during the unmount logic in the carrier. - if (_current->has_async_exception_condition()) { + // Don't preempt if there is a pending popframe or earlyret operation. This can + // be installed in in process_at_transition_start() so we need to check it here. + if (JvmtiExport::can_pop_frame() || JvmtiExport::can_force_early_return()) { + JvmtiThreadState* state = _current->jvmti_thread_state(); + if (_current->has_pending_popframe() || (state != nullptr && state->is_earlyret_pending())) { _failed = true; } - } else { - _current->set_is_in_VTMS_transition(true); - java_lang_Thread::set_is_in_VTMS_transition(_vthread(), true); + } + + // Don't preempt in case there is an async exception installed since + // we would incorrectly throw it during the unmount logic in the carrier. + if (_current->has_async_exception_condition()) { + _failed = true; } } - ~JvmtiUnmountBeginMark() { + ~UnmountBeginMark() { assert(!_current->is_suspended(), "must be"); - - assert(_current->is_in_VTMS_transition(), "must be"); - assert(java_lang_Thread::is_in_VTMS_transition(_vthread()), "must be"); - - // Read it again since for late binding agents the flag could have - // been set while blocked in the allocation path during freeze. - bool jvmti_present = JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events(); + assert(_current->is_in_vthread_transition(), "must be"); if (_result != freeze_ok) { // Undo transition - if (jvmti_present) { - JvmtiVTMSTransitionDisabler::VTMS_vthread_mount((jthread)_vthread.raw_value(), false); - } else { - _current->set_is_in_VTMS_transition(false); - java_lang_Thread::set_is_in_VTMS_transition(_vthread(), false); - } + MountUnmountDisabler::end_transition(_current, _vthread(), true /*is_mount*/, false /*is_thread_start*/); } } void set_result(freeze_result res) { _result = res; } bool failed() { return _failed; } }; +#if INCLUDE_JVMTI static bool is_vthread_safe_to_preempt_for_jvmti(JavaThread* current) { - if (current->is_in_VTMS_transition()) { + if (current->is_in_vthread_transition()) { // We are at the end of a mount transition. return false; } @@ -150,11 +135,11 @@ freeze_result Continuation::try_preempt(JavaThread* current, oop continuation) { return freeze_pinned_native; } - JVMTI_ONLY(JvmtiUnmountBeginMark jubm(current);) - JVMTI_ONLY(if (jubm.failed()) return freeze_pinned_native;) + UnmountBeginMark ubm(current); + if (ubm.failed()) return freeze_pinned_native; freeze_result res = CAST_TO_FN_PTR(FreezeContFnT, freeze_preempt_entry())(current, current->last_Java_sp()); log_trace(continuations, preempt)("try_preempt: %d", res); - JVMTI_ONLY(jubm.set_result(res);) + ubm.set_result(res); if (current->has_pending_exception()) { assert(res == freeze_exception, "expecting an exception result from freeze"); diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index 2a31e5fb5b2..ccbe78817d6 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -58,6 +58,7 @@ #include "runtime/javaThread.inline.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/keepStackGCProcessed.hpp" +#include "runtime/mountUnmountDisabler.hpp" #include "runtime/objectMonitor.inline.hpp" #include "runtime/orderAccess.hpp" #include "runtime/prefetch.inline.hpp" @@ -1690,7 +1691,7 @@ static void jvmti_mount_end(JavaThread* current, ContinuationWrapper& cont, fram AnchorMark am(current, top); // Set anchor so that the stack is walkable. JRT_BLOCK - JvmtiVTMSTransitionDisabler::VTMS_vthread_mount((jthread)vth.raw_value(), false); + MountUnmountDisabler::end_transition(current, vth(), true /*is_mount*/, false /*is_thread_start*/); if (current->pending_contended_entered_event()) { // No monitor JVMTI events for ObjectLocker case. @@ -2629,19 +2630,21 @@ intptr_t* ThawBase::handle_preempted_continuation(intptr_t* sp, Continuation::pr DEBUG_ONLY(verify_frame_kind(top, preempt_kind);) NOT_PRODUCT(int64_t tid = _thread->monitor_owner_id();) -#if INCLUDE_JVMTI // Finish the VTMS transition. - assert(_thread->is_in_VTMS_transition(), "must be"); + assert(_thread->is_in_vthread_transition(), "must be"); bool is_vthread = Continuation::continuation_scope(_cont.continuation()) == java_lang_VirtualThread::vthread_scope(); if (is_vthread) { - if (JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { +#if INCLUDE_JVMTI + if (MountUnmountDisabler::notify_jvmti_events()) { jvmti_mount_end(_thread, _cont, top, preempt_kind); - } else { - _thread->set_is_in_VTMS_transition(false); - java_lang_Thread::set_is_in_VTMS_transition(_thread->vthread(), false); + } else +#endif + { // Faster version of MountUnmountDisabler::end_transition() to avoid + // unnecessary extra instructions from jvmti_mount_end(). + java_lang_Thread::set_is_in_vthread_transition(_thread->vthread(), false); + _thread->set_is_in_vthread_transition(false); } } -#endif if (fast_case) { // If we thawed in the slow path the runtime stub/native wrapper frame already diff --git a/src/hotspot/share/runtime/handshake.cpp b/src/hotspot/share/runtime/handshake.cpp index f468f27e2c0..89b02717a7a 100644 --- a/src/hotspot/share/runtime/handshake.cpp +++ b/src/hotspot/share/runtime/handshake.cpp @@ -34,6 +34,7 @@ #include "runtime/handshake.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.inline.hpp" +#include "runtime/mountUnmountDisabler.hpp" #include "runtime/os.hpp" #include "runtime/osThread.hpp" #include "runtime/stackWatermarkSet.hpp" @@ -361,6 +362,27 @@ void Handshake::execute(HandshakeClosure* hs_cl) { VMThread::execute(&handshake); } +void Handshake::execute(HandshakeClosure* hs_cl, oop vthread) { + assert(java_lang_VirtualThread::is_instance(vthread), ""); + Handle vth(JavaThread::current(), vthread); + + MountUnmountDisabler md(vthread); + oop carrier_thread = java_lang_VirtualThread::carrier_thread(vth()); + if (carrier_thread != nullptr) { + JavaThread* target = java_lang_Thread::thread(carrier_thread); + assert(target != nullptr, ""); + // Technically there is no need for a ThreadsListHandle since the target + // will block if it tries to unmount the vthread, so it can never exit. + ThreadsListHandle tlh(JavaThread::current()); + assert(tlh.includes(target), ""); + execute(hs_cl, &tlh, target); + assert(target->threadObj() == java_lang_VirtualThread::carrier_thread(vth()), ""); + } else { + // unmounted vthread, execute closure with the current thread + hs_cl->do_thread(nullptr); + } +} + void Handshake::execute(HandshakeClosure* hs_cl, JavaThread* target) { // tlh == nullptr means we rely on a ThreadsListHandle somewhere // in the caller's context (and we sanity check for that). diff --git a/src/hotspot/share/runtime/handshake.hpp b/src/hotspot/share/runtime/handshake.hpp index 1304dca12b7..c764bbcfcd2 100644 --- a/src/hotspot/share/runtime/handshake.hpp +++ b/src/hotspot/share/runtime/handshake.hpp @@ -69,6 +69,7 @@ class Handshake : public AllStatic { // This version of execute() relies on a ThreadListHandle somewhere in // the caller's context to protect target (and we sanity check for that). static void execute(HandshakeClosure* hs_cl, JavaThread* target); + static void execute(HandshakeClosure* hs_cl, oop vthread); // This version of execute() is used when you have a ThreadListHandle in // hand and are using it to protect target. If tlh == nullptr, then we // sanity check for a ThreadListHandle somewhere in the caller's context diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index a81a687a9c0..1bf4841e193 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -448,16 +448,11 @@ JavaThread::JavaThread(MemTag mem_tag) : _do_not_unlock_if_synchronized(false), #if INCLUDE_JVMTI _carrier_thread_suspended(false), - _is_in_VTMS_transition(false), _is_disable_suspend(false), _is_in_java_upcall(false), _jvmti_events_disabled(0), - _VTMS_transition_mark(false), _on_monitor_waited_event(false), _contended_entered_monitor(nullptr), -#ifdef ASSERT - _is_VTMS_transition_disabler(false), -#endif #endif _jni_attach_state(_not_attaching_via_jni), _is_in_internal_oome_mark(false), @@ -503,6 +498,10 @@ JavaThread::JavaThread(MemTag mem_tag) : _handshake(this), _suspend_resume_manager(this, &_handshake._lock), + _is_in_vthread_transition(false), + DEBUG_ONLY(_is_vthread_transition_disabler(false) COMMA) + DEBUG_ONLY(_is_disabler_at_start(false) COMMA) + _popframe_preserved_args(nullptr), _popframe_preserved_args_size(0), @@ -1150,17 +1149,26 @@ void JavaThread::send_async_exception(JavaThread* target, oop java_throwable) { Handshake::execute(&iaeh, target); } -#if INCLUDE_JVMTI -void JavaThread::set_is_in_VTMS_transition(bool val) { - assert(is_in_VTMS_transition() != val, "already %s transition", val ? "inside" : "outside"); - _is_in_VTMS_transition = val; +bool JavaThread::is_in_vthread_transition() const { + DEBUG_ONLY(Thread* current = Thread::current();) + assert(is_handshake_safe_for(current) || SafepointSynchronize::is_at_safepoint() + || JavaThread::cast(current)->is_disabler_at_start(), "not safe"); + return AtomicAccess::load(&_is_in_vthread_transition); +} + +void JavaThread::set_is_in_vthread_transition(bool val) { + assert(is_in_vthread_transition() != val, "already %s transition", val ? "inside" : "outside"); + AtomicAccess::store(&_is_in_vthread_transition, val); } #ifdef ASSERT -void JavaThread::set_is_VTMS_transition_disabler(bool val) { - _is_VTMS_transition_disabler = val; +void JavaThread::set_is_vthread_transition_disabler(bool val) { + _is_vthread_transition_disabler = val; +} + +void JavaThread::set_is_disabler_at_start(bool val) { + _is_disabler_at_start = val; } -#endif #endif // External suspension mechanism. @@ -1170,11 +1178,8 @@ void JavaThread::set_is_VTMS_transition_disabler(bool val) { // - Target thread will not enter any new monitors. // bool JavaThread::java_suspend(bool register_vthread_SR) { -#if INCLUDE_JVMTI - // Suspending a JavaThread in VTMS transition or disabling VTMS transitions can cause deadlocks. - assert(!is_in_VTMS_transition(), "no suspend allowed in VTMS transition"); - assert(!is_VTMS_transition_disabler(), "no suspend allowed for VTMS transition disablers"); -#endif + // Suspending a vthread transition disabler can cause deadlocks. + assert(!is_vthread_transition_disabler(), "no suspend allowed for vthread transition disablers"); guarantee(Thread::is_JavaThread_protected(/* target */ this), "target JavaThread is not protected in calling context."); diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index ba2aa42132f..d4c12887e10 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -322,16 +322,11 @@ class JavaThread: public Thread { // never locked) when throwing an exception. Used by interpreter only. #if INCLUDE_JVMTI volatile bool _carrier_thread_suspended; // Carrier thread is externally suspended - bool _is_in_VTMS_transition; // thread is in virtual thread mount state transition bool _is_disable_suspend; // JVMTI suspend is temporarily disabled; used on current thread only bool _is_in_java_upcall; // JVMTI is doing a Java upcall, so JVMTI events must be hidden int _jvmti_events_disabled; // JVMTI events disabled manually - bool _VTMS_transition_mark; // used for sync between VTMS transitions and disablers bool _on_monitor_waited_event; // Avoid callee arg processing for enterSpecial when posting waited event ObjectMonitor* _contended_entered_monitor; // Monitor for pending monitor_contended_entered callback -#ifdef ASSERT - bool _is_VTMS_transition_disabler; // thread currently disabled VTMS transitions -#endif #endif // JNI attach states: @@ -737,6 +732,20 @@ public: // current thread, i.e. reverts optimizations based on escape analysis. void wait_for_object_deoptimization(); +private: + bool _is_in_vthread_transition; // thread is in virtual thread mount state transition + DEBUG_ONLY(bool _is_vthread_transition_disabler;) // thread currently disabled vthread transitions + DEBUG_ONLY(bool _is_disabler_at_start;) // thread at process of disabling vthread transitions +public: + bool is_in_vthread_transition() const; + void set_is_in_vthread_transition(bool val); +#ifdef ASSERT + bool is_vthread_transition_disabler() const { return _is_vthread_transition_disabler; } + void set_is_vthread_transition_disabler(bool val); + bool is_disabler_at_start() const { return _is_disabler_at_start; } + void set_is_disabler_at_start(bool val); +#endif + #if INCLUDE_JVMTI inline bool set_carrier_thread_suspended(); inline bool clear_carrier_thread_suspended(); @@ -745,9 +754,6 @@ public: return AtomicAccess::load(&_carrier_thread_suspended); } - bool is_in_VTMS_transition() const { return _is_in_VTMS_transition; } - void set_is_in_VTMS_transition(bool val); - bool is_disable_suspend() const { return _is_disable_suspend; } void toggle_is_disable_suspend() { _is_disable_suspend = !_is_disable_suspend; } @@ -757,15 +763,12 @@ public: void disable_jvmti_events() { _jvmti_events_disabled++; } void enable_jvmti_events() { _jvmti_events_disabled--; } - bool VTMS_transition_mark() const { return AtomicAccess::load(&_VTMS_transition_mark); } - void set_VTMS_transition_mark(bool val) { AtomicAccess::store(&_VTMS_transition_mark, val); } - // Temporarily skip posting JVMTI events for safety reasons when executions is in a critical section: - // - is in a VTMS transition (_is_in_VTMS_transition) + // - is in a vthread transition (_is_in_vthread_transition) // - is in an interruptLock or similar critical section (_is_disable_suspend) // - JVMTI is making a Java upcall (_is_in_java_upcall) bool should_hide_jvmti_events() const { - return _is_in_VTMS_transition || _is_disable_suspend || _is_in_java_upcall || _jvmti_events_disabled != 0; + return _is_in_vthread_transition || _is_disable_suspend || _is_in_java_upcall || _jvmti_events_disabled != 0; } bool on_monitor_waited_event() { return _on_monitor_waited_event; } @@ -773,10 +776,6 @@ public: bool pending_contended_entered_event() { return _contended_entered_monitor != nullptr; } ObjectMonitor* contended_entered_monitor() { return _contended_entered_monitor; } -#ifdef ASSERT - bool is_VTMS_transition_disabler() const { return _is_VTMS_transition_disabler; } - void set_is_VTMS_transition_disabler(bool val); -#endif #endif void set_contended_entered_monitor(ObjectMonitor* val) NOT_JVMTI_RETURN JVMTI_ONLY({ _contended_entered_monitor = val; }) @@ -931,9 +930,9 @@ public: static ByteSize preempt_alternate_return_offset() { return byte_offset_of(JavaThread, _preempt_alternate_return); } DEBUG_ONLY(static ByteSize interp_at_preemptable_vmcall_cnt_offset() { return byte_offset_of(JavaThread, _interp_at_preemptable_vmcall_cnt); }) static ByteSize unlocked_inflated_monitor_offset() { return byte_offset_of(JavaThread, _unlocked_inflated_monitor); } + static ByteSize is_in_vthread_transition_offset() { return byte_offset_of(JavaThread, _is_in_vthread_transition); } #if INCLUDE_JVMTI - static ByteSize is_in_VTMS_transition_offset() { return byte_offset_of(JavaThread, _is_in_VTMS_transition); } static ByteSize is_disable_suspend_offset() { return byte_offset_of(JavaThread, _is_disable_suspend); } #endif diff --git a/src/hotspot/share/runtime/mountUnmountDisabler.cpp b/src/hotspot/share/runtime/mountUnmountDisabler.cpp new file mode 100644 index 00000000000..5bf00323f10 --- /dev/null +++ b/src/hotspot/share/runtime/mountUnmountDisabler.cpp @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2025, 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 "classfile/javaClasses.inline.hpp" +#include "prims/jvmtiEventController.hpp" +#include "prims/jvmtiExport.hpp" +#include "prims/jvmtiThreadState.inline.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/javaThread.hpp" +#include "runtime/mountUnmountDisabler.hpp" +#include "runtime/threadSMR.hpp" + +volatile int MountUnmountDisabler::_global_vthread_transition_disable_count = 0; +volatile int MountUnmountDisabler::_active_disablers = 0; +bool MountUnmountDisabler::_exclusive_operation_ongoing = false; +bool MountUnmountDisabler::_notify_jvmti_events = false; + +#if INCLUDE_JVMTI +class JVMTIStartTransition : public StackObj { + JavaThread* _current; + Handle _vthread; + bool _is_mount; + bool _is_thread_end; + public: + JVMTIStartTransition(JavaThread* current, oop vthread, bool is_mount, bool is_thread_end) : + _current(current), _vthread(current, vthread), _is_mount(is_mount), _is_thread_end(is_thread_end) { + assert(DoJVMTIVirtualThreadTransitions || !JvmtiExport::can_support_virtual_threads(), "sanity check"); + if (DoJVMTIVirtualThreadTransitions && MountUnmountDisabler::notify_jvmti_events()) { + // post VirtualThreadUnmount event before VirtualThreadEnd + if (!_is_mount && JvmtiExport::should_post_vthread_unmount()) { + JvmtiExport::post_vthread_unmount((jthread)_vthread.raw_value()); + } + if (_is_thread_end && JvmtiExport::should_post_vthread_end()) { + JvmtiExport::post_vthread_end((jthread)_vthread.raw_value()); + } + } + } + ~JVMTIStartTransition() { + if (DoJVMTIVirtualThreadTransitions && MountUnmountDisabler::notify_jvmti_events()) { + if (_is_thread_end && _current->jvmti_thread_state() != nullptr) { + JvmtiExport::cleanup_thread(_current); + assert(_current->jvmti_thread_state() == nullptr, "should be null"); + assert(java_lang_Thread::jvmti_thread_state(_vthread()) == nullptr, "should be null"); + } + if (!_is_mount) { + _current->rebind_to_jvmti_thread_state_of(_current->threadObj()); + } + } + } +}; + +class JVMTIEndTransition : public StackObj { + JavaThread* _current; + Handle _vthread; + bool _is_mount; + bool _is_thread_start; + public: + JVMTIEndTransition(JavaThread* current, oop vthread, bool is_mount, bool is_thread_start) : + _current(current), _vthread(current, vthread), _is_mount(is_mount), _is_thread_start(is_thread_start) { + assert(DoJVMTIVirtualThreadTransitions || !JvmtiExport::can_support_virtual_threads(), "sanity check"); + if (DoJVMTIVirtualThreadTransitions && MountUnmountDisabler::notify_jvmti_events()) { + if (_is_mount) { + _current->rebind_to_jvmti_thread_state_of(_vthread()); + } + DEBUG_ONLY(bool is_virtual = java_lang_VirtualThread::is_instance(_current->jvmti_vthread())); + assert(_is_mount == is_virtual, "wrong identity"); + } + } + ~JVMTIEndTransition() { + if (DoJVMTIVirtualThreadTransitions && MountUnmountDisabler::notify_jvmti_events()) { + if (!_is_mount && _current->is_carrier_thread_suspended()) { + MonitorLocker ml(VThreadTransition_lock); + while (_current->is_carrier_thread_suspended()) { + ml.wait(200); + } + } + + if (_is_thread_start) { + // If interp_only_mode has been enabled then we must eagerly create JvmtiThreadState + // objects for globally enabled virtual thread filtered events. Otherwise, + // it is an important optimization to create JvmtiThreadState objects lazily. + // This optimization is disabled when watchpoint capabilities are present. It is to + // work around a bug with virtual thread frames which can be not deoptimized in time. + if (JvmtiThreadState::seen_interp_only_mode() || + JvmtiExport::should_post_field_access() || + JvmtiExport::should_post_field_modification()){ + JvmtiEventController::thread_started(_current); + } + if (JvmtiExport::should_post_vthread_start()) { + JvmtiExport::post_vthread_start((jthread)_vthread.raw_value()); + } + } + if (_is_mount && JvmtiExport::should_post_vthread_mount()) { + JvmtiExport::post_vthread_mount((jthread)_vthread.raw_value()); + } + } + } +}; +#endif // INCLUDE_JVMTI + +bool MountUnmountDisabler::is_start_transition_disabled(JavaThread* thread, oop vthread) { + // We need to read the per-vthread and global counters to check if transitions are disabled. + // In case of JVMTI present, the global counter will always be at least 1. This is to force + // the slow path and check for possible event posting. Here we need to check if transitions + // are actually disabled, so we compare the global counter against 1 or 0 accordingly. + // In case of JVMTI we also need to check for suspension. + int base_disable_count = notify_jvmti_events() ? 1 : 0; + return java_lang_Thread::vthread_transition_disable_count(vthread) > 0 + || global_vthread_transition_disable_count() > base_disable_count + JVMTI_ONLY(|| (JvmtiVTSuspender::is_vthread_suspended(java_lang_Thread::thread_id(vthread)) || thread->is_suspended())); +} + +void MountUnmountDisabler::start_transition(JavaThread* current, oop vthread, bool is_mount, bool is_thread_end) { + assert(!java_lang_Thread::is_in_vthread_transition(vthread), ""); + assert(!current->is_in_vthread_transition(), ""); + Handle vth = Handle(current, vthread); + JVMTI_ONLY(JVMTIStartTransition jst(current, vthread, is_mount, is_thread_end);) + + java_lang_Thread::set_is_in_vthread_transition(vth(), true); + current->set_is_in_vthread_transition(true); + + // Prevent loads of disable conditions from floating up. + OrderAccess::storeload(); + + while (is_start_transition_disabled(current, vth())) { + java_lang_Thread::set_is_in_vthread_transition(vth(), false); + current->set_is_in_vthread_transition(false); + { + // Block while transitions are disabled + MonitorLocker ml(VThreadTransition_lock); + while (is_start_transition_disabled(current, vth())) { + ml.wait(200); + } + } + + // Try to start transition again... + java_lang_Thread::set_is_in_vthread_transition(vth(), true); + current->set_is_in_vthread_transition(true); + OrderAccess::storeload(); + } + + // Start of the critical section. If this is a mount, we need an acquire barrier to + // synchronize with a possible disabler that executed an operation while this thread + // was unmounted. We make VirtualThread.mount guarantee such ordering and avoid barriers + // here. If this is an unmount, the handshake that the disabler executed against this + // thread already provided the needed synchronization. + // This pairs with the release barrier in xx_enable_for_one()/xx_enable_for_all(). +} + +void MountUnmountDisabler::end_transition(JavaThread* current, oop vthread, bool is_mount, bool is_thread_start) { + assert(java_lang_Thread::is_in_vthread_transition(vthread), ""); + assert(current->is_in_vthread_transition(), ""); + Handle vth = Handle(current, vthread); + JVMTI_ONLY(JVMTIEndTransition jst(current, vthread, is_mount, is_thread_start);) + + // End of the critical section. If this is an unmount, we need a release barrier before + // clearing the in_transition flags to make sure any memory operations executed in the + // transition are visible to a possible disabler that executes while this thread is unmounted. + // We make VirtualThread.unmount guarantee such ordering and avoid barriers here. If this is + // a mount, the only thing that needs to be published is the setting of carrierThread, since + // the handshake that the disabler will execute against it already provides the needed + // synchronization. This order is already guaranteed by the barriers in VirtualThread.mount. + // This pairs with the acquire barrier in xx_disable_for_one()/xx_disable_for_all(). + + java_lang_Thread::set_is_in_vthread_transition(vth(), false); + current->set_is_in_vthread_transition(false); + + // Unblock waiting transition disablers. + if (active_disablers() > 0) { + MonitorLocker ml(VThreadTransition_lock); + ml.notify_all(); + } +} + +// disable transitions for one virtual thread +// disable transitions for all threads if thread is nullptr or a platform thread +MountUnmountDisabler::MountUnmountDisabler(oop thread_oop) + : _is_exclusive(false), + _is_self(false) +{ + if (!Continuations::enabled()) { + return; // MountUnmountDisabler is no-op without virtual threads + } + if (Thread::current_or_null() == nullptr) { + return; // Detached thread, can be a call from Agent_OnLoad. + } + JavaThread* current = JavaThread::current(); + assert(!current->is_in_vthread_transition(), ""); + + bool is_virtual = java_lang_VirtualThread::is_instance(thread_oop); + if (thread_oop == nullptr || + (!is_virtual && thread_oop == current->threadObj()) || + (is_virtual && thread_oop == current->vthread())) { + _is_self = true; + return; // no need for current thread to disable and enable transitions for itself + } + + // Target can be virtual or platform thread. + // If target is a platform thread then we have to disable transitions for all threads. + // It is by several reasons: + // - carrier threads can mount virtual threads which may cause incorrect behavior + // - there is no mechanism to disable transitions for a specific carrier thread yet + if (is_virtual) { + _vthread = Handle(current, thread_oop); + disable_transition_for_one(); // disable transitions for one virtual thread + } else { + disable_transition_for_all(); // disable transitions for all virtual threads + } +} + +// disable transitions for all virtual threads +MountUnmountDisabler::MountUnmountDisabler(bool exclusive) + : _is_exclusive(exclusive), + _is_self(false) +{ + if (!Continuations::enabled()) { + return; // MountUnmountDisabler is no-op without virtual threads + } + if (Thread::current_or_null() == nullptr) { + return; // Detached thread, can be a call from Agent_OnLoad. + } + assert(!JavaThread::current()->is_in_vthread_transition(), ""); + disable_transition_for_all(); +} + +MountUnmountDisabler::~MountUnmountDisabler() { + if (!Continuations::enabled()) { + return; // MountUnmountDisabler is a no-op without virtual threads + } + if (Thread::current_or_null() == nullptr) { + return; // Detached thread, can be a call from Agent_OnLoad. + } + if (_is_self) { + return; // no need for current thread to disable and enable transitions for itself + } + if (_vthread() != nullptr) { + enable_transition_for_one(); // enable transitions for one virtual thread + } else { + enable_transition_for_all(); // enable transitions for all virtual threads + } +} + +// disable transitions for one virtual thread +void +MountUnmountDisabler::disable_transition_for_one() { + MonitorLocker ml(VThreadTransition_lock); + while (exclusive_operation_ongoing()) { + ml.wait(10); + } + + inc_active_disablers(); + java_lang_Thread::inc_vthread_transition_disable_count(_vthread()); + + // Prevent load of transition flag from floating up. + OrderAccess::storeload(); + + while (java_lang_Thread::is_in_vthread_transition(_vthread())) { + ml.wait(10); // wait while the virtual thread is in transition + } + + // Start of the critical section. If the target is unmounted, we need an acquire + // barrier to make sure memory operations executed in the last transition are visible. + // If the target is mounted, although the handshake that will be executed against it + // already provides the needed synchronization, we still need to prevent the load of + // carrierThread to float up. + // This pairs with the release barrier in end_transition(). + OrderAccess::acquire(); + DEBUG_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(true);) +} + +// disable transitions for all virtual threads +void +MountUnmountDisabler::disable_transition_for_all() { + DEBUG_ONLY(JavaThread* thread = JavaThread::current();) + DEBUG_ONLY(thread->set_is_disabler_at_start(true);) + + MonitorLocker ml(VThreadTransition_lock); + while (exclusive_operation_ongoing()) { + ml.wait(10); + } + if (_is_exclusive) { + set_exclusive_operation_ongoing(true); + while (active_disablers() > 0) { + ml.wait(10); + } + } + inc_active_disablers(); + inc_global_vthread_transition_disable_count(); + + // Prevent loads of transition flag from floating up. Technically not + // required since JavaThreadIteratorWithHandle includes full fence. + OrderAccess::storeload(); + + // Block while some mount/unmount transitions are in progress. + // Debug version fails and prints diagnostic information. + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { + while (jt->is_in_vthread_transition()) { + ml.wait(10); + } + } + + // Start of the critical section. If some target is unmounted, we need an acquire + // barrier to make sure memory operations executed in the last transition are visible. + // If a target is mounted, although the handshake that will be executed against it + // already provides the needed synchronization, we still need to prevent the load of + // carrierThread to float up. + // This pairs with the release barrier in end_transition(). + OrderAccess::acquire(); + DEBUG_ONLY(thread->set_is_vthread_transition_disabler(true);) + DEBUG_ONLY(thread->set_is_disabler_at_start(false);) +} + +// enable transitions for one virtual thread +void +MountUnmountDisabler::enable_transition_for_one() { + assert(java_lang_VirtualThread::is_instance(_vthread()), ""); + + // End of the critical section. If the target was unmounted, we need a + // release barrier before decrementing _vthread_transition_disable_count to + // make sure any memory operations executed by the disabler are visible to + // the target once it mounts again. If the target was mounted, the handshake + // executed against it already provided the needed synchronization. + // This pairs with the equivalent acquire barrier in start_transition(). + OrderAccess::release(); + + MonitorLocker ml(VThreadTransition_lock); + dec_active_disablers(); + java_lang_Thread::dec_vthread_transition_disable_count(_vthread()); + if (java_lang_Thread::vthread_transition_disable_count(_vthread()) == 0) { + ml.notify_all(); + } + DEBUG_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(false);) +} + +// enable transitions for all virtual threads +void +MountUnmountDisabler::enable_transition_for_all() { + JavaThread* thread = JavaThread::current(); + + // End of the critical section. If some target was unmounted, we need a + // release barrier before decrementing _global_vthread_transition_disable_count + // to make sure any memory operations executed by the disabler are visible to + // the target once it mounts again. If a target was mounted, the handshake + // executed against it already provided the needed synchronization. + // This pairs with the equivalent acquire barrier in start_transition(). + OrderAccess::release(); + + MonitorLocker ml(VThreadTransition_lock); + if (exclusive_operation_ongoing()) { + set_exclusive_operation_ongoing(false); + } + dec_active_disablers(); + dec_global_vthread_transition_disable_count(); + int base_disable_count = notify_jvmti_events() ? 1 : 0; + if (global_vthread_transition_disable_count() == base_disable_count || _is_exclusive) { + ml.notify_all(); + } + DEBUG_ONLY(thread->set_is_vthread_transition_disabler(false);) +} + +int MountUnmountDisabler::global_vthread_transition_disable_count() { + assert(_global_vthread_transition_disable_count >= 0, ""); + return AtomicAccess::load(&_global_vthread_transition_disable_count); +} + +void MountUnmountDisabler::inc_global_vthread_transition_disable_count() { + assert(VThreadTransition_lock->owned_by_self() || SafepointSynchronize::is_at_safepoint(), "Must be locked"); + assert(_global_vthread_transition_disable_count >= 0, ""); + AtomicAccess::store(&_global_vthread_transition_disable_count, _global_vthread_transition_disable_count + 1); +} + +void MountUnmountDisabler::dec_global_vthread_transition_disable_count() { + assert(VThreadTransition_lock->owned_by_self() || SafepointSynchronize::is_at_safepoint(), "Must be locked"); + assert(_global_vthread_transition_disable_count > 0, ""); + AtomicAccess::store(&_global_vthread_transition_disable_count, _global_vthread_transition_disable_count - 1); +} + +bool MountUnmountDisabler::exclusive_operation_ongoing() { + assert(VThreadTransition_lock->owned_by_self(), "Must be locked"); + return _exclusive_operation_ongoing; +} + +void MountUnmountDisabler::set_exclusive_operation_ongoing(bool val) { + assert(VThreadTransition_lock->owned_by_self(), "Must be locked"); + assert(_exclusive_operation_ongoing != val, ""); + _exclusive_operation_ongoing = val; +} + +int MountUnmountDisabler::active_disablers() { + assert(_active_disablers >= 0, ""); + return AtomicAccess::load(&_active_disablers); +} + +void MountUnmountDisabler::inc_active_disablers() { + assert(VThreadTransition_lock->owned_by_self(), "Must be locked"); + assert(_active_disablers >= 0, ""); + _active_disablers++; +} + +void MountUnmountDisabler::dec_active_disablers() { + assert(VThreadTransition_lock->owned_by_self(), "Must be locked"); + assert(_active_disablers > 0, ""); + _active_disablers--; +} + +bool MountUnmountDisabler::notify_jvmti_events() { + return _notify_jvmti_events; +} + +void MountUnmountDisabler::set_notify_jvmti_events(bool val, bool is_onload) { + if (val == _notify_jvmti_events || !DoJVMTIVirtualThreadTransitions) return; + + // Force slow path on start/end vthread transitions for JVMTI bookkeeping. + // 'val' is always true except with WhiteBox methods for testing purposes. + if (is_onload) { + // Skip existing increment methods since asserts will fail. + assert(val && _global_vthread_transition_disable_count == 0, ""); + AtomicAccess::inc(&_global_vthread_transition_disable_count); + } else { + assert(SafepointSynchronize::is_at_safepoint(), ""); + if (val) { + inc_global_vthread_transition_disable_count(); + } else { + dec_global_vthread_transition_disable_count(); + } + } + log_trace(continuations,tracking)("%s _notify_jvmti_events, _global_vthread_transition_disable_count=%d", val ? "enabling" : "disabling", _global_vthread_transition_disable_count); + _notify_jvmti_events = val; +} diff --git a/src/hotspot/share/runtime/mountUnmountDisabler.hpp b/src/hotspot/share/runtime/mountUnmountDisabler.hpp new file mode 100644 index 00000000000..2ebb09734a6 --- /dev/null +++ b/src/hotspot/share/runtime/mountUnmountDisabler.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2025, 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. + * + */ + +#ifndef SHARE_RUNTIME_MOUNTUNMOUNTDISABLER_HPP +#define SHARE_RUNTIME_MOUNTUNMOUNTDISABLER_HPP + +#include "memory/allocation.hpp" +#include "runtime/handles.hpp" + +class JavaThread; + +// This class adds support to disable virtual thread transitions (mount/unmount). +// This is needed to safely execute operations that access virtual thread state. +// Users should use the Handshake class when possible instead of using this directly. +class MountUnmountDisabler : public AnyObj { + // The global counter is used for operations that require disabling + // transitions for all virtual threads. Currently this is only used + // by some JVMTI operations. We also increment this counter when the + // first JVMTI agent attaches to always force the slowpath when starting + // a transition. This is needed because if JVMTI is present we need to + // check for possible event posting. + static volatile int _global_vthread_transition_disable_count; + static volatile int _active_disablers; + static bool _exclusive_operation_ongoing; + + bool _is_exclusive; // currently only for suspender or resumer + bool _is_virtual; // target thread is virtual + bool _is_self; // MountUnmountDisabler is a no-op for current platform, carrier or virtual thread + Handle _vthread; // virtual thread to disable transitions for, no-op if it is a platform thread + + //DEBUG_ONLY(static void print_info();) + void disable_transition_for_one(); + void disable_transition_for_all(); + void enable_transition_for_one(); + void enable_transition_for_all(); + + public: + MountUnmountDisabler(bool exlusive = false); + MountUnmountDisabler(oop thread_oop); + ~MountUnmountDisabler(); + + static int global_vthread_transition_disable_count(); + static void inc_global_vthread_transition_disable_count(); + static void dec_global_vthread_transition_disable_count(); + + static volatile int* global_vthread_transition_disable_count_address() { + return &_global_vthread_transition_disable_count; + } + + static bool exclusive_operation_ongoing(); + static void set_exclusive_operation_ongoing(bool val); + + static int active_disablers(); + static void inc_active_disablers(); + static void dec_active_disablers(); + + static void start_transition(JavaThread* thread, oop vthread, bool is_mount, bool is_thread_end); + static void end_transition(JavaThread* thread, oop vthread, bool is_mount, bool is_thread_start); + + static bool is_start_transition_disabled(JavaThread* thread, oop vthread); + + // enable notifications from VirtualThread about Mount/Unmount events + static bool _notify_jvmti_events; + static bool notify_jvmti_events(); + static void set_notify_jvmti_events(bool val, bool is_onload = false); + static bool* notify_jvmti_events_address() { + return &_notify_jvmti_events; + } +}; + +#endif // SHARE_RUNTIME_MOUNTUNMOUNTDISABLER_HPP diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index e4707a342a7..5d7e310fb11 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -49,7 +49,7 @@ Mutex* JfieldIdCreation_lock = nullptr; Monitor* JNICritical_lock = nullptr; Mutex* JvmtiThreadState_lock = nullptr; Monitor* EscapeBarrier_lock = nullptr; -Monitor* JvmtiVTMSTransition_lock = nullptr; +Monitor* VThreadTransition_lock = nullptr; Mutex* JvmtiVThreadSuspend_lock = nullptr; Monitor* Heap_lock = nullptr; #if INCLUDE_PARALLELGC @@ -265,7 +265,7 @@ void mutex_init() { MUTEX_DEFN(CompileStatistics_lock , PaddedMutex , safepoint); MUTEX_DEFN(DirectivesStack_lock , PaddedMutex , nosafepoint); - MUTEX_DEFN(JvmtiVTMSTransition_lock , PaddedMonitor, safepoint); // used for Virtual Thread Mount State transition management + MUTEX_DEFN(VThreadTransition_lock , PaddedMonitor, safepoint); MUTEX_DEFN(JvmtiVThreadSuspend_lock , PaddedMutex, nosafepoint-1); MUTEX_DEFN(EscapeBarrier_lock , PaddedMonitor, nosafepoint); // Used to synchronize object reallocation/relocking triggered by JVMTI MUTEX_DEFN(Management_lock , PaddedMutex , safepoint); // used for JVM management @@ -361,8 +361,8 @@ void mutex_init() { // JVMCIRuntime_lock must be acquired before JVMCI_lock to avoid deadlock MUTEX_DEFL(JVMCI_lock , PaddedMonitor, JVMCIRuntime_lock); #endif - MUTEX_DEFL(JvmtiThreadState_lock , PaddedMutex , JvmtiVTMSTransition_lock); // Used by JvmtiThreadState/JvmtiEventController - MUTEX_DEFL(SharedDecoder_lock , PaddedMutex , NmtVirtualMemory_lock); // Must be lower than NmtVirtualMemory_lock due to MemTracker::print_containing_region + MUTEX_DEFL(JvmtiThreadState_lock , PaddedMutex , VThreadTransition_lock); // Used by JvmtiThreadState/JvmtiEventController + MUTEX_DEFL(SharedDecoder_lock , PaddedMutex , NmtVirtualMemory_lock); // Must be lower than NmtVirtualMemory_lock due to MemTracker::print_containing_region // Allocate RecursiveMutex MultiArray_lock = new RecursiveMutex(); diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 74fe4650b7c..f6c0a967718 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -47,7 +47,7 @@ extern Mutex* JfieldIdCreation_lock; // a lock on creating JNI stati extern Monitor* JNICritical_lock; // a lock used while synchronizing with threads entering/leaving JNI critical regions extern Mutex* JvmtiThreadState_lock; // a lock on modification of JVMTI thread data extern Monitor* EscapeBarrier_lock; // a lock to sync reallocating and relocking objects because of JVMTI access -extern Monitor* JvmtiVTMSTransition_lock; // a lock for Virtual Thread Mount State transition (VTMS transition) management +extern Monitor* VThreadTransition_lock; // a lock used when disabling virtual thread transitions extern Mutex* JvmtiVThreadSuspend_lock; // a lock for virtual threads suspension extern Monitor* Heap_lock; // a lock on the heap #if INCLUDE_PARALLELGC diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 3a7c60cf83e..7e3bf763d08 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -737,34 +737,6 @@ 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_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)); - JvmtiVTMSTransitionDisabler::VTMS_vthread_start(vthread); - JNIHandles::destroy_local(vthread); -JRT_END - -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 - -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 - // 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 diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp index a696ce5a71b..140207e5d63 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -317,14 +317,6 @@ 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_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 static int rc_trace_method_entry(JavaThread* thread, Method* m); diff --git a/src/hotspot/share/runtime/stubDeclarations.hpp b/src/hotspot/share/runtime/stubDeclarations.hpp index b051c6d0e18..f9080364dc4 100644 --- a/src/hotspot/share/runtime/stubDeclarations.hpp +++ b/src/hotspot/share/runtime/stubDeclarations.hpp @@ -199,23 +199,10 @@ // declaration order. #ifdef COMPILER2 -// do_jvmti_stub(name) -#if INCLUDE_JVMTI -#define C2_JVMTI_STUBS_DO(do_jvmti_stub) \ - do_jvmti_stub(notify_jvmti_vthread_start) \ - do_jvmti_stub(notify_jvmti_vthread_end) \ - do_jvmti_stub(notify_jvmti_vthread_mount) \ - do_jvmti_stub(notify_jvmti_vthread_unmount) \ - -#else -#define C2_JVMTI_STUBS_DO(do_jvmti_stub) -#endif // INCLUDE_JVMTI - // client macro to operate on c2 stubs // // do_blob(name, type) // do_stub(name, fancy_jump, pass_tls, return_pc) -// do_jvmti_stub(name) // // do_blob is used for stubs that are generated via direct invocation // of the assembler to write into a blob of the appropriate type @@ -225,10 +212,8 @@ // in the IR graph employ a special type of jump (0, 1 or 2) or // provide access to TLS and the return pc. // -// do_jvmti_stub generates a JVMTI stub as an IR intrinsic which -// employs jump 0, and requires no special access -#define C2_STUBS_DO(do_blob, do_stub, do_jvmti_stub) \ +#define C2_STUBS_DO(do_blob, do_stub) \ do_blob(uncommon_trap, UncommonTrapBlob) \ do_blob(exception, ExceptionBlob) \ do_stub(new_instance, 0, true, false) \ @@ -239,16 +224,19 @@ do_stub(multianewarray4, 0, true, false) \ do_stub(multianewarray5, 0, true, false) \ do_stub(multianewarrayN, 0, true, false) \ - C2_JVMTI_STUBS_DO(do_jvmti_stub) \ do_stub(complete_monitor_locking, 0, false, false) \ do_stub(monitor_notify, 0, false, false) \ do_stub(monitor_notifyAll, 0, false, false) \ do_stub(rethrow, 2, true, true) \ do_stub(slow_arraycopy, 0, false, false) \ do_stub(register_finalizer, 0, false, false) \ + do_stub(vthread_end_first_transition, 0, false, false) \ + do_stub(vthread_start_final_transition, 0, false, false) \ + do_stub(vthread_start_transition, 0, false, false) \ + do_stub(vthread_end_transition, 0, false, false) \ #else -#define C2_STUBS_DO(do_blob, do_stub, do_jvmti_stub) +#define C2_STUBS_DO(do_blob, do_stub) #endif // Stubgen stub declarations @@ -1190,9 +1178,6 @@ // ignore do_stub(name, fancy_jump, pass_tls, return_pc) declarations #define DO_STUB_EMPTY4(name, fancy_jump, pass_tls, return_pc) -// ignore do_jvmti_stub(name) declarations -#define DO_JVMTI_STUB_EMPTY1(stub_name) - // ignore do_stub(blob_name, stub_name) declarations #define DO_STUB_EMPTY2(blob_name, stub_name) diff --git a/src/hotspot/share/runtime/stubInfo.cpp b/src/hotspot/share/runtime/stubInfo.cpp index bca6ff344ea..47a31fe7967 100644 --- a/src/hotspot/share/runtime/stubInfo.cpp +++ b/src/hotspot/share/runtime/stubInfo.cpp @@ -509,14 +509,6 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, StubId:: JOIN3(c2, name, id), \ EntryId:: JOIN3(c2, name, id)); \ -#define PROCESS_C2_JVMTI_STUB(name) \ - process_c2_blob(_group_cursor, _blob_cursor, \ - _stub_cursor, _entry_cursor, \ - "C2 Runtime " # name "_blob", \ - BlobId:: JOIN3(c2, name, id), \ - StubId:: JOIN3(c2, name, id), \ - EntryId:: JOIN3(c2, name, id)); \ - #define PROCESS_STUBGEN_BLOB(blob) \ process_stubgen_blob(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ @@ -610,7 +602,7 @@ void StubInfo::populate_stub_tables() { group_details(_group_cursor)._max = BlobId::NO_BLOBID; group_details(_group_cursor)._entry_base = EntryId::NO_ENTRYID; group_details(_group_cursor)._entry_max = EntryId::NO_ENTRYID; - C2_STUBS_DO(PROCESS_C2_BLOB, PROCESS_C2_STUB, PROCESS_C2_JVMTI_STUB); + C2_STUBS_DO(PROCESS_C2_BLOB, PROCESS_C2_STUB); _group_cursor = StubGroup::STUBGEN; group_details(_group_cursor)._name = "StubGen Stubs"; @@ -637,7 +629,6 @@ void StubInfo::populate_stub_tables() { #undef PROCESS_C1_BLOB #undef PROCESS_C2_BLOB #undef PROCESS_C2_STUB -#undef PROCESS_C2_JVMTI_STUB #undef PROCESS_STUBGEN_BLOB #undef PROCESS_STUBGEN_STUB #undef PROCESS_STUBGEN_ENTRY diff --git a/src/hotspot/share/runtime/stubInfo.hpp b/src/hotspot/share/runtime/stubInfo.hpp index eaea07f7e6c..9ed6e0cb9f9 100644 --- a/src/hotspot/share/runtime/stubInfo.hpp +++ b/src/hotspot/share/runtime/stubInfo.hpp @@ -164,7 +164,6 @@ enum class StubGroup : int { #define SHARED_DECLARE_TAG(name, type) JOIN3(shared, name, id) , #define C1_DECLARE_TAG(name) JOIN3(c1, name, id) , -#define C2_DECLARE_TAG1(name) JOIN3(c2, name, id) , #define C2_DECLARE_TAG2(name, _1) JOIN3(c2, name, id) , #define C2_DECLARE_TAG4(name, _1, _2, _3) JOIN3(c2, name, id) , #define STUBGEN_DECLARE_TAG(name) JOIN3(stubgen, name, id) , @@ -177,8 +176,7 @@ enum class BlobId : int { C1_STUBS_DO(C1_DECLARE_TAG) // declare an enum tag for each opto runtime blob or stub C2_STUBS_DO(C2_DECLARE_TAG2, - C2_DECLARE_TAG4, - C2_DECLARE_TAG1) + C2_DECLARE_TAG4) // declare an enum tag for each stubgen blob STUBGEN_BLOBS_DO(STUBGEN_DECLARE_TAG) NUM_BLOBIDS @@ -214,7 +212,6 @@ enum class BlobId : int { #define SHARED_DECLARE_TAG(name, type) JOIN3(shared, name, id) , #define C1_DECLARE_TAG(name) JOIN3(c1, name, id) , -#define C2_DECLARE_TAG1(name) JOIN3(c2, name, id) , #define C2_DECLARE_TAG2(name, _1) JOIN3(c2, name, id) , #define C2_DECLARE_TAG4(name, _1, _2, _3) JOIN3(c2, name, id) , #define STUBGEN_DECLARE_TAG(blob, name) JOIN3(stubgen, name, id) , @@ -227,8 +224,7 @@ enum class StubId : int { C1_STUBS_DO(C1_DECLARE_TAG) // declare an enum tag for each opto runtime blob or stub C2_STUBS_DO(C2_DECLARE_TAG2, - C2_DECLARE_TAG4, - C2_DECLARE_TAG1) + C2_DECLARE_TAG4) // declare an enum tag for each stubgen runtime stub STUBGEN_STUBS_DO(STUBGEN_DECLARE_TAG) NUM_STUBIDS @@ -307,7 +303,7 @@ enum class StubId : int { type ::ENTRY_COUNT - 1, \ // macros to declare a tag for a C1 generated blob or a C2 generated -// blob, stub or JVMTI stub all of which have a single unique entry +// blob, stub all of which have a single unique entry #define C1_DECLARE_TAG(name) \ JOIN3(c1, name, id), \ @@ -318,9 +314,6 @@ enum class StubId : int { #define C2_DECLARE_STUB_TAG(name, fancy_jump, pass_tls, return_pc) \ JOIN3(c2, name, id), \ -#define C2_DECLARE_JVMTI_STUB_TAG(name) \ - JOIN3(c2, name, id), \ - // macros to declare a tag for a StubGen normal entry or initialized // entry @@ -366,8 +359,7 @@ enum class EntryId : int { C1_STUBS_DO(C1_DECLARE_TAG) // declare an enum tag for each opto runtime blob or stub C2_STUBS_DO(C2_DECLARE_BLOB_TAG, - C2_DECLARE_STUB_TAG, - C2_DECLARE_JVMTI_STUB_TAG) + C2_DECLARE_STUB_TAG) // declare an enum tag for each stubgen entry or, in the case of an // array of entries for the first and last entries. STUBGEN_ALL_ENTRIES_DO(STUBGEN_DECLARE_TAG, @@ -382,7 +374,6 @@ enum class EntryId : int { #undef C1_DECLARE_TAG #undef C2_DECLARE_BLOB_TAG #undef C2_DECLARE_STUB_TAG -#undef C2_DECLARE_JVMTI_STUB_TAG #undef STUBGEN_DECLARE_TAG #undef STUBGEN_DECLARE_INIT_TAG #undef STUBGEN_DECLARE_ARRAY_TAG @@ -402,7 +393,7 @@ enum class EntryId : int { 0 C1_STUBS_DO(COUNT1) #define C2_STUB_COUNT_INITIALIZER \ - 0 C2_STUBS_DO(COUNT2, COUNT4, COUNT1) + 0 C2_STUBS_DO(COUNT2, COUNT4) #define STUBGEN_BLOB_COUNT_INITIALIZER \ 0 STUBGEN_BLOBS_DO(COUNT1) diff --git a/src/hotspot/share/runtime/suspendResumeManager.cpp b/src/hotspot/share/runtime/suspendResumeManager.cpp index 1b9606cf633..067579b6386 100644 --- a/src/hotspot/share/runtime/suspendResumeManager.cpp +++ b/src/hotspot/share/runtime/suspendResumeManager.cpp @@ -93,11 +93,12 @@ void SuspendResumeManager::set_suspended_current_thread(int64_t vthread_id, bool } bool SuspendResumeManager::suspend(bool register_vthread_SR) { - JVMTI_ONLY(assert(!_target->is_in_VTMS_transition(), "no suspend allowed in VTMS transition");) JavaThread* self = JavaThread::current(); if (_target == self) { // If target is the current thread we can bypass the handshake machinery // and just suspend directly. + // Self-suspending while in transition can cause deadlocks. + assert(!self->is_in_vthread_transition(), "no self-suspend allowed in transition"); // The vthread() oop must only be accessed before state is set to _thread_blocked. int64_t id = java_lang_Thread::thread_id(_target->vthread()); ThreadBlockInVM tbivm(self); diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index 48f4eb16cf1..8772195df0b 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -1179,12 +1179,13 @@ public: GrowableArray* _bcis; JavaThreadStatus _thread_status; OopHandle _thread_name; + OopHandle _carrier_thread; GrowableArray* _locks; Blocker _blocker; - GetThreadSnapshotHandshakeClosure(Handle thread_h, JavaThread* java_thread): + GetThreadSnapshotHandshakeClosure(Handle thread_h): HandshakeClosure("GetThreadSnapshotHandshakeClosure"), - _thread_h(thread_h), _java_thread(java_thread), + _thread_h(thread_h), _java_thread(nullptr), _frame_count(0), _methods(nullptr), _bcis(nullptr), _thread_status(), _thread_name(nullptr), _locks(nullptr), _blocker() { @@ -1275,14 +1276,15 @@ private: public: void do_thread(Thread* th) override { Thread* current = Thread::current(); + _java_thread = th != nullptr ? JavaThread::cast(th) : nullptr; bool is_virtual = java_lang_VirtualThread::is_instance(_thread_h()); if (_java_thread != nullptr) { if (is_virtual) { // mounted vthread, use carrier thread state - oop carrier_thread = java_lang_VirtualThread::carrier_thread(_thread_h()); - assert(carrier_thread != nullptr, "should only get here for a mounted vthread"); - _thread_status = java_lang_Thread::get_thread_status(carrier_thread); + _carrier_thread = OopHandle(oop_storage(), java_lang_VirtualThread::carrier_thread(_thread_h())); + assert(_carrier_thread.resolve() == _java_thread->threadObj(), ""); + _thread_status = java_lang_Thread::get_thread_status(_carrier_thread.resolve()); } else { _thread_status = java_lang_Thread::get_thread_status(_thread_h()); } @@ -1459,67 +1461,21 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) { oop thread_oop; bool has_javathread = tlh.cv_internal_thread_to_JavaThread(jthread, &java_thread, &thread_oop); assert((has_javathread && thread_oop != nullptr) || !has_javathread, "Missing Thread oop"); - Handle thread_h(THREAD, thread_oop); - bool is_virtual = java_lang_VirtualThread::is_instance(thread_h()); // Deals with null + bool is_virtual = java_lang_VirtualThread::is_instance(thread_oop); // Deals with null if (!has_javathread && !is_virtual) { return nullptr; // thread terminated so not of interest } - // wrapper to auto delete JvmtiVTMSTransitionDisabler - class TransitionDisabler { - JvmtiVTMSTransitionDisabler* _transition_disabler; - public: - TransitionDisabler(): _transition_disabler(nullptr) {} - ~TransitionDisabler() { - reset(); - } - void init(jobject jthread) { - _transition_disabler = new (mtInternal) JvmtiVTMSTransitionDisabler(jthread); - } - void reset() { - if (_transition_disabler != nullptr) { - delete _transition_disabler; - _transition_disabler = nullptr; - } - } - } transition_disabler; - - Handle carrier_thread; - if (is_virtual) { - // 1st need to disable mount/unmount transitions - transition_disabler.init(jthread); - - carrier_thread = Handle(THREAD, java_lang_VirtualThread::carrier_thread(thread_h())); - if (carrier_thread != nullptr) { - // Note: The java_thread associated with this carrier_thread may not be - // protected by the ThreadsListHandle above. There could have been an - // unmount and remount after the ThreadsListHandle above was created - // and before the JvmtiVTMSTransitionDisabler was created. However, as - // we have disabled transitions, if we are mounted on it, then it cannot - // terminate and so is safe to handshake with. - java_thread = java_lang_Thread::thread(carrier_thread()); - } else { - // We may have previously found a carrier but the virtual thread has unmounted - // after that, so clear that previous reference. - java_thread = nullptr; - } - } else { - java_thread = java_lang_Thread::thread(thread_h()); - } - // Handshake with target - GetThreadSnapshotHandshakeClosure cl(thread_h, java_thread); - if (java_thread == nullptr) { - // unmounted vthread, execute on the current thread - cl.do_thread(nullptr); + Handle thread_h(THREAD, thread_oop); + GetThreadSnapshotHandshakeClosure cl(thread_h); + if (java_lang_VirtualThread::is_instance(thread_oop)) { + Handshake::execute(&cl, thread_oop); } else { Handshake::execute(&cl, &tlh, java_thread); } - // all info is collected, can enable transitions. - transition_disabler.reset(); - // StackTrace InstanceKlass* ste_klass = vmClasses::StackTraceElement_klass(); assert(ste_klass != nullptr, "must be loaded"); @@ -1569,7 +1525,7 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) { Handle snapshot = jdk_internal_vm_ThreadSnapshot::allocate(InstanceKlass::cast(snapshot_klass), CHECK_NULL); jdk_internal_vm_ThreadSnapshot::set_name(snapshot(), cl._thread_name.resolve()); jdk_internal_vm_ThreadSnapshot::set_thread_status(snapshot(), (int)cl._thread_status); - jdk_internal_vm_ThreadSnapshot::set_carrier_thread(snapshot(), carrier_thread()); + jdk_internal_vm_ThreadSnapshot::set_carrier_thread(snapshot(), cl._carrier_thread.resolve()); jdk_internal_vm_ThreadSnapshot::set_stack_trace(snapshot(), trace()); jdk_internal_vm_ThreadSnapshot::set_locks(snapshot(), locks()); if (!cl._blocker.is_empty()) { diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index 51543f835c1..514bf07e665 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -246,11 +246,11 @@ final class VirtualThread extends BaseVirtualThread { @Hidden @JvmtiHideEvents public void run() { - vthread.notifyJvmtiStart(); // notify JVMTI + vthread.endFirstTransition(); try { vthread.run(task); } finally { - vthread.notifyJvmtiEnd(); // notify JVMTI + vthread.startFinalTransition(); } } }; @@ -491,8 +491,9 @@ final class VirtualThread extends BaseVirtualThread { @ChangesCurrentThread @ReservedStackAccess private void mount() { - // notify JVMTI before mount - notifyJvmtiMount(/*hide*/true); + startTransition(/*is_mount*/true); + // We assume following volatile accesses provide equivalent + // of acquire ordering, otherwise we need U.loadFence() here. // sets the carrier thread Thread carrier = Thread.currentCarrierThread(); @@ -533,8 +534,9 @@ final class VirtualThread extends BaseVirtualThread { } carrier.clearInterrupt(); - // notify JVMTI after unmount - notifyJvmtiUnmount(/*hide*/false); + // We assume previous volatile accesses provide equivalent + // of release ordering, otherwise we need U.storeFence() here. + endTransition(/*is_mount*/false); } /** @@ -543,11 +545,11 @@ final class VirtualThread extends BaseVirtualThread { */ @Hidden private boolean yieldContinuation() { - notifyJvmtiUnmount(/*hide*/true); + startTransition(/*is_mount*/false); try { return Continuation.yield(VTHREAD_SCOPE); } finally { - notifyJvmtiMount(/*hide*/false); + endTransition(/*is_mount*/true); } } @@ -1401,23 +1403,34 @@ final class VirtualThread extends BaseVirtualThread { this.carrierThread = carrier; } - // -- JVM TI support -- + // The following four methods notify the VM when a "transition" starts and ends. + // A "mount transition" embodies the steps to transfer control from a platform + // thread to a virtual thread, changing the thread identity, and starting or + // resuming the virtual thread's continuation on the carrier. + // An "unmount transition" embodies the steps to transfer control from a virtual + // thread to its carrier, suspending the virtual thread's continuation, and + // restoring the thread identity to the platform thread. + // The notifications to the VM are necessary in order to coordinate with functions + // (JVMTI mostly) that disable transitions for one or all virtual threads. Starting + // a transition may block if transitions are disabled. Ending a transition may + // notify a thread that is waiting to disable transitions. The notifications are + // also used to post JVMTI events for virtual thread start and end. @IntrinsicCandidate @JvmtiMountTransition - private native void notifyJvmtiStart(); + private native void endFirstTransition(); @IntrinsicCandidate @JvmtiMountTransition - private native void notifyJvmtiEnd(); + private native void startFinalTransition(); @IntrinsicCandidate @JvmtiMountTransition - private native void notifyJvmtiMount(boolean hide); + private native void startTransition(boolean is_mount); @IntrinsicCandidate @JvmtiMountTransition - private native void notifyJvmtiUnmount(boolean hide); + private native void endTransition(boolean is_mount); @IntrinsicCandidate private static native void notifyJvmtiDisableSuspend(boolean enter); diff --git a/src/java.base/share/native/libjava/VirtualThread.c b/src/java.base/share/native/libjava/VirtualThread.c index 3cacf89e752..5936031f926 100644 --- a/src/java.base/share/native/libjava/VirtualThread.c +++ b/src/java.base/share/native/libjava/VirtualThread.c @@ -32,10 +32,10 @@ #define VIRTUAL_THREAD "Ljava/lang/VirtualThread;" static JNINativeMethod methods[] = { - { "notifyJvmtiStart", "()V", (void *)&JVM_VirtualThreadStart }, - { "notifyJvmtiEnd", "()V", (void *)&JVM_VirtualThreadEnd }, - { "notifyJvmtiMount", "(Z)V", (void *)&JVM_VirtualThreadMount }, - { "notifyJvmtiUnmount", "(Z)V", (void *)&JVM_VirtualThreadUnmount }, + { "endFirstTransition", "()V", (void *)&JVM_VirtualThreadEndFirstTransition }, + { "startFinalTransition", "()V", (void *)&JVM_VirtualThreadStartFinalTransition }, + { "startTransition", "(Z)V", (void *)&JVM_VirtualThreadStartTransition }, + { "endTransition", "(Z)V", (void *)&JVM_VirtualThreadEndTransition }, { "notifyJvmtiDisableSuspend", "(Z)V", (void *)&JVM_VirtualThreadDisableSuspend }, { "postPinnedEvent", "(" STR ")V", (void *)&JVM_VirtualThreadPinnedEvent }, { "takeVirtualThreadListToUnblock", "()" VIRTUAL_THREAD, (void *)&JVM_TakeVirtualThreadListToUnblock}, diff --git a/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreadsWhenParking.java b/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreadsWhenParking.java new file mode 100644 index 00000000000..bd7dc014460 --- /dev/null +++ b/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreadsWhenParking.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2025, 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=transitions + * @bug 8364343 + * @summary HotSpotDiagnosticMXBean.dumpThreads while virtual threads are parking and unparking + * @requires vm.continuations + * @modules jdk.management + * @library /test/lib + * @run main/othervm/timeout=200 DumpThreadsWhenParking 1000 1 100 + */ + +/* + * @test id=concurrent + * @summary HotSpotDiagnosticMXBean.dumpThreads from concurrent threads while virtual threads + * are parking and unparking + * @requires vm.continuations + * @modules jdk.management + * @library /test/lib + * @run main/othervm/timeout=200 DumpThreadsWhenParking 100 4 100 + */ + +/* + * @test id=concurrent_gcstress + * @summary HotSpotDiagnosticMXBean.dumpThreads from concurrent threads while virtual threads + * are parking and unparking + * @requires vm.debug == true & vm.continuations + * @modules jdk.management + * @library /test/lib + * @run main/othervm/timeout=200 -XX:+UnlockDiagnosticVMOptions -XX:+FullGCALot -XX:FullGCALotInterval=10000 DumpThreadsWhenParking 100 4 100 + */ + +import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Instant; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.Phaser; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.LockSupport; +import java.util.stream.IntStream; +import com.sun.management.HotSpotDiagnosticMXBean; +import jdk.test.lib.thread.VThreadRunner; +import jdk.test.lib.threaddump.ThreadDump; + +public class DumpThreadsWhenParking { + + public static void main(String... args) throws Throwable { + int vthreadCount = Integer.parseInt(args[0]); + int concurrentDumpers = Integer.parseInt(args[1]); + int iterations = Integer.parseInt(args[2]); + + // need >=2 carriers to make progress + VThreadRunner.ensureParallelism(2); + + try (var executor = Executors.newVirtualThreadPerTaskExecutor(); + var pool = Executors.newCachedThreadPool()) { + + // start virtual threads that park and unpark + var done = new AtomicBoolean(); + var phaser = new Phaser(vthreadCount + 1); + for (int i = 0; i < vthreadCount; i++) { + executor.submit(() -> { + phaser.arriveAndAwaitAdvance(); + while (!done.get()) { + LockSupport.parkNanos(1); + } + }); + } + // wait for all virtual threads to start so all have a non-empty stack + System.out.format("Waiting for %d virtual threads to start ...%n", vthreadCount); + phaser.arriveAndAwaitAdvance(); + System.out.format("%d virtual threads started.%n", vthreadCount); + + // Bash on HotSpotDiagnosticMXBean.dumpThreads from >= 1 threads + try { + String containerName = Objects.toIdentityString(executor); + for (int i = 1; i <= iterations; i++) { + System.out.format("%s %d of %d ...%n", Instant.now(), i, iterations); + List> futures = IntStream.of(0, concurrentDumpers) + .mapToObj(_ -> pool.submit(() -> dumpThreads(containerName, vthreadCount))) + .toList(); + for (Future future : futures) { + future.get(); + } + } + } finally { + done.set(true); + } + } + } + + /** + * Invoke HotSpotDiagnosticMXBean.dumpThreads to generate a thread dump to a file in + * JSON format. Parse the thread dump to ensure it contains a thread grouping with + * the expected number of virtual threads. + */ + static Void dumpThreads(String containerName, int expectedVThreadCount) throws Exception { + long tid = Thread.currentThread().threadId(); + Path file = Path.of("threads-" + tid + ".json").toAbsolutePath(); + Files.deleteIfExists(file); + ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class) + .dumpThreads(file.toString(), HotSpotDiagnosticMXBean.ThreadDumpFormat.JSON); + + // read and parse the dump + String jsonText = Files.readString(file); + ThreadDump threadDump = ThreadDump.parse(jsonText); + var container = threadDump.findThreadContainer(containerName).orElse(null); + if (container == null) { + fail(containerName + " not found in thread dump"); + } + + // check expected virtual thread count + long threadCount = container.threads().count(); + if (threadCount != expectedVThreadCount) { + fail(threadCount + " virtual threads found, expected " + expectedVThreadCount); + } + + // check each thread is a virtual thread with stack frames + container.threads().forEach(t -> { + if (!t.isVirtual()) { + fail("#" + t.tid() + "(" + t.name() + ") is not a virtual thread"); + } + long stackFrameCount = t.stack().count(); + if (stackFrameCount == 0) { + fail("#" + t.tid() + " has empty stack"); + } + }); + return null; + } + + private static void fail(String message) { + throw new RuntimeException(message); + } +} diff --git a/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreadsWithEliminatedLock.java b/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreadsWithEliminatedLock.java index 4cbd0de460c..787d53ae045 100644 --- a/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreadsWithEliminatedLock.java +++ b/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreadsWithEliminatedLock.java @@ -43,6 +43,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.Instant; import java.util.List; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -73,9 +74,11 @@ public class DumpThreadsWithEliminatedLock { // A thread that spins creating and adding to a StringBuffer. StringBuffer is // synchronized, assume object will be scalar replaced and the lock eliminated. + var started = new CountDownLatch(1); var done = new AtomicBoolean(); var ref = new AtomicReference(); Thread thread = factory.newThread(() -> { + started.countDown(); while (!done.get()) { StringBuffer sb = new StringBuffer(); sb.append(System.currentTimeMillis()); @@ -85,6 +88,7 @@ public class DumpThreadsWithEliminatedLock { }); try { thread.start(); + started.await(); if (plain) { testPlainFormat(); } else {