8343132: Remove temporary transitions from Virtual thread implementation

Reviewed-by: dholmes, sspitsyn, pchilanomate
This commit is contained in:
Alan Bateman 2024-10-31 08:53:19 +00:00
parent 2f1ba5ef09
commit dee0982c60
16 changed files with 184 additions and 263 deletions

View File

@ -611,7 +611,6 @@ class methodHandle;
do_intrinsic(_notifyJvmtiVThreadEnd, java_lang_VirtualThread, notifyJvmtiEnd_name, void_method_signature, F_RN) \
do_intrinsic(_notifyJvmtiVThreadMount, java_lang_VirtualThread, notifyJvmtiMount_name, bool_void_signature, F_RN) \
do_intrinsic(_notifyJvmtiVThreadUnmount, java_lang_VirtualThread, notifyJvmtiUnmount_name, bool_void_signature, F_RN) \
do_intrinsic(_notifyJvmtiVThreadHideFrames, java_lang_VirtualThread, notifyJvmtiHideFrames_name, bool_void_signature, F_SN) \
do_intrinsic(_notifyJvmtiVThreadDisableSuspend, java_lang_VirtualThread, notifyJvmtiDisableSuspend_name, bool_void_signature, F_SN) \
\
/* support for UnsafeConstants */ \

View File

@ -398,7 +398,6 @@ class SerializeClosure;
template(notifyJvmtiEnd_name, "notifyJvmtiEnd") \
template(notifyJvmtiMount_name, "notifyJvmtiMount") \
template(notifyJvmtiUnmount_name, "notifyJvmtiUnmount") \
template(notifyJvmtiHideFrames_name, "notifyJvmtiHideFrames") \
template(notifyJvmtiDisableSuspend_name, "notifyJvmtiDisableSuspend") \
template(doYield_name, "doYield") \
template(enter_name, "enter") \

View File

@ -1142,9 +1142,6 @@ JVM_VirtualThreadMount(JNIEnv* env, jobject vthread, jboolean hide);
JNIEXPORT void JNICALL
JVM_VirtualThreadUnmount(JNIEnv* env, jobject vthread, jboolean hide);
JNIEXPORT void JNICALL
JVM_VirtualThreadHideFrames(JNIEnv* env, jclass clazz, jboolean hide);
JNIEXPORT void JNICALL
JVM_VirtualThreadDisableSuspend(JNIEnv* env, jclass clazz, jboolean enter);

View File

@ -248,7 +248,6 @@
nonstatic_field(JavaThread, _cont_entry, ContinuationEntry*) \
nonstatic_field(JavaThread, _unlocked_inflated_monitor, ObjectMonitor*) \
JVMTI_ONLY(nonstatic_field(JavaThread, _is_in_VTMS_transition, bool)) \
JVMTI_ONLY(nonstatic_field(JavaThread, _is_in_tmp_VTMS_transition, bool)) \
JVMTI_ONLY(nonstatic_field(JavaThread, _is_disable_suspend, bool)) \
\
nonstatic_field(ContinuationEntry, _pin_count, uint32_t) \

View File

