mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-28 11:53:09 +00:00
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:
parent
ba777f6610
commit
e534ee9932
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 */ \
|
||||
|
||||
@ -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>") \
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) \
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
//
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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).
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.");
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
450
src/hotspot/share/runtime/mountUnmountDisabler.cpp
Normal file
450
src/hotspot/share/runtime/mountUnmountDisabler.cpp
Normal 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;
|
||||
}
|
||||
92
src/hotspot/share/runtime/mountUnmountDisabler.hpp
Normal file
92
src/hotspot/share/runtime/mountUnmountDisabler.hpp
Normal 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
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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},
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user