From dee0982c603b389148a2e615c10c1276c3c589ae Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Thu, 31 Oct 2024 08:53:19 +0000 Subject: [PATCH] 8343132: Remove temporary transitions from Virtual thread implementation Reviewed-by: dholmes, sspitsyn, pchilanomate --- src/hotspot/share/classfile/vmIntrinsics.hpp | 1 - src/hotspot/share/classfile/vmSymbols.hpp | 1 - src/hotspot/share/include/jvm.h | 3 - src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 1 - src/hotspot/share/opto/c2compiler.cpp | 1 - src/hotspot/share/opto/library_call.cpp | 24 --- src/hotspot/share/prims/jvm.cpp | 13 -- src/hotspot/share/prims/jvmtiExport.cpp | 102 ++++----- src/hotspot/share/prims/jvmtiThreadState.cpp | 7 - src/hotspot/share/runtime/javaThread.cpp | 1 - src/hotspot/share/runtime/javaThread.hpp | 6 - .../share/classes/java/lang/ThreadLocal.java | 45 ++-- .../classes/java/lang/VirtualThread.java | 204 +++++++----------- .../java/util/concurrent/ForkJoinPool.java | 4 +- .../share/native/libjava/VirtualThread.c | 3 +- .../virtual/ParkWithFixedThreadPool.java | 31 ++- 16 files changed, 184 insertions(+), 263 deletions(-) diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 54912a5ded7..68121c56c32 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -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 */ \ diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index 014a8a00c7b..d8018cd0c8a 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -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") \ diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index edad228b4e3..6634306636b 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -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); diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 530b02db46a..45ad1f577be 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -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) \ diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index fa0abf2deb1..138fd38bfa4 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -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; diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index c7c9da18e54..6b4a6524fd5 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -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) { diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 20dc842eded..f0f14a05031 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -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)) diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index cec898a0de8..b2e9fb9a4ab 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -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* 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, diff --git a/src/hotspot/share/prims/jvmtiThreadState.cpp b/src/hotspot/share/prims/jvmtiThreadState.cpp index 743f112b9b6..d175aa6c78d 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiThreadState.cpp @@ -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); } diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index 14528f6d908..285c2de17fa 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -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 diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index bda438612e3..249a1e4dc87 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -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 diff --git a/src/java.base/share/classes/java/lang/ThreadLocal.java b/src/java.base/share/classes/java/lang/ThreadLocal.java index 4c2eb08c125..8a9aa7998fd 100644 --- a/src/java.base/share/classes/java/lang/ThreadLocal.java +++ b/src/java.base/share/classes/java/lang/ThreadLocal.java @@ -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 { 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 { */ 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 { } } - /** * 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 { } /** - * 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 DUMPING_STACK = new ThreadLocal<>(); } diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index 8f389906f2a..f76a6eec914 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -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 executeOnCarrierThread(Callable 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); diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java index cb061813c8a..13ab965c530 100644 --- a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java +++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java @@ -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 diff --git a/src/java.base/share/native/libjava/VirtualThread.c b/src/java.base/share/native/libjava/VirtualThread.c index 94dbe0b7e37..f9d1d4996fc 100644 --- a/src/java.base/share/native/libjava/VirtualThread.c +++ b/src/java.base/share/native/libjava/VirtualThread.c @@ -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 }, }; diff --git a/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java b/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java index 28a32fc504a..70b73884d92 100644 --- a/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java +++ b/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java @@ -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(); + } + } }