8298853: JvmtiVTMSTransitionDisabler should support disabling one virtual thread transitions

Reviewed-by: pchilanomate, lmesnik
This commit is contained in:
Serguei Spitsyn 2023-02-14 01:28:42 +00:00
parent abbeb7e4d2
commit 13b1ebba27
8 changed files with 182 additions and 69 deletions

@ -1516,6 +1516,8 @@ int java_lang_Thread::_contextClassLoader_offset;
int java_lang_Thread::_inheritedAccessControlContext_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::_interrupted_offset;
int java_lang_Thread::_tid_offset;
int java_lang_Thread::_continuation_offset;
@ -1566,6 +1568,31 @@ 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);
}
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::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);
}
bool java_lang_Thread::is_in_VTMS_transition(oop java_thread) {
return java_thread->bool_field_volatile(_jvmti_is_in_VTMS_transition_offset);
}
void java_lang_Thread::set_is_in_VTMS_transition(oop java_thread, bool val) {
java_thread->bool_field_put_volatile(_jvmti_is_in_VTMS_transition_offset, val);
}
void java_lang_Thread::clear_scopedValueBindings(oop java_thread) {
assert(java_thread != nullptr, "need a java_lang_Thread pointer here");
java_thread->obj_field_put(_scopedValueBindings_offset, nullptr);

@ -334,6 +334,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) \
JFR_ONLY(macro(java_lang_Thread, jfr_epoch, short_signature, false))
class java_lang_Thread : AllStatic {
@ -347,6 +349,8 @@ class java_lang_Thread : AllStatic {
static int _inheritedAccessControlContext_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 _interrupted_offset;
static int _tid_offset;
static int _continuation_offset;
@ -396,6 +400,11 @@ 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);
// Clear all scoped value bindings on error
static void clear_scopedValueBindings(oop java_thread);

@ -520,6 +520,8 @@
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(module_entry_name, "module_entry") \
template(resolved_references_name, "<resolved_references>") \
template(init_lock_name, "<init_lock>") \

@ -149,7 +149,7 @@ jvmtiError
JvmtiEnv::SetThreadLocalStorage(jthread thread, const void* data) {
JavaThread* current = JavaThread::current();
JvmtiThreadState* state = nullptr;
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current);
JavaThread* java_thread = nullptr;
@ -202,7 +202,7 @@ JvmtiEnv::GetThreadLocalStorage(jthread thread, void** data_ptr) {
VM_ENTRY_BASE(jvmtiError, JvmtiEnv::GetThreadLocalStorage , current_thread)
debug_only(VMNativeEntryWrapper __vew;)
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -575,7 +575,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;
JvmtiVTMSTransitionDisabler disabler(event_thread);
if (event_thread == nullptr) {
// Can be called at Agent_OnLoad() time with event_thread == nullptr
@ -856,7 +856,7 @@ JvmtiEnv::GetJLocationFormat(jvmtiJlocationFormat* format_ptr) {
jvmtiError
JvmtiEnv::GetThreadState(jthread thread, jint* thread_state_ptr) {
JavaThread* current_thread = JavaThread::current();
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -1181,7 +1181,7 @@ jvmtiError
JvmtiEnv::StopThread(jthread thread, jobject exception) {
JavaThread* current_thread = JavaThread::current();
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
oop thread_oop = nullptr;
@ -1213,7 +1213,7 @@ JvmtiEnv::InterruptThread(jthread thread) {
JavaThread* current_thread = JavaThread::current();
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -1258,7 +1258,7 @@ JvmtiEnv::GetThreadInfo(jthread thread, jvmtiThreadInfo* info_ptr) {
JavaThread* java_thread = nullptr;
oop thread_oop = nullptr;
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
// if thread is null the current thread is used
@ -1358,7 +1358,7 @@ JvmtiEnv::GetOwnedMonitorInfo(jthread thread, jint* owned_monitor_count_ptr, job
GrowableArray<jvmtiMonitorStackDepthInfo*> *owned_monitors_list =
new (mtServiceability) GrowableArray<jvmtiMonitorStackDepthInfo*>(1, mtServiceability);
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(calling_thread);
JavaThread* java_thread = nullptr;
@ -1431,7 +1431,7 @@ JvmtiEnv::GetOwnedMonitorStackDepthInfo(jthread thread, jint* monitor_info_count
GrowableArray<jvmtiMonitorStackDepthInfo*> *owned_monitors_list =
new (mtServiceability) GrowableArray<jvmtiMonitorStackDepthInfo*>(1, mtServiceability);
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(calling_thread);
JavaThread* java_thread = nullptr;
@ -1501,7 +1501,7 @@ JvmtiEnv::GetCurrentContendedMonitor(jthread thread, jobject* monitor_ptr) {
JavaThread* calling_thread = JavaThread::current();
HandleMark hm(calling_thread);
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(calling_thread);
JavaThread* java_thread = nullptr;
@ -1715,7 +1715,7 @@ JvmtiEnv::GetStackTrace(jthread thread, jint start_depth, jint max_frame_count,
JavaThread* current_thread = JavaThread::current();
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -1833,7 +1833,7 @@ JvmtiEnv::GetFrameCount(jthread thread, jint* count_ptr) {
JavaThread* current_thread = JavaThread::current();
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -1877,7 +1877,7 @@ JvmtiEnv::PopFrame(jthread thread) {
if (thread == nullptr) {
return JVMTI_ERROR_INVALID_THREAD;
}
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -1925,7 +1925,7 @@ JvmtiEnv::GetFrameLocation(jthread thread, jint depth, jmethodID* method_ptr, jl
JavaThread* current_thread = JavaThread::current();
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -1965,7 +1965,7 @@ JvmtiEnv::GetFrameLocation(jthread thread, jint depth, jmethodID* method_ptr, jl
jvmtiError
JvmtiEnv::NotifyFramePop(jthread thread, jint depth) {
ResourceMark rm;
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh;
JavaThread* java_thread = nullptr;
@ -2243,7 +2243,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;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -2284,7 +2284,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;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -2326,7 +2326,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;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -2368,7 +2368,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;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -2410,7 +2410,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;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -2452,7 +2452,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;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -2493,7 +2493,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;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -2530,7 +2530,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;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -2567,7 +2567,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;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -2604,7 +2604,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;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;
@ -2641,7 +2641,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;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;

@ -680,7 +680,7 @@ JvmtiEnvBase::check_and_skip_hidden_frames(oop vthread, javaVFrame* jvf) {
// nothing to skip
return jvf;
}
jvf = check_and_skip_hidden_frames(state->is_in_VTMS_transition(), jvf);
jvf = check_and_skip_hidden_frames(java_lang_Thread::is_in_VTMS_transition(vthread), jvf);
return jvf;
}
@ -1912,7 +1912,7 @@ JvmtiEnvBase::force_early_return(jthread thread, jvalue value, TosState tos) {
JavaThread* current_thread = JavaThread::current();
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
JvmtiVTMSTransitionDisabler disabler(thread);
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = nullptr;

@ -376,7 +376,6 @@ void JvmtiEnvThreadState::reset_current_location(jvmtiEvent event_type, bool ena
JavaThread* thread = get_thread_or_saved();
oop thread_oop = jvmti_thread_state()->get_thread_oop();
assert(!jvmti_thread_state()->is_in_VTMS_transition(), "sanity check");
if (thread == nullptr && event_type == JVMTI_EVENT_SINGLE_STEP && is_virtual()) {
// Handle the unmounted virtual thread case.

@ -82,9 +82,7 @@ JvmtiThreadState::JvmtiThreadState(JavaThread* thread, oop thread_oop)
_earlyret_tos = ilgl;
_earlyret_value.j = 0L;
_earlyret_oop = nullptr;
_jvmti_event_queue = nullptr;
_is_in_VTMS_transition = false;
_is_virtual = false;
_thread_oop_h = OopHandle(JvmtiExport::jvmti_oop_storage(), thread_oop);
@ -218,8 +216,11 @@ JvmtiThreadState::periodic_clean_up() {
// VTMS transitions cannot be disabled while this counter is positive.
volatile int JvmtiVTMSTransitionDisabler::_VTMS_transition_count = 0;
// VTMS transitions is disabled while this counter is positive
volatile int JvmtiVTMSTransitionDisabler::_VTMS_transition_disable_count = 0;
// VTMS transitions for one virtual thread are disabled while it is positive
volatile int JvmtiVTMSTransitionDisabler::_VTMS_transition_disable_for_one_count = 0;
// VTMs transitions for all virtual threads are disabled while it is positive
volatile int JvmtiVTMSTransitionDisabler::_VTMS_transition_disable_for_all_count = 0;
// There is an active suspender or resumer.
volatile bool JvmtiVTMSTransitionDisabler::_SR_mode = false;
@ -228,8 +229,9 @@ volatile bool JvmtiVTMSTransitionDisabler::_SR_mode = false;
#ifdef ASSERT
void
JvmtiVTMSTransitionDisabler::print_info() {
log_error(jvmti)("_VTMS_transition_disable_count: %d _VTMS_transition_count: %d\n\n",
_VTMS_transition_disable_count, _VTMS_transition_count);
log_error(jvmti)("_VTMS_transition_count: %d\n", _VTMS_transition_count);
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(); ) {
ResourceMark rm;
@ -240,15 +242,35 @@ JvmtiVTMSTransitionDisabler::print_info() {
}
#endif
JvmtiVTMSTransitionDisabler::JvmtiVTMSTransitionDisabler(bool is_SR) {
// disable VTMS transitions for one virtual thread
// no-op if thread is non-NULL and not a virtual thread
JvmtiVTMSTransitionDisabler::JvmtiVTMSTransitionDisabler(jthread thread)
: _is_SR(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.
}
_is_SR = is_SR;
disable_VTMS_transitions();
if (_thread != nullptr) {
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), _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.
}
VTMS_transition_disable_for_all();
}
JvmtiVTMSTransitionDisabler::~JvmtiVTMSTransitionDisabler() {
@ -258,11 +280,43 @@ JvmtiVTMSTransitionDisabler::~JvmtiVTMSTransitionDisabler() {
if (Thread::current_or_null() == nullptr) {
return; // Detached thread, can be a call from Agent_OnLoad.
}
enable_VTMS_transitions();
if (_thread != nullptr) {
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
}
}
// disable VTMS transitions for one virtual thread
void
JvmtiVTMSTransitionDisabler::disable_VTMS_transitions() {
JvmtiVTMSTransitionDisabler::VTMS_transition_disable_for_one() {
assert(_thread != NULL, "sanity check");
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
}
ThreadBlockInVM tbivm(thread);
MonitorLocker ml(JvmtiVTMSTransition_lock, Mutex::_no_safepoint_check_flag);
while (_SR_mode) { // suspender or resumer is a JvmtiVTMSTransitionDisabler monopolist
ml.wait(10); // wait while there is an active suspender or resumer
}
Atomic::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;
{
@ -276,11 +330,11 @@ JvmtiVTMSTransitionDisabler::disable_VTMS_transitions() {
}
if (_is_SR) {
_SR_mode = true;
while (_VTMS_transition_disable_count > 0) {
while (_VTMS_transition_disable_for_all_count > 0) {
ml.wait(10); // Wait while there is any active jvmtiVTMSTransitionDisabler.
}
}
Atomic::inc(&_VTMS_transition_disable_count);
Atomic::inc(&_VTMS_transition_disable_for_all_count);
// Block while some mount/unmount transitions are in progress.
// Debug version fails and prints diagnostic information.
@ -300,23 +354,44 @@ JvmtiVTMSTransitionDisabler::disable_VTMS_transitions() {
#ifdef ASSERT
if (attempts == 0) {
print_info();
fatal("stuck in JvmtiVTMSTransitionDisabler::disable_VTMS_transitions");
fatal("stuck in JvmtiVTMSTransitionDisabler::VTMS_transition_disable");
}
#endif
}
// enable VTMS transitions for one virtual thread
void
JvmtiVTMSTransitionDisabler::enable_VTMS_transitions() {
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, Mutex::_no_safepoint_check_flag);
java_lang_Thread::dec_VTMS_transition_disable_count(vth());
Atomic::dec(&_VTMS_transition_disable_for_one_count);
if (_VTMS_transition_disable_for_one_count == 0 || _is_SR) {
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, Mutex::_no_safepoint_check_flag);
assert(_VTMS_transition_disable_count > 0, "VTMS_transition sanity check");
assert(_VTMS_transition_disable_for_all_count > 0, "VTMS_transition sanity check");
if (_is_SR) { // Disabler is suspender or resumer.
_SR_mode = false;
}
Atomic::dec(&_VTMS_transition_disable_count);
if (_VTMS_transition_disable_count == 0 || _is_SR) {
Atomic::dec(&_VTMS_transition_disable_for_all_count);
if (_VTMS_transition_disable_for_all_count == 0 || _is_SR) {
ml.notify_all();
}
#ifdef ASSERT
@ -335,17 +410,21 @@ JvmtiVTMSTransitionDisabler::start_VTMS_transition(jthread vthread, bool is_moun
// Avoid using MonitorLocker on performance critical path, use
// two-level synchronization with lock-free operations on counters.
Atomic::inc(&_VTMS_transition_count); // Try to enter VTMS transition section optmistically.
java_lang_Thread::set_is_in_VTMS_transition(vth(), true);
// 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_count > 0 ||
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 counter incrementation.
// It can cause an extra waiting cycle for VTMS transition disablers.
Atomic::dec(&_VTMS_transition_count);
java_lang_Thread::set_is_in_VTMS_transition(vth(), false);
while (true) {
ThreadBlockInVM tbivm(thread);
@ -353,7 +432,8 @@ JvmtiVTMSTransitionDisabler::start_VTMS_transition(jthread vthread, bool is_moun
// Do not allow suspends inside VTMS transitions.
// Block while transitions are disabled or there are suspend requests.
if (_VTMS_transition_disable_count > 0 ||
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)
) {
@ -365,6 +445,7 @@ JvmtiVTMSTransitionDisabler::start_VTMS_transition(jthread vthread, bool is_moun
continue; // ~ThreadBlockInVM has handshake-based suspend point.
}
Atomic::inc(&_VTMS_transition_count);
java_lang_Thread::set_is_in_VTMS_transition(vth(), true);
break;
}
}
@ -379,10 +460,6 @@ JvmtiVTMSTransitionDisabler::start_VTMS_transition(jthread vthread, bool is_moun
// Enter VTMS transition section.
assert(!thread->is_in_VTMS_transition(), "VTMS_transition sanity check");
thread->set_is_in_VTMS_transition(true);
JvmtiThreadState* vstate = java_lang_Thread::jvmti_thread_state(vth());
if (vstate != nullptr) {
vstate->set_is_in_VTMS_transition(true);
}
}
void
@ -393,15 +470,13 @@ JvmtiVTMSTransitionDisabler::finish_VTMS_transition(jthread vthread, bool is_mou
thread->set_is_in_VTMS_transition(false);
oop vt = JNIHandles::resolve_external_guard(vthread);
int64_t thread_id = java_lang_Thread::thread_id(vt);
JvmtiThreadState* vstate = java_lang_Thread::jvmti_thread_state(vt);
if (vstate != nullptr) {
vstate->set_is_in_VTMS_transition(false);
}
java_lang_Thread::set_is_in_VTMS_transition(vt, false);
Atomic::dec(&_VTMS_transition_count);
// Unblock waiting VTMS transition disablers.
if (_VTMS_transition_disable_count > 0) {
if (_VTMS_transition_disable_for_one_count > 0 ||
_VTMS_transition_disable_for_all_count > 0) {
MonitorLocker ml(JvmtiVTMSTransition_lock, Mutex::_no_safepoint_check_flag);
ml.notify_all();
}

@ -79,19 +79,24 @@ class JvmtiEnvThreadStateIterator : public StackObj {
//
class JvmtiVTMSTransitionDisabler {
private:
static volatile bool _SR_mode; // there is an active suspender or resumer
static volatile int _VTMS_transition_count; // current number of VTMS transitions
static volatile int _VTMS_transition_disable_count; // VTMS transitions are disabled while it is non-zero
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 _VTMS_transition_count; // current number of VTMS transitions
bool _is_SR; // is suspender or resumer
void disable_VTMS_transitions();
void enable_VTMS_transitions();
bool _is_SR; // is suspender or resumer
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:
// parameter is_SR: suspender or resumer
JvmtiVTMSTransitionDisabler(bool is_SR = false);
JvmtiVTMSTransitionDisabler(jthread thread);
~JvmtiVTMSTransitionDisabler();
static void start_VTMS_transition(jthread vthread, bool is_mount);
@ -153,7 +158,6 @@ class JvmtiThreadState : public CHeapObj<mtInternal> {
OopHandle _thread_oop_h;
// Jvmti Events that cannot be posted in their current context.
JvmtiDeferredEventQueue* _jvmti_event_queue;
bool _is_in_VTMS_transition; // saved JavaThread.is_in_VTMS_transition()
bool _is_virtual; // state belongs to a virtual thread
bool _hide_single_stepping;
bool _pending_interp_only_mode;
@ -270,9 +274,6 @@ class JvmtiThreadState : public CHeapObj<mtInternal> {
void set_thread(JavaThread* thread);
oop get_thread_oop();
// The JavaThread is_in_VTMS_transition() bit saved at unmount to restore at mount.
inline bool is_in_VTMS_transition() { return _is_in_VTMS_transition; }
inline void set_is_in_VTMS_transition(bool val) { _is_in_VTMS_transition = val; }
inline bool is_virtual() { return _is_virtual; } // the _thread is virtual
inline bool is_exception_detected() { return _exception_state == ES_DETECTED; }