@ -841,7 +841,6 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) {
case vmIntrinsics::_notifyJvmtiVThreadEnd:
case vmIntrinsics::_notifyJvmtiVThreadMount:
case vmIntrinsics::_notifyJvmtiVThreadUnmount:
case vmIntrinsics::_notifyJvmtiVThreadHideFrames:
case vmIntrinsics::_notifyJvmtiVThreadDisableSuspend:
#endif
break;

View File

@ -495,7 +495,6 @@ bool LibraryCallKit::try_to_inline(int predicate) {
"notifyJvmtiMount", false, false);
case vmIntrinsics::_notifyJvmtiVThreadUnmount: return inline_native_notify_jvmti_funcs(CAST_FROM_FN_PTR(address, OptoRuntime::notify_jvmti_vthread_unmount()),
"notifyJvmtiUnmount", false, false);
case vmIntrinsics::_notifyJvmtiVThreadHideFrames: return inline_native_notify_jvmti_hide();
case vmIntrinsics::_notifyJvmtiVThreadDisableSuspend: return inline_native_notify_jvmti_sync();
#endif
@ -2975,29 +2974,6 @@ bool LibraryCallKit::inline_native_notify_jvmti_funcs(address funcAddr, const ch
return true;
}
// Always update the temporary VTMS transition bit.
bool LibraryCallKit::inline_native_notify_jvmti_hide() {
if (!DoJVMTIVirtualThreadTransitions) {
return true;
}
IdealKit ideal(this);
{
// unconditionally update the temporary VTMS transition bit in current JavaThread
Node* thread = ideal.thread();
Node* hide = _gvn.transform(argument(0)); // hide argument for temporary VTMS transition notification
Node* addr = basic_plus_adr(thread, in_bytes(JavaThread::is_in_tmp_VTMS_transition_offset()));
const TypePtr *addr_type = _gvn.type(addr)->isa_ptr();
sync_kit(ideal);
access_store_at(nullptr, addr, addr_type, hide, _gvn.type(hide), T_BOOLEAN, IN_NATIVE | MO_UNORDERED);
ideal.sync_kit(this);
}
final_sync(ideal);
return true;
}
// Always update the is_disable_suspend bit.
bool LibraryCallKit::inline_native_notify_jvmti_sync() {
if (!DoJVMTIVirtualThreadTransitions) {

View File

@ -3941,19 +3941,6 @@ JVM_ENTRY(void, JVM_VirtualThreadUnmount(JNIEnv* env, jobject vthread, jboolean
#endif
JVM_END
// Always update the temporary VTMS transition bit.
JVM_ENTRY(void, JVM_VirtualThreadHideFrames(JNIEnv* env, jclass clazz, jboolean hide))
#if INCLUDE_JVMTI
if (!DoJVMTIVirtualThreadTransitions) {
assert(!JvmtiExport::can_support_virtual_threads(), "sanity check");
return;
}
assert(!thread->is_in_VTMS_transition(), "sanity check");
assert(thread->is_in_tmp_VTMS_transition() != (bool)hide, "sanity check");
thread->toggle_is_in_tmp_VTMS_transition();
#endif
JVM_END
// Notification from VirtualThread about disabling JVMTI Suspend in a sync critical section.
// Needed to avoid deadlocks with JVMTI suspend mechanism.
JVM_ENTRY(void, JVM_VirtualThreadDisableSuspend(JNIEnv* env, jclass clazz, jboolean enter))

View File

@ -929,7 +929,7 @@ class JvmtiClassFileLoadHookPoster : public StackObj {
_cached_class_file_ptr = cache_ptr;
_has_been_modified = false;
assert(!_thread->is_in_any_VTMS_transition(), "CFLH events are not allowed in any VTMS transition");
assert(!_thread->is_in_VTMS_transition(), "CFLH events are not allowed in VTMS transition");
_state = JvmtiExport::get_jvmti_thread_state(_thread);
if (_state != nullptr) {
@ -1091,8 +1091,8 @@ bool JvmtiExport::post_class_file_load_hook(Symbol* h_name,
return false;
}
if (JavaThread::current()->is_in_any_VTMS_transition()) {
return false; // no events should be posted if thread is in any VTMS transition
if (JavaThread::current()->is_in_VTMS_transition()) {
return false; // no events should be posted if thread is in VTMS transition
}
JvmtiClassFileLoadHookPoster poster(h_name, class_loader,
@ -1228,8 +1228,8 @@ void JvmtiExport::post_raw_breakpoint(JavaThread *thread, Method* method, addres
if (state == nullptr) {
return;
}
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_BREAKPOINT, ("[%s] Trg Breakpoint triggered",
@ -1368,8 +1368,8 @@ void JvmtiExport::post_class_load(JavaThread *thread, Klass* klass) {
if (state == nullptr) {
return;
}
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_CLASS_LOAD, ("[%s] Trg Class Load triggered",
@ -1405,8 +1405,8 @@ void JvmtiExport::post_class_prepare(JavaThread *thread, Klass* klass) {
if (state == nullptr) {
return;
}
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_CLASS_PREPARE, ("[%s] Trg Class Prepare triggered",
@ -1743,8 +1743,8 @@ void JvmtiExport::post_object_free(JvmtiEnv* env, GrowableArray<jlong>* objects)
assert(objects != nullptr, "Nothing to post");
JavaThread *javaThread = JavaThread::current();
if (javaThread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (javaThread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
if (!env->is_enabled(JVMTI_EVENT_OBJECT_FREE)) {
return; // the event type has been already disabled
@ -1767,8 +1767,8 @@ void JvmtiExport::post_resource_exhausted(jint resource_exhausted_flags, const c
JavaThread *thread = JavaThread::current();
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
log_error(jvmti)("Posting Resource Exhausted event: %s",
@ -1810,8 +1810,8 @@ void JvmtiExport::post_method_entry(JavaThread *thread, Method* method, frame cu
// for any thread that actually wants method entry, interp_only_mode is set
return;
}
if (mh->jvmti_mount_transition() || thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (mh->jvmti_mount_transition() || thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_METHOD_ENTRY, ("[%s] Trg Method Entry triggered %s.%s",
JvmtiTrace::safe_get_thread_name(thread),
@ -1902,8 +1902,8 @@ void JvmtiExport::post_method_exit_inner(JavaThread* thread,
bool exception_exit,
frame current_frame,
jvalue& value) {
if (mh->jvmti_mount_transition() || thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (mh->jvmti_mount_transition() || thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_METHOD_EXIT, ("[%s] Trg Method Exit triggered %s.%s",
@ -1978,8 +1978,8 @@ void JvmtiExport::post_single_step(JavaThread *thread, Method* method, address l
if (state == nullptr) {
return;
}
if (mh->jvmti_mount_transition() || thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (mh->jvmti_mount_transition() || thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
JvmtiEnvThreadStateIterator it(state);
@ -2020,8 +2020,8 @@ void JvmtiExport::post_exception_throw(JavaThread *thread, Method* method, addre
if (state == nullptr) {
return;
}
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_EXCEPTION, ("[%s] Trg Exception thrown triggered",
@ -2142,8 +2142,8 @@ void JvmtiExport::notice_unwind_due_to_exception(JavaThread *thread, Method* met
assert(!state->is_exception_caught(), "exception must not be caught yet.");
state->set_exception_caught();
if (mh->jvmti_mount_transition() || thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (mh->jvmti_mount_transition() || thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
JvmtiEnvThreadStateIterator it(state);
for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) {
@ -2188,8 +2188,8 @@ void JvmtiExport::post_field_access_by_jni(JavaThread *thread, oop obj,
// function don't make the call unless there is a Java context.
assert(thread->has_last_Java_frame(), "must be called with a Java context");
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
ResourceMark rm;
@ -2224,8 +2224,8 @@ void JvmtiExport::post_field_access(JavaThread *thread, Method* method,
if (state == nullptr) {
return;
}
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_FIELD_ACCESS, ("[%s] Trg Field Access event triggered",
@ -2274,8 +2274,8 @@ void JvmtiExport::post_field_modification_by_jni(JavaThread *thread, oop obj,
// function don't make the call unless there is a Java context.
assert(thread->has_last_Java_frame(), "must be called with Java context");
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
ResourceMark rm;
@ -2305,8 +2305,8 @@ void JvmtiExport::post_raw_field_modification(JavaThread *thread, Method* method
address location, Klass* field_klass, Handle object, jfieldID field,
char sig_type, jvalue *value) {
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
if (sig_type == JVM_SIGNATURE_INT || sig_type == JVM_SIGNATURE_BOOLEAN ||
@ -2380,8 +2380,8 @@ void JvmtiExport::post_field_modification(JavaThread *thread, Method* method,
if (state == nullptr) {
return;
}
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_FIELD_MODIFICATION,
@ -2419,8 +2419,8 @@ void JvmtiExport::post_native_method_bind(Method* method, address* function_ptr)
HandleMark hm(thread);
methodHandle mh(thread, method);
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_NATIVE_METHOD_BIND, ("[%s] Trg Native Method Bind event triggered",
JvmtiTrace::safe_get_thread_name(thread)));
@ -2493,7 +2493,7 @@ void JvmtiExport::post_compiled_method_load(nmethod *nm) {
}
JavaThread* thread = JavaThread::current();
assert(!thread->is_in_any_VTMS_transition(), "compiled method load events are not allowed in any VTMS transition");
assert(!thread->is_in_VTMS_transition(), "compiled method load events are not allowed in VTMS transition");
EVT_TRIG_TRACE(JVMTI_EVENT_COMPILED_METHOD_LOAD,
("[%s] method compile load event triggered",
@ -2516,7 +2516,7 @@ void JvmtiExport::post_compiled_method_load(JvmtiEnv* env, nmethod *nm) {
}
JavaThread* thread = JavaThread::current();
assert(!thread->is_in_any_VTMS_transition(), "compiled method load events are not allowed in any VTMS transition");
assert(!thread->is_in_VTMS_transition(), "compiled method load events are not allowed in VTMS transition");
EVT_TRACE(JVMTI_EVENT_COMPILED_METHOD_LOAD,
("[%s] method compile load event sent %s.%s ",
@ -2541,7 +2541,7 @@ void JvmtiExport::post_dynamic_code_generated_internal(const char *name, const v
JavaThread* thread = JavaThread::current();
assert(!thread->is_in_any_VTMS_transition(), "dynamic code generated events are not allowed in any VTMS transition");
assert(!thread->is_in_VTMS_transition(), "dynamic code generated events are not allowed in VTMS transition");
// In theory everyone coming thru here is in_vm but we need to be certain
// because a callee will do a vm->native transition
@ -2589,7 +2589,7 @@ void JvmtiExport::post_dynamic_code_generated(JvmtiEnv* env, const char *name,
{
JavaThread* thread = JavaThread::current();
assert(!thread->is_in_any_VTMS_transition(), "dynamic code generated events are not allowed in any VTMS transition");
assert(!thread->is_in_VTMS_transition(), "dynamic code generated events are not allowed in VTMS transition");
EVT_TRIG_TRACE(JVMTI_EVENT_DYNAMIC_CODE_GENERATED,
("[%s] dynamic code generated event triggered (by GenerateEvents)",
@ -2744,8 +2744,8 @@ void JvmtiExport::post_monitor_contended_enter(JavaThread *thread, ObjectMonitor
if (state == nullptr) {
return;
}
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_CONTENDED_ENTER,
@ -2777,8 +2777,8 @@ void JvmtiExport::post_monitor_contended_entered(JavaThread *thread, ObjectMonit
if (state == nullptr) {
return;
}
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_CONTENDED_ENTERED,
@ -2811,8 +2811,8 @@ void JvmtiExport::post_monitor_wait(JavaThread *thread, oop object,
if (state == nullptr) {
return;
}
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_WAIT,
@ -2845,8 +2845,8 @@ void JvmtiExport::post_monitor_waited(JavaThread *thread, ObjectMonitor *obj_mnt
if (state == nullptr) {
return;
}
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_WAITED,
@ -2874,8 +2874,8 @@ void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) {
if (object == nullptr) {
return;
}
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
HandleMark hm(thread);
Handle h(thread, object);
@ -2911,8 +2911,8 @@ void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) {
if (object == nullptr) {
return;
}
if (thread->is_in_any_VTMS_transition()) {
return; // no events should be posted if thread is in any VTMS transition
if (thread->is_in_VTMS_transition()) {
return; // no events should be posted if thread is in VTMS transition
}
EVT_TRIG_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,

View File

@ -353,7 +353,6 @@ JvmtiVTMSTransitionDisabler::VTMS_transition_disable_for_all() {
{
MonitorLocker ml(JvmtiVTMSTransition_lock);
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
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.
@ -567,7 +566,6 @@ JvmtiVTMSTransitionDisabler::VTMS_vthread_start(jobject vthread) {
JavaThread* thread = JavaThread::current();
assert(!thread->is_in_VTMS_transition(), "sanity check");
assert(!thread->is_in_tmp_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,
@ -593,7 +591,6 @@ JvmtiVTMSTransitionDisabler::VTMS_vthread_end(jobject vthread) {
JavaThread* thread = JavaThread::current();
assert(!thread->is_in_VTMS_transition(), "sanity check");
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
// post VirtualThreadUnmount event before VirtualThreadEnd
if (JvmtiExport::should_post_vthread_unmount()) {
@ -638,7 +635,6 @@ JvmtiVTMSTransitionDisabler::VTMS_vthread_unmount(jobject vthread, bool hide) {
void
JvmtiVTMSTransitionDisabler::VTMS_mount_begin(jobject vthread) {
JavaThread* thread = JavaThread::current();
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
assert(!thread->is_in_VTMS_transition(), "sanity check");
start_VTMS_transition(vthread, /* is_mount */ true);
}
@ -651,7 +647,6 @@ JvmtiVTMSTransitionDisabler::VTMS_mount_end(jobject vthread) {
thread->rebind_to_jvmti_thread_state_of(vt);
assert(thread->is_in_VTMS_transition(), "sanity check");
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
finish_VTMS_transition(vthread, /* is_mount */ true);
}
@ -659,7 +654,6 @@ void
JvmtiVTMSTransitionDisabler::VTMS_unmount_begin(jobject vthread, bool last_unmount) {
JavaThread* thread = JavaThread::current();
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
assert(!thread->is_in_VTMS_transition(), "sanity check");
start_VTMS_transition(vthread, /* is_mount */ false);
@ -672,7 +666,6 @@ void
JvmtiVTMSTransitionDisabler::VTMS_unmount_end(jobject vthread) {
JavaThread* thread = JavaThread::current();
assert(thread->is_in_VTMS_transition(), "sanity check");
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
finish_VTMS_transition(vthread, /* is_mount */ false);
}

View File

@ -446,7 +446,6 @@ JavaThread::JavaThread(MemTag mem_tag) :
#if INCLUDE_JVMTI
_carrier_thread_suspended(false),
_is_in_VTMS_transition(false),
_is_in_tmp_VTMS_transition(false),
_is_disable_suspend(false),
_VTMS_transition_mark(false),
#ifdef ASSERT

View File

@ -311,7 +311,6 @@ class JavaThread: public Thread {
#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_in_tmp_VTMS_transition; // thread is in temporary virtual thread mount state transition
bool _is_disable_suspend; // JVMTI suspend is temporarily disabled; used on current thread only
bool _VTMS_transition_mark; // used for sync between VTMS transitions and disablers
#ifdef ASSERT
@ -675,11 +674,7 @@ private:
}
bool is_in_VTMS_transition() const { return _is_in_VTMS_transition; }
bool is_in_tmp_VTMS_transition() const { return _is_in_tmp_VTMS_transition; }
bool is_in_any_VTMS_transition() const { return _is_in_VTMS_transition || _is_in_tmp_VTMS_transition; }
void set_is_in_VTMS_transition(bool val);
void toggle_is_in_tmp_VTMS_transition() { _is_in_tmp_VTMS_transition = !_is_in_tmp_VTMS_transition; };
bool is_disable_suspend() const { return _is_disable_suspend; }
void toggle_is_disable_suspend() { _is_disable_suspend = !_is_disable_suspend; };
@ -851,7 +846,6 @@ private:
#if INCLUDE_JVMTI
static ByteSize is_in_VTMS_transition_offset() { return byte_offset_of(JavaThread, _is_in_VTMS_transition); }
static ByteSize is_in_tmp_VTMS_transition_offset() { return byte_offset_of(JavaThread, _is_in_tmp_VTMS_transition); }
static ByteSize is_disable_suspend_offset() { return byte_offset_of(JavaThread, _is_disable_suspend); }
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2024, 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
@ -232,8 +232,8 @@ public class ThreadLocal<T> {
if (this instanceof TerminatingThreadLocal<?> ttl) {
TerminatingThreadLocal.register(ttl);
}
if (TRACE_VTHREAD_LOCALS) {
dumpStackIfVirtualThread();
if (TRACE_VTHREAD_LOCALS && t == Thread.currentThread() && t.isVirtual()) {
printStackTrace();
}
return value;
}
@ -249,8 +249,8 @@ public class ThreadLocal<T> {
*/
public void set(T value) {
set(Thread.currentThread(), value);
if (TRACE_VTHREAD_LOCALS) {
dumpStackIfVirtualThread();
if (TRACE_VTHREAD_LOCALS && Thread.currentThread().isVirtual()) {
printStackTrace();
}
}
@ -799,7 +799,6 @@ public class ThreadLocal<T> {
}
}
/**
* Reads the value of the jdk.traceVirtualThreadLocals property to determine if
* a stack trace should be printed when a virtual thread sets a thread local.
@ -811,30 +810,28 @@ public class ThreadLocal<T> {
}
/**
* Print a stack trace if the current thread is a virtual thread.
* Print the stack trace of the current thread, skipping the printStackTrace frame.
* A thread local is used to detect reentrancy as the printing may itself use
* thread locals.
*/
static void dumpStackIfVirtualThread() {
if (Thread.currentThread() instanceof VirtualThread vthread) {
private void printStackTrace() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map.getEntry(DUMPING_STACK) == null) {
map.set(DUMPING_STACK, true);
try {
var stack = StackWalkerHolder.STACK_WALKER.walk(s ->
var stack = StackWalker.getInstance().walk(s ->
s.skip(1) // skip caller
.collect(Collectors.toList()));
// switch to carrier thread to avoid recursive use of thread-locals
vthread.executeOnCarrierThread(() -> {
System.out.println(vthread);
for (StackWalker.StackFrame frame : stack) {
System.out.format(" %s%n", frame.toStackTraceElement());
}
return null;
});
} catch (Exception e) {
throw new InternalError(e);
System.out.println(t);
for (StackWalker.StackFrame frame : stack) {
System.out.format(" %s%n", frame.toStackTraceElement());
}
} finally {
map.remove(DUMPING_STACK);
}
}
}
private static class StackWalkerHolder {
static final StackWalker STACK_WALKER = StackWalker.getInstance();
}
private static final ThreadLocal<Boolean> DUMPING_STACK = new ThreadLocal<>();
}

View File

@ -28,7 +28,6 @@ import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@ -137,13 +136,18 @@ final class VirtualThread extends BaseVirtualThread {
// parking permit
private volatile boolean parkPermit;
// timeout for timed-park, in nanoseconds, only accessed on current/carrier thread
private long parkTimeout;
// timer task for timed-park, only accessed on current/carrier thread
private Future<?> timeoutTask;
// carrier thread when mounted, accessed by VM
private volatile Thread carrierThread;
// termination object when joining, created lazily if needed
private volatile CountDownLatch termination;
/**
* Returns the default scheduler.
*/
@ -246,8 +250,10 @@ final class VirtualThread extends BaseVirtualThread {
if (!compareAndSetState(initialState, RUNNING)) {
return;
}
// consume parking permit when continuing after parking
// consume permit when continuing after parking. If continuing after a
// timed-park then the timeout task is cancelled.
if (initialState == UNPARKED) {
cancelTimeoutTask();
setParkPermit(false);
}
} else {
@ -268,6 +274,17 @@ final class VirtualThread extends BaseVirtualThread {
}
}
/**
* Cancel timeout task when continuing after a timed-park. The
* timeout task may be executing, or may have already completed.
*/
private void cancelTimeoutTask() {
if (timeoutTask != null) {
timeoutTask.cancel(false);
timeoutTask = null;
}
}
/**
* Submits the runContinuation task to the scheduler. For the default scheduler,
* and calling it on a worker thread, the task will be pushed to the local queue,
@ -276,23 +293,21 @@ final class VirtualThread extends BaseVirtualThread {
* @param retryOnOOME true to retry indefinitely if OutOfMemoryError is thrown
* @throws RejectedExecutionException
*/
@ChangesCurrentThread
private void submitRunContinuation(Executor scheduler, boolean retryOnOOME) {
boolean done = false;
while (!done) {
try {
// The scheduler's execute method is invoked in the context of the
// carrier thread. For the default scheduler this ensures that the
// current thread is a ForkJoinWorkerThread so the task will be pushed
// to the local queue. For other schedulers, it avoids deadlock that
// would arise due to platform and virtual threads contending for a
// lock on the scheduler's submission queue.
if (currentThread() instanceof VirtualThread vthread) {
vthread.switchToCarrierThread();
// Pin the continuation to prevent the virtual thread from unmounting
// when submitting a task. For the default scheduler this ensures that
// the carrier doesn't change when pushing a task. For other schedulers
// it avoids deadlock that could arise due to carriers and virtual
// threads contending for a lock.
if (currentThread().isVirtual()) {
Continuation.pin();
try {
scheduler.execute(runContinuation);
} finally {
switchToVirtualThread(vthread);
Continuation.unpin();
}
} else {
scheduler.execute(runContinuation);
@ -311,24 +326,6 @@ final class VirtualThread extends BaseVirtualThread {
}
}
/**
* Submits the runContinuation task to given scheduler with a lazy submit.
* If OutOfMemoryError is thrown then the submit will be retried until it succeeds.
* @throws RejectedExecutionException
* @see ForkJoinPool#lazySubmit(ForkJoinTask)
*/
private void lazySubmitRunContinuation(ForkJoinPool pool) {
assert Thread.currentThread() instanceof CarrierThread;
try {
pool.lazySubmit(ForkJoinTask.adapt(runContinuation));
} catch (RejectedExecutionException ree) {
submitFailed(ree);
throw ree;
} catch (OutOfMemoryError e) {
submitRunContinuation(pool, true);
}
}
/**
* Submits the runContinuation task to the given scheduler as an external submit.
* If OutOfMemoryError is thrown then the submit will be retried until it succeeds.
@ -358,6 +355,30 @@ final class VirtualThread extends BaseVirtualThread {
submitRunContinuation(scheduler, true);
}
/**
* Lazy submit the runContinuation task if invoked on a carrier thread and its local
* queue is empty. If not empty, or invoked by another thread, then this method works
* like submitRunContinuation and just submits the task to the scheduler.
* If OutOfMemoryError is thrown then the submit will be retried until it succeeds.
* @throws RejectedExecutionException
* @see ForkJoinPool#lazySubmit(ForkJoinTask)
*/
private void lazySubmitRunContinuation() {
if (currentThread() instanceof CarrierThread ct && ct.getQueuedTaskCount() == 0) {
ForkJoinPool pool = ct.getPool();
try {
pool.lazySubmit(ForkJoinTask.adapt(runContinuation));
} catch (RejectedExecutionException ree) {
submitFailed(ree);
throw ree;
} catch (OutOfMemoryError e) {
submitRunContinuation();
}
} else {
submitRunContinuation();
}
}
/**
* Submits the runContinuation task to the scheduler. For the default scheduler, and
* calling it a virtual thread that uses the default scheduler, the task will be
@ -474,45 +495,6 @@ final class VirtualThread extends BaseVirtualThread {
notifyJvmtiUnmount(/*hide*/false);
}
/**
* Sets the current thread to the current carrier thread.
*/
@ChangesCurrentThread
@JvmtiMountTransition
private void switchToCarrierThread() {
notifyJvmtiHideFrames(true);
Thread carrier = this.carrierThread;
assert Thread.currentThread() == this
&& carrier == Thread.currentCarrierThread();
carrier.setCurrentThread(carrier);
}
/**
* Sets the current thread to the given virtual thread.
*/
@ChangesCurrentThread
@JvmtiMountTransition
private static void switchToVirtualThread(VirtualThread vthread) {
Thread carrier = vthread.carrierThread;
assert carrier == Thread.currentCarrierThread();
carrier.setCurrentThread(vthread);
notifyJvmtiHideFrames(false);
}
/**
* Executes the given value returning task on the current carrier thread.
*/
@ChangesCurrentThread
<V> V executeOnCarrierThread(Callable<V> task) throws Exception {
assert Thread.currentThread() == this;
switchToCarrierThread();
try {
return task.call();
} finally {
switchToVirtualThread(this);
}
}
/**
* Invokes Continuation.yield, notifying JVMTI (if enabled) to hide frames until
* the continuation continues.
@ -528,9 +510,8 @@ final class VirtualThread extends BaseVirtualThread {
}
/**
* Invoked after the continuation yields. If parking then it sets the state
* and also re-submits the task to continue if unparked while parking.
* If yielding due to Thread.yield then it just submits the task to continue.
* Invoked in the context of the carrier thread after the Continuation yields when
* parking or Thread.yield.
*/
private void afterYield() {
assert carrierThread == null;
@ -544,17 +525,20 @@ final class VirtualThread extends BaseVirtualThread {
// LockSupport.park/parkNanos
if (s == PARKING || s == TIMED_PARKING) {
int newState = (s == PARKING) ? PARKED : TIMED_PARKED;
setState(newState);
int newState;
if (s == PARKING) {
setState(newState = PARKED);
} else {
// schedule unpark
assert parkTimeout > 0;
timeoutTask = schedule(this::unpark, parkTimeout, NANOSECONDS);
setState(newState = TIMED_PARKED);
}
// may have been unparked while parking
if (parkPermit && compareAndSetState(newState, UNPARKED)) {
// lazy submit to continue on the current carrier if possible
if (currentThread() instanceof CarrierThread ct && ct.getQueuedTaskCount() == 0) {
lazySubmitRunContinuation(ct.getPool());
} else {
submitRunContinuation();
}
// lazy submit if local queue is empty
lazySubmitRunContinuation();
}
return;
}
@ -672,7 +656,9 @@ final class VirtualThread extends BaseVirtualThread {
boolean yielded = false;
setState(PARKING);
try {
yielded = yieldContinuation(); // may throw
yielded = yieldContinuation();
} catch (OutOfMemoryError e) {
// park on carrier
} finally {
assert (Thread.currentThread() == this) && (yielded == (state() == RUNNING));
if (!yielded) {
@ -707,21 +693,23 @@ final class VirtualThread extends BaseVirtualThread {
if (nanos > 0) {
long startTime = System.nanoTime();
// park the thread, afterYield will schedule the thread to unpark
boolean yielded = false;
Future<?> unparker = scheduleUnpark(nanos); // may throw OOME
setParkTimeout(nanos);
setState(TIMED_PARKING);
try {
yielded = yieldContinuation(); // may throw
yielded = yieldContinuation();
} catch (OutOfMemoryError e) {
// park on carrier
} finally {
assert (Thread.currentThread() == this) && (yielded == (state() == RUNNING));
if (!yielded) {
assert state() == TIMED_PARKING;
setState(RUNNING);
}
cancel(unparker);
}
// park on carrier thread for remaining time when pinned
// park on carrier thread for remaining time when pinned (or OOME)
if (!yielded) {
long remainingNanos = nanos - (System.nanoTime() - startTime);
parkOnCarrierThread(true, remainingNanos);
@ -772,38 +760,6 @@ final class VirtualThread extends BaseVirtualThread {
}
}
/**
* Schedule this virtual thread to be unparked after a given delay.
*/
@ChangesCurrentThread
private Future<?> scheduleUnpark(long nanos) {
assert Thread.currentThread() == this;
// need to switch to current carrier thread to avoid nested parking
switchToCarrierThread();
try {
return schedule(this::unpark, nanos, NANOSECONDS);
} finally {
switchToVirtualThread(this);
}
}
/**
* Cancels a task if it has not completed.
*/
@ChangesCurrentThread
private void cancel(Future<?> future) {
assert Thread.currentThread() == this;
if (!future.isDone()) {
// need to switch to current carrier thread to avoid nested parking
switchToCarrierThread();
try {
future.cancel(false);
} finally {
switchToVirtualThread(this);
}
}
}
/**
* Re-enables this virtual thread for scheduling. If this virtual thread is parked
* then its task is scheduled to continue, otherwise its next call to {@code park} or
@ -1041,10 +997,10 @@ final class VirtualThread extends BaseVirtualThread {
return Thread.State.RUNNABLE;
case PARKED:
case PINNED:
return State.WAITING;
return Thread.State.WAITING;
case TIMED_PARKED:
case TIMED_PINNED:
return State.TIMED_WAITING;
return Thread.State.TIMED_WAITING;
case TERMINATED:
return Thread.State.TERMINATED;
default:
@ -1263,6 +1219,10 @@ final class VirtualThread extends BaseVirtualThread {
}
}
private void setParkTimeout(long timeout) {
parkTimeout = timeout;
}
private void setCarrierThread(Thread carrier) {
// U.putReferenceRelease(this, CARRIER_THREAD, carrier);
this.carrierThread = carrier;
@ -1286,10 +1246,6 @@ final class VirtualThread extends BaseVirtualThread {
@JvmtiMountTransition
private native void notifyJvmtiUnmount(boolean hide);
@IntrinsicCandidate
@JvmtiMountTransition
private static native void notifyJvmtiHideFrames(boolean hide);
@IntrinsicCandidate
private static native void notifyJvmtiDisableSuspend(boolean enter);

View File

@ -51,6 +51,7 @@ import java.util.Objects;
import java.util.function.Predicate;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.LockSupport;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.JavaUtilConcurrentFJPAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
@ -2632,7 +2633,7 @@ public class ForkJoinPool extends AbstractExecutorService {
private void poolSubmit(boolean signalIfEmpty, ForkJoinTask<?> task) {
Thread t; ForkJoinWorkerThread wt; WorkQueue q; boolean internal;
if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) &&
if (((t = JLA.currentCarrierThread()) instanceof ForkJoinWorkerThread) &&
(wt = (ForkJoinWorkerThread)t).pool == this) {
internal = true;
q = wt.workQueue;
@ -2643,6 +2644,7 @@ public class ForkJoinPool extends AbstractExecutorService {
}
q.push(task, signalIfEmpty ? this : null, internal);
}
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
/**
* Returns queue for an external submission, bypassing call to

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, 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
@ -36,7 +36,6 @@ static JNINativeMethod methods[] = {
{ "notifyJvmtiEnd", "()V", (void *)&JVM_VirtualThreadEnd },
{ "notifyJvmtiMount", "(Z)V", (void *)&JVM_VirtualThreadMount },
{ "notifyJvmtiUnmount", "(Z)V", (void *)&JVM_VirtualThreadUnmount },
{ "notifyJvmtiHideFrames", "(Z)V", (void *)&JVM_VirtualThreadHideFrames },
{ "notifyJvmtiDisableSuspend", "(Z)V", (void *)&JVM_VirtualThreadDisableSuspend },
};

View File

@ -30,14 +30,16 @@
* @run main ParkWithFixedThreadPool
*/
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.locks.LockSupport;
import jdk.test.lib.thread.VThreadScheduler;
public class ParkWithFixedThreadPool {
public static void main(String[] args) throws Exception {
try (ExecutorService scheduler = Executors.newFixedThreadPool(8)) {
try (var scheduler = new Scheduler(8)) {
int vthreadCount = 300;
Thread[] vthreads = new Thread[vthreadCount];
Runnable target = new Runnable() {
@ -74,4 +76,27 @@ public class ParkWithFixedThreadPool {
}
}
}
static class Scheduler implements Executor, AutoCloseable {
private final ExecutorService pool;
Scheduler(int poolSize) {
pool = Executors.newFixedThreadPool(poolSize);
}
@Override
public void execute(Runnable task) {
try {
pool.execute(task);
} finally {
// ExecutorService::execute may consume parking permit
LockSupport.unpark(Thread.currentThread());
}
}
@Override
public void close() {
pool.close();
}
}
}