8364343: Virtual Thread transition management needs to be independent of JVM TI

Co-authored-by: Alan Bateman <alanb@openjdk.org>
Reviewed-by: coleenp, dholmes, sspitsyn
This commit is contained in:
Patricio Chilano Mateo 2025-12-03 20:01:45 +00:00
parent ba777f6610
commit e534ee9932
41 changed files with 1140 additions and 1049 deletions

View File

@ -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<jint>(_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<jint>(_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<jint>(_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<jboolean>(_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<jboolean>(_is_in_vthread_transition_offset);
AtomicAccess::store(addr, (jboolean)val);
}
void java_lang_Thread::clear_scopedValueBindings(oop java_thread) {

View File

@ -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);

View File

@ -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 */ \

View File

@ -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, "<resolved_references>") \
template(init_lock_name, "<init_lock>") \

View File

@ -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

View File

@ -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);

View File

@ -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) \

View File

@ -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;

View File

@ -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) {

View File

@ -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

View File

@ -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();
}

View File

@ -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");

View File

@ -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

View File

@ -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<jthread>* elist = new GrowableArray<jthread>(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<jthread>* elist = new GrowableArray<jthread>(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);

View File

@ -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,

View File

@ -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) {

View File

@ -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<jvmtiExtensionFunctionInfo*>* 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;

View File

@ -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<jlong> 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();

View File

@ -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
//

View File

@ -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

View File

@ -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");

View File

@ -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

View File

@ -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).

View File

@ -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

View File

@ -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.");

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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<oopDesc*>(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<oopDesc*>(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<oopDesc*>(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<oopDesc*>(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

View File

@ -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);

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -1179,12 +1179,13 @@ public:
GrowableArray<int>* _bcis;
JavaThreadStatus _thread_status;
OopHandle _thread_name;
OopHandle _carrier_thread;
GrowableArray<OwnedLock>* _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()) {

View File

@ -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);

View File

@ -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},

View File

@ -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<Future<Void>> 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);
}
}

View File

@ -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<String>();
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 {