8343132: Remove temporary transitions from Virtual thread implementation
Reviewed-by: dholmes, sspitsyn, pchilanomate
This commit is contained in:
parent
2f1ba5ef09
commit
dee0982c60
@ -611,7 +611,6 @@ class methodHandle;
|
|||||||
do_intrinsic(_notifyJvmtiVThreadEnd, java_lang_VirtualThread, notifyJvmtiEnd_name, void_method_signature, F_RN) \
|
do_intrinsic(_notifyJvmtiVThreadEnd, java_lang_VirtualThread, notifyJvmtiEnd_name, void_method_signature, F_RN) \
|
||||||
do_intrinsic(_notifyJvmtiVThreadMount, java_lang_VirtualThread, notifyJvmtiMount_name, bool_void_signature, F_RN) \
|
do_intrinsic(_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(_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) \
|
do_intrinsic(_notifyJvmtiVThreadDisableSuspend, java_lang_VirtualThread, notifyJvmtiDisableSuspend_name, bool_void_signature, F_SN) \
|
||||||
\
|
\
|
||||||
/* support for UnsafeConstants */ \
|
/* support for UnsafeConstants */ \
|
||||||
|
@ -398,7 +398,6 @@ class SerializeClosure;
|
|||||||
template(notifyJvmtiEnd_name, "notifyJvmtiEnd") \
|
template(notifyJvmtiEnd_name, "notifyJvmtiEnd") \
|
||||||
template(notifyJvmtiMount_name, "notifyJvmtiMount") \
|
template(notifyJvmtiMount_name, "notifyJvmtiMount") \
|
||||||
template(notifyJvmtiUnmount_name, "notifyJvmtiUnmount") \
|
template(notifyJvmtiUnmount_name, "notifyJvmtiUnmount") \
|
||||||
template(notifyJvmtiHideFrames_name, "notifyJvmtiHideFrames") \
|
|
||||||
template(notifyJvmtiDisableSuspend_name, "notifyJvmtiDisableSuspend") \
|
template(notifyJvmtiDisableSuspend_name, "notifyJvmtiDisableSuspend") \
|
||||||
template(doYield_name, "doYield") \
|
template(doYield_name, "doYield") \
|
||||||
template(enter_name, "enter") \
|
template(enter_name, "enter") \
|
||||||
|
@ -1142,9 +1142,6 @@ JVM_VirtualThreadMount(JNIEnv* env, jobject vthread, jboolean hide);
|
|||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
JVM_VirtualThreadUnmount(JNIEnv* env, jobject vthread, jboolean hide);
|
JVM_VirtualThreadUnmount(JNIEnv* env, jobject vthread, jboolean hide);
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
JVM_VirtualThreadHideFrames(JNIEnv* env, jclass clazz, jboolean hide);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
JVM_VirtualThreadDisableSuspend(JNIEnv* env, jclass clazz, jboolean enter);
|
JVM_VirtualThreadDisableSuspend(JNIEnv* env, jclass clazz, jboolean enter);
|
||||||
|
|
||||||
|
@ -248,7 +248,6 @@
|
|||||||
nonstatic_field(JavaThread, _cont_entry, ContinuationEntry*) \
|
nonstatic_field(JavaThread, _cont_entry, ContinuationEntry*) \
|
||||||
nonstatic_field(JavaThread, _unlocked_inflated_monitor, ObjectMonitor*) \
|
nonstatic_field(JavaThread, _unlocked_inflated_monitor, ObjectMonitor*) \
|
||||||
JVMTI_ONLY(nonstatic_field(JavaThread, _is_in_VTMS_transition, bool)) \
|
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)) \
|
JVMTI_ONLY(nonstatic_field(JavaThread, _is_disable_suspend, bool)) \
|
||||||
\
|
\
|
||||||
nonstatic_field(ContinuationEntry, _pin_count, uint32_t) \
|
nonstatic_field(ContinuationEntry, _pin_count, uint32_t) \
|
||||||
|
@ -841,7 +841,6 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) {
|
|||||||
case vmIntrinsics::_notifyJvmtiVThreadEnd:
|
case vmIntrinsics::_notifyJvmtiVThreadEnd:
|
||||||
case vmIntrinsics::_notifyJvmtiVThreadMount:
|
case vmIntrinsics::_notifyJvmtiVThreadMount:
|
||||||
case vmIntrinsics::_notifyJvmtiVThreadUnmount:
|
case vmIntrinsics::_notifyJvmtiVThreadUnmount:
|
||||||
case vmIntrinsics::_notifyJvmtiVThreadHideFrames:
|
|
||||||
case vmIntrinsics::_notifyJvmtiVThreadDisableSuspend:
|
case vmIntrinsics::_notifyJvmtiVThreadDisableSuspend:
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
@ -495,7 +495,6 @@ bool LibraryCallKit::try_to_inline(int predicate) {
|
|||||||
"notifyJvmtiMount", false, false);
|
"notifyJvmtiMount", false, false);
|
||||||
case vmIntrinsics::_notifyJvmtiVThreadUnmount: return inline_native_notify_jvmti_funcs(CAST_FROM_FN_PTR(address, OptoRuntime::notify_jvmti_vthread_unmount()),
|
case vmIntrinsics::_notifyJvmtiVThreadUnmount: return inline_native_notify_jvmti_funcs(CAST_FROM_FN_PTR(address, OptoRuntime::notify_jvmti_vthread_unmount()),
|
||||||
"notifyJvmtiUnmount", false, false);
|
"notifyJvmtiUnmount", false, false);
|
||||||
case vmIntrinsics::_notifyJvmtiVThreadHideFrames: return inline_native_notify_jvmti_hide();
|
|
||||||
case vmIntrinsics::_notifyJvmtiVThreadDisableSuspend: return inline_native_notify_jvmti_sync();
|
case vmIntrinsics::_notifyJvmtiVThreadDisableSuspend: return inline_native_notify_jvmti_sync();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -2975,29 +2974,6 @@ bool LibraryCallKit::inline_native_notify_jvmti_funcs(address funcAddr, const ch
|
|||||||
return true;
|
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.
|
// Always update the is_disable_suspend bit.
|
||||||
bool LibraryCallKit::inline_native_notify_jvmti_sync() {
|
bool LibraryCallKit::inline_native_notify_jvmti_sync() {
|
||||||
if (!DoJVMTIVirtualThreadTransitions) {
|
if (!DoJVMTIVirtualThreadTransitions) {
|
||||||
|
@ -3941,19 +3941,6 @@ JVM_ENTRY(void, JVM_VirtualThreadUnmount(JNIEnv* env, jobject vthread, jboolean
|
|||||||
#endif
|
#endif
|
||||||
JVM_END
|
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.
|
// Notification from VirtualThread about disabling JVMTI Suspend in a sync critical section.
|
||||||
// Needed to avoid deadlocks with JVMTI suspend mechanism.
|
// Needed to avoid deadlocks with JVMTI suspend mechanism.
|
||||||
JVM_ENTRY(void, JVM_VirtualThreadDisableSuspend(JNIEnv* env, jclass clazz, jboolean enter))
|
JVM_ENTRY(void, JVM_VirtualThreadDisableSuspend(JNIEnv* env, jclass clazz, jboolean enter))
|
||||||
|
@ -929,7 +929,7 @@ class JvmtiClassFileLoadHookPoster : public StackObj {
|
|||||||
_cached_class_file_ptr = cache_ptr;
|
_cached_class_file_ptr = cache_ptr;
|
||||||
_has_been_modified = false;
|
_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);
|
_state = JvmtiExport::get_jvmti_thread_state(_thread);
|
||||||
if (_state != nullptr) {
|
if (_state != nullptr) {
|
||||||
@ -1091,8 +1091,8 @@ bool JvmtiExport::post_class_file_load_hook(Symbol* h_name,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JavaThread::current()->is_in_any_VTMS_transition()) {
|
if (JavaThread::current()->is_in_VTMS_transition()) {
|
||||||
return false; // no events should be posted if thread is in any VTMS transition
|
return false; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
|
|
||||||
JvmtiClassFileLoadHookPoster poster(h_name, class_loader,
|
JvmtiClassFileLoadHookPoster poster(h_name, class_loader,
|
||||||
@ -1228,8 +1228,8 @@ void JvmtiExport::post_raw_breakpoint(JavaThread *thread, Method* method, addres
|
|||||||
if (state == nullptr) {
|
if (state == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any VTMS transition
|
return; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
|
|
||||||
EVT_TRIG_TRACE(JVMTI_EVENT_BREAKPOINT, ("[%s] Trg Breakpoint triggered",
|
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) {
|
if (state == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any 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",
|
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) {
|
if (state == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any 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",
|
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");
|
assert(objects != nullptr, "Nothing to post");
|
||||||
|
|
||||||
JavaThread *javaThread = JavaThread::current();
|
JavaThread *javaThread = JavaThread::current();
|
||||||
if (javaThread->is_in_any_VTMS_transition()) {
|
if (javaThread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any VTMS transition
|
return; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
if (!env->is_enabled(JVMTI_EVENT_OBJECT_FREE)) {
|
if (!env->is_enabled(JVMTI_EVENT_OBJECT_FREE)) {
|
||||||
return; // the event type has been already disabled
|
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();
|
JavaThread *thread = JavaThread::current();
|
||||||
|
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any VTMS transition
|
return; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
|
|
||||||
log_error(jvmti)("Posting Resource Exhausted event: %s",
|
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
|
// for any thread that actually wants method entry, interp_only_mode is set
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mh->jvmti_mount_transition() || 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 any 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",
|
EVT_TRIG_TRACE(JVMTI_EVENT_METHOD_ENTRY, ("[%s] Trg Method Entry triggered %s.%s",
|
||||||
JvmtiTrace::safe_get_thread_name(thread),
|
JvmtiTrace::safe_get_thread_name(thread),
|
||||||
@ -1902,8 +1902,8 @@ void JvmtiExport::post_method_exit_inner(JavaThread* thread,
|
|||||||
bool exception_exit,
|
bool exception_exit,
|
||||||
frame current_frame,
|
frame current_frame,
|
||||||
jvalue& value) {
|
jvalue& value) {
|
||||||
if (mh->jvmti_mount_transition() || 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 any 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",
|
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) {
|
if (state == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mh->jvmti_mount_transition() || 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 any VTMS transition
|
return; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
|
|
||||||
JvmtiEnvThreadStateIterator it(state);
|
JvmtiEnvThreadStateIterator it(state);
|
||||||
@ -2020,8 +2020,8 @@ void JvmtiExport::post_exception_throw(JavaThread *thread, Method* method, addre
|
|||||||
if (state == nullptr) {
|
if (state == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any 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",
|
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.");
|
assert(!state->is_exception_caught(), "exception must not be caught yet.");
|
||||||
state->set_exception_caught();
|
state->set_exception_caught();
|
||||||
|
|
||||||
if (mh->jvmti_mount_transition() || 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 any VTMS transition
|
return; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
JvmtiEnvThreadStateIterator it(state);
|
JvmtiEnvThreadStateIterator it(state);
|
||||||
for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) {
|
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.
|
// 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");
|
assert(thread->has_last_Java_frame(), "must be called with a Java context");
|
||||||
|
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any VTMS transition
|
return; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
@ -2224,8 +2224,8 @@ void JvmtiExport::post_field_access(JavaThread *thread, Method* method,
|
|||||||
if (state == nullptr) {
|
if (state == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any 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",
|
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.
|
// function don't make the call unless there is a Java context.
|
||||||
assert(thread->has_last_Java_frame(), "must be called with Java context");
|
assert(thread->has_last_Java_frame(), "must be called with Java context");
|
||||||
|
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any VTMS transition
|
return; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceMark rm;
|
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,
|
address location, Klass* field_klass, Handle object, jfieldID field,
|
||||||
char sig_type, jvalue *value) {
|
char sig_type, jvalue *value) {
|
||||||
|
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any 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 ||
|
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) {
|
if (state == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any VTMS transition
|
return; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
|
|
||||||
EVT_TRIG_TRACE(JVMTI_EVENT_FIELD_MODIFICATION,
|
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);
|
HandleMark hm(thread);
|
||||||
methodHandle mh(thread, method);
|
methodHandle mh(thread, method);
|
||||||
|
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any 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",
|
EVT_TRIG_TRACE(JVMTI_EVENT_NATIVE_METHOD_BIND, ("[%s] Trg Native Method Bind event triggered",
|
||||||
JvmtiTrace::safe_get_thread_name(thread)));
|
JvmtiTrace::safe_get_thread_name(thread)));
|
||||||
@ -2493,7 +2493,7 @@ void JvmtiExport::post_compiled_method_load(nmethod *nm) {
|
|||||||
}
|
}
|
||||||
JavaThread* thread = JavaThread::current();
|
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,
|
EVT_TRIG_TRACE(JVMTI_EVENT_COMPILED_METHOD_LOAD,
|
||||||
("[%s] method compile load event triggered",
|
("[%s] method compile load event triggered",
|
||||||
@ -2516,7 +2516,7 @@ void JvmtiExport::post_compiled_method_load(JvmtiEnv* env, nmethod *nm) {
|
|||||||
}
|
}
|
||||||
JavaThread* thread = JavaThread::current();
|
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,
|
EVT_TRACE(JVMTI_EVENT_COMPILED_METHOD_LOAD,
|
||||||
("[%s] method compile load event sent %s.%s ",
|
("[%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();
|
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
|
// In theory everyone coming thru here is in_vm but we need to be certain
|
||||||
// because a callee will do a vm->native transition
|
// 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();
|
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,
|
EVT_TRIG_TRACE(JVMTI_EVENT_DYNAMIC_CODE_GENERATED,
|
||||||
("[%s] dynamic code generated event triggered (by GenerateEvents)",
|
("[%s] dynamic code generated event triggered (by GenerateEvents)",
|
||||||
@ -2744,8 +2744,8 @@ void JvmtiExport::post_monitor_contended_enter(JavaThread *thread, ObjectMonitor
|
|||||||
if (state == nullptr) {
|
if (state == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any VTMS transition
|
return; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
|
|
||||||
EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_CONTENDED_ENTER,
|
EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_CONTENDED_ENTER,
|
||||||
@ -2777,8 +2777,8 @@ void JvmtiExport::post_monitor_contended_entered(JavaThread *thread, ObjectMonit
|
|||||||
if (state == nullptr) {
|
if (state == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any VTMS transition
|
return; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
|
|
||||||
EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_CONTENDED_ENTERED,
|
EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_CONTENDED_ENTERED,
|
||||||
@ -2811,8 +2811,8 @@ void JvmtiExport::post_monitor_wait(JavaThread *thread, oop object,
|
|||||||
if (state == nullptr) {
|
if (state == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any VTMS transition
|
return; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
|
|
||||||
EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_WAIT,
|
EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_WAIT,
|
||||||
@ -2845,8 +2845,8 @@ void JvmtiExport::post_monitor_waited(JavaThread *thread, ObjectMonitor *obj_mnt
|
|||||||
if (state == nullptr) {
|
if (state == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any VTMS transition
|
return; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
|
|
||||||
EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_WAITED,
|
EVT_TRIG_TRACE(JVMTI_EVENT_MONITOR_WAITED,
|
||||||
@ -2874,8 +2874,8 @@ void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) {
|
|||||||
if (object == nullptr) {
|
if (object == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any VTMS transition
|
return; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
HandleMark hm(thread);
|
HandleMark hm(thread);
|
||||||
Handle h(thread, object);
|
Handle h(thread, object);
|
||||||
@ -2911,8 +2911,8 @@ void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) {
|
|||||||
if (object == nullptr) {
|
if (object == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thread->is_in_any_VTMS_transition()) {
|
if (thread->is_in_VTMS_transition()) {
|
||||||
return; // no events should be posted if thread is in any VTMS transition
|
return; // no events should be posted if thread is in VTMS transition
|
||||||
}
|
}
|
||||||
|
|
||||||
EVT_TRIG_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,
|
EVT_TRIG_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,
|
||||||
|
@ -353,7 +353,6 @@ JvmtiVTMSTransitionDisabler::VTMS_transition_disable_for_all() {
|
|||||||
{
|
{
|
||||||
MonitorLocker ml(JvmtiVTMSTransition_lock);
|
MonitorLocker ml(JvmtiVTMSTransition_lock);
|
||||||
|
|
||||||
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
|
|
||||||
assert(!thread->is_in_VTMS_transition(), "VTMS_transition sanity check");
|
assert(!thread->is_in_VTMS_transition(), "VTMS_transition sanity check");
|
||||||
while (_SR_mode) { // Suspender or resumer is a JvmtiVTMSTransitionDisabler monopolist.
|
while (_SR_mode) { // Suspender or resumer is a JvmtiVTMSTransitionDisabler monopolist.
|
||||||
ml.wait(10); // Wait while there is an active suspender or resumer.
|
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();
|
JavaThread* thread = JavaThread::current();
|
||||||
|
|
||||||
assert(!thread->is_in_VTMS_transition(), "sanity check");
|
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
|
// If interp_only_mode has been enabled then we must eagerly create JvmtiThreadState
|
||||||
// objects for globally enabled virtual thread filtered events. Otherwise,
|
// objects for globally enabled virtual thread filtered events. Otherwise,
|
||||||
@ -593,7 +591,6 @@ JvmtiVTMSTransitionDisabler::VTMS_vthread_end(jobject vthread) {
|
|||||||
JavaThread* thread = JavaThread::current();
|
JavaThread* thread = JavaThread::current();
|
||||||
|
|
||||||
assert(!thread->is_in_VTMS_transition(), "sanity check");
|
assert(!thread->is_in_VTMS_transition(), "sanity check");
|
||||||
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
|
|
||||||
|
|
||||||
// post VirtualThreadUnmount event before VirtualThreadEnd
|
// post VirtualThreadUnmount event before VirtualThreadEnd
|
||||||
if (JvmtiExport::should_post_vthread_unmount()) {
|
if (JvmtiExport::should_post_vthread_unmount()) {
|
||||||
@ -638,7 +635,6 @@ JvmtiVTMSTransitionDisabler::VTMS_vthread_unmount(jobject vthread, bool hide) {
|
|||||||
void
|
void
|
||||||
JvmtiVTMSTransitionDisabler::VTMS_mount_begin(jobject vthread) {
|
JvmtiVTMSTransitionDisabler::VTMS_mount_begin(jobject vthread) {
|
||||||
JavaThread* thread = JavaThread::current();
|
JavaThread* thread = JavaThread::current();
|
||||||
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
|
|
||||||
assert(!thread->is_in_VTMS_transition(), "sanity check");
|
assert(!thread->is_in_VTMS_transition(), "sanity check");
|
||||||
start_VTMS_transition(vthread, /* is_mount */ true);
|
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);
|
thread->rebind_to_jvmti_thread_state_of(vt);
|
||||||
|
|
||||||
assert(thread->is_in_VTMS_transition(), "sanity check");
|
assert(thread->is_in_VTMS_transition(), "sanity check");
|
||||||
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
|
|
||||||
finish_VTMS_transition(vthread, /* is_mount */ true);
|
finish_VTMS_transition(vthread, /* is_mount */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -659,7 +654,6 @@ void
|
|||||||
JvmtiVTMSTransitionDisabler::VTMS_unmount_begin(jobject vthread, bool last_unmount) {
|
JvmtiVTMSTransitionDisabler::VTMS_unmount_begin(jobject vthread, bool last_unmount) {
|
||||||
JavaThread* thread = JavaThread::current();
|
JavaThread* thread = JavaThread::current();
|
||||||
|
|
||||||
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
|
|
||||||
assert(!thread->is_in_VTMS_transition(), "sanity check");
|
assert(!thread->is_in_VTMS_transition(), "sanity check");
|
||||||
|
|
||||||
start_VTMS_transition(vthread, /* is_mount */ false);
|
start_VTMS_transition(vthread, /* is_mount */ false);
|
||||||
@ -672,7 +666,6 @@ void
|
|||||||
JvmtiVTMSTransitionDisabler::VTMS_unmount_end(jobject vthread) {
|
JvmtiVTMSTransitionDisabler::VTMS_unmount_end(jobject vthread) {
|
||||||
JavaThread* thread = JavaThread::current();
|
JavaThread* thread = JavaThread::current();
|
||||||
assert(thread->is_in_VTMS_transition(), "sanity check");
|
assert(thread->is_in_VTMS_transition(), "sanity check");
|
||||||
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
|
|
||||||
finish_VTMS_transition(vthread, /* is_mount */ false);
|
finish_VTMS_transition(vthread, /* is_mount */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,7 +446,6 @@ JavaThread::JavaThread(MemTag mem_tag) :
|
|||||||
#if INCLUDE_JVMTI
|
#if INCLUDE_JVMTI
|
||||||
_carrier_thread_suspended(false),
|
_carrier_thread_suspended(false),
|
||||||
_is_in_VTMS_transition(false),
|
_is_in_VTMS_transition(false),
|
||||||
_is_in_tmp_VTMS_transition(false),
|
|
||||||
_is_disable_suspend(false),
|
_is_disable_suspend(false),
|
||||||
_VTMS_transition_mark(false),
|
_VTMS_transition_mark(false),
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
|
@ -311,7 +311,6 @@ class JavaThread: public Thread {
|
|||||||
#if INCLUDE_JVMTI
|
#if INCLUDE_JVMTI
|
||||||
volatile bool _carrier_thread_suspended; // Carrier thread is externally suspended
|
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_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 _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
|
bool _VTMS_transition_mark; // used for sync between VTMS transitions and disablers
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
@ -675,11 +674,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool is_in_VTMS_transition() const { return _is_in_VTMS_transition; }
|
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 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; }
|
bool is_disable_suspend() const { return _is_disable_suspend; }
|
||||||
void toggle_is_disable_suspend() { _is_disable_suspend = !_is_disable_suspend; };
|
void toggle_is_disable_suspend() { _is_disable_suspend = !_is_disable_suspend; };
|
||||||
@ -851,7 +846,6 @@ private:
|
|||||||
|
|
||||||
#if INCLUDE_JVMTI
|
#if INCLUDE_JVMTI
|
||||||
static ByteSize is_in_VTMS_transition_offset() { return byte_offset_of(JavaThread, _is_in_VTMS_transition); }
|
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); }
|
static ByteSize is_disable_suspend_offset() { return byte_offset_of(JavaThread, _is_disable_suspend); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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) {
|
if (this instanceof TerminatingThreadLocal<?> ttl) {
|
||||||
TerminatingThreadLocal.register(ttl);
|
TerminatingThreadLocal.register(ttl);
|
||||||
}
|
}
|
||||||
if (TRACE_VTHREAD_LOCALS) {
|
if (TRACE_VTHREAD_LOCALS && t == Thread.currentThread() && t.isVirtual()) {
|
||||||
dumpStackIfVirtualThread();
|
printStackTrace();
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@ -249,8 +249,8 @@ public class ThreadLocal<T> {
|
|||||||
*/
|
*/
|
||||||
public void set(T value) {
|
public void set(T value) {
|
||||||
set(Thread.currentThread(), value);
|
set(Thread.currentThread(), value);
|
||||||
if (TRACE_VTHREAD_LOCALS) {
|
if (TRACE_VTHREAD_LOCALS && Thread.currentThread().isVirtual()) {
|
||||||
dumpStackIfVirtualThread();
|
printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -799,7 +799,6 @@ public class ThreadLocal<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the value of the jdk.traceVirtualThreadLocals property to determine if
|
* 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.
|
* 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() {
|
private void printStackTrace() {
|
||||||
if (Thread.currentThread() instanceof VirtualThread vthread) {
|
Thread t = Thread.currentThread();
|
||||||
|
ThreadLocalMap map = getMap(t);
|
||||||
|
if (map.getEntry(DUMPING_STACK) == null) {
|
||||||
|
map.set(DUMPING_STACK, true);
|
||||||
try {
|
try {
|
||||||
var stack = StackWalkerHolder.STACK_WALKER.walk(s ->
|
var stack = StackWalker.getInstance().walk(s ->
|
||||||
s.skip(1) // skip caller
|
s.skip(1) // skip caller
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
|
System.out.println(t);
|
||||||
// switch to carrier thread to avoid recursive use of thread-locals
|
for (StackWalker.StackFrame frame : stack) {
|
||||||
vthread.executeOnCarrierThread(() -> {
|
System.out.format(" %s%n", frame.toStackTraceElement());
|
||||||
System.out.println(vthread);
|
}
|
||||||
for (StackWalker.StackFrame frame : stack) {
|
} finally {
|
||||||
System.out.format(" %s%n", frame.toStackTraceElement());
|
map.remove(DUMPING_STACK);
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new InternalError(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class StackWalkerHolder {
|
private static final ThreadLocal<Boolean> DUMPING_STACK = new ThreadLocal<>();
|
||||||
static final StackWalker STACK_WALKER = StackWalker.getInstance();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ import java.security.AccessController;
|
|||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
@ -137,13 +136,18 @@ final class VirtualThread extends BaseVirtualThread {
|
|||||||
// parking permit
|
// parking permit
|
||||||
private volatile boolean parkPermit;
|
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
|
// carrier thread when mounted, accessed by VM
|
||||||
private volatile Thread carrierThread;
|
private volatile Thread carrierThread;
|
||||||
|
|
||||||
// termination object when joining, created lazily if needed
|
// termination object when joining, created lazily if needed
|
||||||
private volatile CountDownLatch termination;
|
private volatile CountDownLatch termination;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the default scheduler.
|
* Returns the default scheduler.
|
||||||
*/
|
*/
|
||||||
@ -246,8 +250,10 @@ final class VirtualThread extends BaseVirtualThread {
|
|||||||
if (!compareAndSetState(initialState, RUNNING)) {
|
if (!compareAndSetState(initialState, RUNNING)) {
|
||||||
return;
|
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) {
|
if (initialState == UNPARKED) {
|
||||||
|
cancelTimeoutTask();
|
||||||
setParkPermit(false);
|
setParkPermit(false);
|
||||||
}
|
}
|
||||||
} else {
|
} 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,
|
* 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,
|
* 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
|
* @param retryOnOOME true to retry indefinitely if OutOfMemoryError is thrown
|
||||||
* @throws RejectedExecutionException
|
* @throws RejectedExecutionException
|
||||||
*/
|
*/
|
||||||
@ChangesCurrentThread
|
|
||||||
private void submitRunContinuation(Executor scheduler, boolean retryOnOOME) {
|
private void submitRunContinuation(Executor scheduler, boolean retryOnOOME) {
|
||||||
boolean done = false;
|
boolean done = false;
|
||||||
while (!done) {
|
while (!done) {
|
||||||
try {
|
try {
|
||||||
// The scheduler's execute method is invoked in the context of the
|
// Pin the continuation to prevent the virtual thread from unmounting
|
||||||
// carrier thread. For the default scheduler this ensures that the
|
// when submitting a task. For the default scheduler this ensures that
|
||||||
// current thread is a ForkJoinWorkerThread so the task will be pushed
|
// the carrier doesn't change when pushing a task. For other schedulers
|
||||||
// to the local queue. For other schedulers, it avoids deadlock that
|
// it avoids deadlock that could arise due to carriers and virtual
|
||||||
// would arise due to platform and virtual threads contending for a
|
// threads contending for a lock.
|
||||||
// lock on the scheduler's submission queue.
|
if (currentThread().isVirtual()) {
|
||||||
if (currentThread() instanceof VirtualThread vthread) {
|
Continuation.pin();
|
||||||
vthread.switchToCarrierThread();
|
|
||||||
try {
|
try {
|
||||||
scheduler.execute(runContinuation);
|
scheduler.execute(runContinuation);
|
||||||
} finally {
|
} finally {
|
||||||
switchToVirtualThread(vthread);
|
Continuation.unpin();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
scheduler.execute(runContinuation);
|
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.
|
* 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.
|
* 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);
|
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
|
* 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
|
* 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);
|
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
|
* Invokes Continuation.yield, notifying JVMTI (if enabled) to hide frames until
|
||||||
* the continuation continues.
|
* the continuation continues.
|
||||||
@ -528,9 +510,8 @@ final class VirtualThread extends BaseVirtualThread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked after the continuation yields. If parking then it sets the state
|
* Invoked in the context of the carrier thread after the Continuation yields when
|
||||||
* and also re-submits the task to continue if unparked while parking.
|
* parking or Thread.yield.
|
||||||
* If yielding due to Thread.yield then it just submits the task to continue.
|
|
||||||
*/
|
*/
|
||||||
private void afterYield() {
|
private void afterYield() {
|
||||||
assert carrierThread == null;
|
assert carrierThread == null;
|
||||||
@ -544,17 +525,20 @@ final class VirtualThread extends BaseVirtualThread {
|
|||||||
|
|
||||||
// LockSupport.park/parkNanos
|
// LockSupport.park/parkNanos
|
||||||
if (s == PARKING || s == TIMED_PARKING) {
|
if (s == PARKING || s == TIMED_PARKING) {
|
||||||
int newState = (s == PARKING) ? PARKED : TIMED_PARKED;
|
int newState;
|
||||||
setState(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
|
// may have been unparked while parking
|
||||||
if (parkPermit && compareAndSetState(newState, UNPARKED)) {
|
if (parkPermit && compareAndSetState(newState, UNPARKED)) {
|
||||||
// lazy submit to continue on the current carrier if possible
|
// lazy submit if local queue is empty
|
||||||
if (currentThread() instanceof CarrierThread ct && ct.getQueuedTaskCount() == 0) {
|
lazySubmitRunContinuation();
|
||||||
lazySubmitRunContinuation(ct.getPool());
|
|
||||||
} else {
|
|
||||||
submitRunContinuation();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -672,7 +656,9 @@ final class VirtualThread extends BaseVirtualThread {
|
|||||||
boolean yielded = false;
|
boolean yielded = false;
|
||||||
setState(PARKING);
|
setState(PARKING);
|
||||||
try {
|
try {
|
||||||
yielded = yieldContinuation(); // may throw
|
yielded = yieldContinuation();
|
||||||
|
} catch (OutOfMemoryError e) {
|
||||||
|
// park on carrier
|
||||||
} finally {
|
} finally {
|
||||||
assert (Thread.currentThread() == this) && (yielded == (state() == RUNNING));
|
assert (Thread.currentThread() == this) && (yielded == (state() == RUNNING));
|
||||||
if (!yielded) {
|
if (!yielded) {
|
||||||
@ -707,21 +693,23 @@ final class VirtualThread extends BaseVirtualThread {
|
|||||||
if (nanos > 0) {
|
if (nanos > 0) {
|
||||||
long startTime = System.nanoTime();
|
long startTime = System.nanoTime();
|
||||||
|
|
||||||
|
// park the thread, afterYield will schedule the thread to unpark
|
||||||
boolean yielded = false;
|
boolean yielded = false;
|
||||||
Future<?> unparker = scheduleUnpark(nanos); // may throw OOME
|
setParkTimeout(nanos);
|
||||||
setState(TIMED_PARKING);
|
setState(TIMED_PARKING);
|
||||||
try {
|
try {
|
||||||
yielded = yieldContinuation(); // may throw
|
yielded = yieldContinuation();
|
||||||
|
} catch (OutOfMemoryError e) {
|
||||||
|
// park on carrier
|
||||||
} finally {
|
} finally {
|
||||||
assert (Thread.currentThread() == this) && (yielded == (state() == RUNNING));
|
assert (Thread.currentThread() == this) && (yielded == (state() == RUNNING));
|
||||||
if (!yielded) {
|
if (!yielded) {
|
||||||
assert state() == TIMED_PARKING;
|
assert state() == TIMED_PARKING;
|
||||||
setState(RUNNING);
|
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) {
|
if (!yielded) {
|
||||||
long remainingNanos = nanos - (System.nanoTime() - startTime);
|
long remainingNanos = nanos - (System.nanoTime() - startTime);
|
||||||
parkOnCarrierThread(true, remainingNanos);
|
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
|
* 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
|
* 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;
|
return Thread.State.RUNNABLE;
|
||||||
case PARKED:
|
case PARKED:
|
||||||
case PINNED:
|
case PINNED:
|
||||||
return State.WAITING;
|
return Thread.State.WAITING;
|
||||||
case TIMED_PARKED:
|
case TIMED_PARKED:
|
||||||
case TIMED_PINNED:
|
case TIMED_PINNED:
|
||||||
return State.TIMED_WAITING;
|
return Thread.State.TIMED_WAITING;
|
||||||
case TERMINATED:
|
case TERMINATED:
|
||||||
return Thread.State.TERMINATED;
|
return Thread.State.TERMINATED;
|
||||||
default:
|
default:
|
||||||
@ -1263,6 +1219,10 @@ final class VirtualThread extends BaseVirtualThread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setParkTimeout(long timeout) {
|
||||||
|
parkTimeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
private void setCarrierThread(Thread carrier) {
|
private void setCarrierThread(Thread carrier) {
|
||||||
// U.putReferenceRelease(this, CARRIER_THREAD, carrier);
|
// U.putReferenceRelease(this, CARRIER_THREAD, carrier);
|
||||||
this.carrierThread = carrier;
|
this.carrierThread = carrier;
|
||||||
@ -1286,10 +1246,6 @@ final class VirtualThread extends BaseVirtualThread {
|
|||||||
@JvmtiMountTransition
|
@JvmtiMountTransition
|
||||||
private native void notifyJvmtiUnmount(boolean hide);
|
private native void notifyJvmtiUnmount(boolean hide);
|
||||||
|
|
||||||
@IntrinsicCandidate
|
|
||||||
@JvmtiMountTransition
|
|
||||||
private static native void notifyJvmtiHideFrames(boolean hide);
|
|
||||||
|
|
||||||
@IntrinsicCandidate
|
@IntrinsicCandidate
|
||||||
private static native void notifyJvmtiDisableSuspend(boolean enter);
|
private static native void notifyJvmtiDisableSuspend(boolean enter);
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ import java.util.Objects;
|
|||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.locks.LockSupport;
|
import java.util.concurrent.locks.LockSupport;
|
||||||
|
import jdk.internal.access.JavaLangAccess;
|
||||||
import jdk.internal.access.JavaUtilConcurrentFJPAccess;
|
import jdk.internal.access.JavaUtilConcurrentFJPAccess;
|
||||||
import jdk.internal.access.SharedSecrets;
|
import jdk.internal.access.SharedSecrets;
|
||||||
import jdk.internal.misc.Unsafe;
|
import jdk.internal.misc.Unsafe;
|
||||||
@ -2632,7 +2633,7 @@ public class ForkJoinPool extends AbstractExecutorService {
|
|||||||
|
|
||||||
private void poolSubmit(boolean signalIfEmpty, ForkJoinTask<?> task) {
|
private void poolSubmit(boolean signalIfEmpty, ForkJoinTask<?> task) {
|
||||||
Thread t; ForkJoinWorkerThread wt; WorkQueue q; boolean internal;
|
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) {
|
(wt = (ForkJoinWorkerThread)t).pool == this) {
|
||||||
internal = true;
|
internal = true;
|
||||||
q = wt.workQueue;
|
q = wt.workQueue;
|
||||||
@ -2643,6 +2644,7 @@ public class ForkJoinPool extends AbstractExecutorService {
|
|||||||
}
|
}
|
||||||
q.push(task, signalIfEmpty ? this : null, internal);
|
q.push(task, signalIfEmpty ? this : null, internal);
|
||||||
}
|
}
|
||||||
|
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns queue for an external submission, bypassing call to
|
* Returns queue for an external submission, bypassing call to
|
||||||
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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 },
|
{ "notifyJvmtiEnd", "()V", (void *)&JVM_VirtualThreadEnd },
|
||||||
{ "notifyJvmtiMount", "(Z)V", (void *)&JVM_VirtualThreadMount },
|
{ "notifyJvmtiMount", "(Z)V", (void *)&JVM_VirtualThreadMount },
|
||||||
{ "notifyJvmtiUnmount", "(Z)V", (void *)&JVM_VirtualThreadUnmount },
|
{ "notifyJvmtiUnmount", "(Z)V", (void *)&JVM_VirtualThreadUnmount },
|
||||||
{ "notifyJvmtiHideFrames", "(Z)V", (void *)&JVM_VirtualThreadHideFrames },
|
|
||||||
{ "notifyJvmtiDisableSuspend", "(Z)V", (void *)&JVM_VirtualThreadDisableSuspend },
|
{ "notifyJvmtiDisableSuspend", "(Z)V", (void *)&JVM_VirtualThreadDisableSuspend },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,14 +30,16 @@
|
|||||||
* @run main ParkWithFixedThreadPool
|
* @run main ParkWithFixedThreadPool
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.atomic.*;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
import java.util.concurrent.locks.LockSupport;
|
import java.util.concurrent.locks.LockSupport;
|
||||||
import jdk.test.lib.thread.VThreadScheduler;
|
import jdk.test.lib.thread.VThreadScheduler;
|
||||||
|
|
||||||
public class ParkWithFixedThreadPool {
|
public class ParkWithFixedThreadPool {
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
try (ExecutorService scheduler = Executors.newFixedThreadPool(8)) {
|
try (var scheduler = new Scheduler(8)) {
|
||||||
int vthreadCount = 300;
|
int vthreadCount = 300;
|
||||||
Thread[] vthreads = new Thread[vthreadCount];
|
Thread[] vthreads = new Thread[vthreadCount];
|
||||||
Runnable target = new Runnable() {
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user