Skip to content

Commit e1c834b

Browse files
committed
event management support for virtual threads
1 parent 06aa75d commit e1c834b

File tree

25 files changed

+403
-221
lines changed

25 files changed

+403
-221
lines changed

src/hotspot/share/classfile/javaClasses.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2020,6 +2020,7 @@ int java_lang_Thread::_name_offset;
20202020
int java_lang_Thread::_contextClassLoader_offset;
20212021
int java_lang_Thread::_inheritedAccessControlContext_offset;
20222022
int java_lang_Thread::_eetop_offset;
2023+
int java_lang_Thread::_jvmti_thread_state_offset;
20232024
int java_lang_Thread::_interrupted_offset;
20242025
int java_lang_Thread::_tid_offset;
20252026
int java_lang_Thread::_continuation_offset;
@@ -2031,6 +2032,7 @@ int java_lang_Thread::_park_blocker_offset;
20312032
macro(_contextClassLoader_offset, k, vmSymbols::contextClassLoader_name(), classloader_signature, false); \
20322033
macro(_inheritedAccessControlContext_offset, k, vmSymbols::inheritedAccessControlContext_name(), accesscontrolcontext_signature, false); \
20332034
macro(_eetop_offset, k, "eetop", long_signature, false); \
2035+
macro(_jvmti_thread_state_offset, k, "jvmtiThreadState", long_signature, false); \
20342036
macro(_interrupted_offset, k, "interrupted", bool_signature, false); \
20352037
macro(_tid_offset, k, "tid", long_signature, false); \
20362038
macro(_park_blocker_offset, k, "parkBlocker", object_signature, false); \
@@ -2053,11 +2055,18 @@ JavaThread* java_lang_Thread::thread(oop java_thread) {
20532055
return (JavaThread*)java_thread->address_field(_eetop_offset);
20542056
}
20552057

2056-
20572058
void java_lang_Thread::set_thread(oop java_thread, JavaThread* thread) {
20582059
java_thread->address_field_put(_eetop_offset, (address)thread);
20592060
}
20602061

2062+
JvmtiThreadState* java_lang_Thread::jvmti_thread_state(oop java_thread) {
2063+
return (JvmtiThreadState*)java_thread->address_field(_jvmti_thread_state_offset);
2064+
}
2065+
2066+
void java_lang_Thread::set_jvmti_thread_state(oop java_thread, JvmtiThreadState* state) {
2067+
java_thread->address_field_put(_jvmti_thread_state_offset, (address)state);
2068+
}
2069+
20612070
oop java_lang_Thread::holder(oop java_thread) {
20622071
return java_thread->obj_field(_holder_offset);
20632072
}

src/hotspot/share/classfile/javaClasses.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@
2626
#define SHARE_CLASSFILE_JAVACLASSES_HPP
2727

2828
#include "classfile/systemDictionary.hpp"
29-
#include "jvmtifiles/jvmti.h"
3029
#include "oops/oop.hpp"
3130
#include "oops/instanceKlass.hpp"
3231
#include "oops/symbol.hpp"
3332
#include "runtime/os.hpp"
3433
#include "utilities/vmEnums.hpp"
3534

35+
class JvmtiThreadState;
3636
class RecordComponent;
3737

3838
// Interface for manipulating the basic Java classes.
@@ -376,6 +376,7 @@ class java_lang_Thread : AllStatic {
376376
static int _contextClassLoader_offset;
377377
static int _inheritedAccessControlContext_offset;
378378
static int _eetop_offset;
379+
static int _jvmti_thread_state_offset;
379380
static int _interrupted_offset;
380381
static int _tid_offset;
381382
static int _continuation_offset;
@@ -426,6 +427,9 @@ class java_lang_Thread : AllStatic {
426427
// Continuation
427428
static inline oop continuation(oop java_thread);
428429

430+
static JvmtiThreadState* jvmti_thread_state(oop java_thread);
431+
static void set_jvmti_thread_state(oop java_thread, JvmtiThreadState* state);
432+
429433
// Blocker object responsible for thread parking
430434
static oop park_blocker(oop java_thread);
431435

src/hotspot/share/prims/jvm.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
#include "oops/oop.inline.hpp"
6363
#include "prims/jvm_misc.hpp"
6464
#include "prims/jvmtiExport.hpp"
65-
#include "prims/jvmtiThreadState.hpp"
65+
#include "prims/jvmtiThreadState.inline.hpp"
6666
#include "prims/nativeLookup.hpp"
6767
#include "prims/stackwalk.hpp"
6868
#include "runtime/arguments.hpp"
@@ -4115,25 +4115,41 @@ JVM_END
41154115
JVM_ENTRY(void, JVM_VirtualThreadMountEnd(JNIEnv* env, jclass vthread_class, jobject event_thread, jobject vthread, jboolean first_mount))
41164116
JVMWrapper("JVM_VirtualThreadMountEnd");
41174117
JvmtiVTMTDisabler::finish_VTMT(vthread, 0);
4118-
thread->set_mounted_vthread(JNIHandles::resolve(vthread));
4118+
oop vt_oop = JNIHandles::resolve(vthread);
4119+
thread->set_mounted_vthread(vt_oop);
4120+
4121+
// unbind JvmtiThreadState of carrier thread from JavaThread
4122+
thread->jvmti_thread_state()->unbind_from(thread);
4123+
4124+
// bind JvmtiThreadState of virtual thread to JavaThread
4125+
java_lang_Thread::jvmti_thread_state(vt_oop)->bind_to(thread);
4126+
41194127
if (first_mount) {
41204128
// thread start
41214129
if (JvmtiExport::should_post_vthread_scheduled()) {
4122-
JvmtiExport::post_vthread_scheduled(event_thread, vthread);
4130+
JvmtiExport::post_vthread_scheduled(vthread);
41234131
}
41244132
JFR_ONLY(Jfr::on_thread_start(event_thread, vthread));
41254133
}
41264134
if (JvmtiExport::should_post_vthread_mounted()) {
4127-
JvmtiExport::post_vthread_mounted(event_thread, vthread);
4135+
JvmtiExport::post_vthread_mounted(vthread);
41284136
}
41294137
JVM_END
41304138

41314139
JVM_ENTRY(void, JVM_VirtualThreadUnmountBegin(JNIEnv* env, jclass vthread_class, jobject event_thread, jobject vthread))
41324140
JVMWrapper("JVM_VirtualThreadUnmountBegin");
41334141
if (JvmtiExport::should_post_vthread_unmounted()) {
4134-
JvmtiExport::post_vthread_unmounted(event_thread, vthread);
4142+
JvmtiExport::post_vthread_unmounted(vthread);
41354143
}
4136-
thread->set_mounted_vthread(java_lang_VirtualThread::carrier_thread(thread->mounted_vthread()));
4144+
oop ct_oop = JNIHandles::resolve(event_thread);
4145+
4146+
// unbind JvmtiThreadState of virtual thread from JavaThread
4147+
thread->jvmti_thread_state()->unbind_from(thread);
4148+
4149+
// bind JvmtiThreadState of carrier thread to JavaThread
4150+
java_lang_Thread::jvmti_thread_state(ct_oop)->bind_to(thread);
4151+
4152+
thread->set_mounted_vthread(ct_oop);
41374153
JvmtiVTMTDisabler::start_VTMT(vthread, 1);
41384154
JVM_END
41394155

@@ -4145,10 +4161,10 @@ JVM_END
41454161
JVM_ENTRY(void, JVM_VirtualThreadTerminated(JNIEnv* env, jclass vthread_class, jthread event_thread, jobject vthread))
41464162
JVMWrapper("JVM_VirtualThreadTerminated");
41474163
if (JvmtiExport::should_post_vthread_terminated()) {
4148-
JvmtiExport::post_vthread_terminated(event_thread, vthread);
4164+
JvmtiExport::post_vthread_terminated(vthread);
41494165
}
41504166
JFR_ONLY(Jfr::on_thread_exit(event_thread, vthread));
41514167
thread->set_mounted_vthread(NULL);
41524168
JvmtiVTMTDisabler::start_VTMT(vthread, 0);
41534169
JvmtiVTMTDisabler::finish_VTMT(vthread, 0);
4154-
JVM_END
4170+
JVM_END

src/hotspot/share/prims/jvmti.xml

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3035,7 +3035,7 @@ err = (*jvmti)->Deallocate(jvmti, stack_info);
30353035
</capabilities>
30363036
<parameters>
30373037
<param id="thread">
3038-
<jthread null="current" frame="depth"/>
3038+
<jthread null="current" frame="depth" impl="noconvert"/>
30393039
<description>
30403040
The thread of the frame for which the frame pop event will be generated.
30413041
</description>
@@ -12922,12 +12922,6 @@ myInit() {
1292212922
The JNI environment of the event (current) thread.
1292312923
</description>
1292412924
</param>
12925-
<param id="thread">
12926-
<jthread/>
12927-
<description>
12928-
Thread scheduling this virtual thread.
12929-
</description>
12930-
</param>
1293112925
<param id="virtual_thread">
1293212926
<jthread/>
1293312927
<description>
@@ -12957,12 +12951,6 @@ myInit() {
1295712951
The JNI environment of the event (current) thread.
1295812952
</description>
1295912953
</param>
12960-
<param id="thread">
12961-
<jthread/>
12962-
<description>
12963-
Thread terminating this virtual thread.
12964-
</description>
12965-
</param>
1296612954
<param id="virtual_thread">
1296712955
<jthread/>
1296812956
<description>
@@ -12972,7 +12960,7 @@ myInit() {
1297212960
</parameters>
1297312961
</event>
1297412962

12975-
<event label="Virtual Thread Mount"
12963+
<event label="Virtual Thread Mounted"
1297612964
id="VirtualThreadMounted" const="JVMTI_EVENT_VIRTUAL_THREAD_MOUNTED" filtered="thread" num="89" phase="start" since="14">
1297712965
<description>
1297812966
Virtual thread mount events are generated before its method continue to execute on the mounted thread.
@@ -12992,12 +12980,6 @@ myInit() {
1299212980
The JNI environment of the event (current) thread.
1299312981
</description>
1299412982
</param>
12995-
<param id="thread">
12996-
<jthread/>
12997-
<description>
12998-
Thread the virtual thread is mounted to.
12999-
</description>
13000-
</param>
1300112983
<param id="virtual_thread">
1300212984
<jthread/>
1300312985
<description>
@@ -13007,7 +12989,7 @@ myInit() {
1300712989
</parameters>
1300812990
</event>
1300912991

13010-
<event label="Virtual Thread Unmount"
12992+
<event label="Virtual Thread Unmounted"
1301112993
id="VirtualThreadUnmounted" const="JVMTI_EVENT_VIRTUAL_THREAD_UNMOUNTED" filtered="thread" num="90" phase="start" since="14">
1301212994
<description>
1301312995
Virtual thread unmount events are generated when the virtual thread is about to be unmounted from the carrier thread.
@@ -13027,12 +13009,6 @@ myInit() {
1302713009
The JNI environment of the event (current) thread.
1302813010
</description>
1302913011
</param>
13030-
<param id="thread">
13031-
<jthread/>
13032-
<description>
13033-
Thread the virtual thread is unmounted from.
13034-
</description>
13035-
</param>
1303613012
<param id="virtual_thread">
1303713013
<jthread/>
1303813014
<description>

src/hotspot/share/prims/jvmtiEnv.cpp

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -536,58 +536,51 @@ JvmtiEnv::SetEventCallbacks(const jvmtiEventCallbacks* callbacks, jint size_of_c
536536
// event_thread - NULL is a valid value, must be checked
537537
jvmtiError
538538
JvmtiEnv::SetEventNotificationMode(jvmtiEventMode mode, jvmtiEvent event_type, jthread event_thread, ...) {
539+
bool enabled = (mode == JVMTI_ENABLE);
540+
541+
// event_type must be valid
542+
if (!JvmtiEventController::is_valid_event_type(event_type)) {
543+
return JVMTI_ERROR_INVALID_EVENT_TYPE;
544+
}
545+
546+
// assure that needed capabilities are present
547+
if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) {
548+
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
549+
}
550+
551+
if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) {
552+
record_class_file_load_hook_enabled();
553+
}
554+
539555
if (event_thread == NULL) {
540556
// Can be called at Agent_OnLoad() time with event_thread == NULL
541557
// when Thread::current() does not work yet so we cannot create a
542558
// ThreadsListHandle that is common to both thread-specific and
543559
// global code paths.
544560

545-
// event_type must be valid
546-
if (!JvmtiEventController::is_valid_event_type(event_type)) {
547-
return JVMTI_ERROR_INVALID_EVENT_TYPE;
548-
}
549-
550-
bool enabled = (mode == JVMTI_ENABLE);
551-
552-
// assure that needed capabilities are present
553-
if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) {
554-
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
555-
}
556-
557-
if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) {
558-
record_class_file_load_hook_enabled();
559-
}
560-
561561
JvmtiEventController::set_user_enabled(this, (JavaThread*) NULL, event_type, enabled);
562562
} else {
563563
// We have a specified event_thread.
564564
JavaThread* java_thread = NULL;
565565
ThreadsListHandle tlh;
566-
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), event_thread, &java_thread, NULL);
566+
oop thread_obj = NULL;
567+
JvmtiVTMTDisabler vtmt_disabler;
568+
569+
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), event_thread, &java_thread, &thread_obj);
567570
if (err != JVMTI_ERROR_NONE) {
568571
return err;
569572
}
570573

571-
// event_type must be valid
572-
if (!JvmtiEventController::is_valid_event_type(event_type)) {
573-
return JVMTI_ERROR_INVALID_EVENT_TYPE;
574-
}
575-
576574
// global events cannot be controlled at thread level.
577575
if (JvmtiEventController::is_global_event(event_type)) {
578576
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
579577
}
580578

581-
bool enabled = (mode == JVMTI_ENABLE);
582-
583579
// assure that needed capabilities are present
584-
if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) {
580+
if (java_lang_VirtualThread::is_instance(thread_obj) && !JvmtiExport::can_support_virtual_threads()) {
585581
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
586582
}
587583

588-
if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) {
589-
record_class_file_load_hook_enabled();
590-
}
591584
JvmtiEventController::set_user_enabled(this, java_thread, event_type, enabled);
592585
}
593586

@@ -1913,11 +1906,39 @@ JvmtiEnv::GetFrameLocation(jthread thread, jint depth, jmethodID* method_ptr, jl
19131906

19141907

19151908
// Threads_lock NOT held, java_thread not protected by lock
1916-
// java_thread - pre-checked
1917-
// java_thread - unchecked
1909+
// thread - NOT pre-checked
19181910
// depth - pre-checked as non-negative
19191911
jvmtiError
1920-
JvmtiEnv::NotifyFramePop(JavaThread* java_thread, jint depth) {
1912+
JvmtiEnv::NotifyFramePop(jthread thread, jint depth) {
1913+
jvmtiError err = JVMTI_ERROR_NONE;
1914+
ResourceMark rm;
1915+
JavaThread* java_thread = NULL;
1916+
oop thread_obj = NULL;
1917+
ThreadsListHandle tlh;
1918+
JvmtiVTMTDisabler vtmt_disabler;
1919+
1920+
err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
1921+
if (err != JVMTI_ERROR_NONE) {
1922+
return err;
1923+
}
1924+
1925+
// Support for virtual threads
1926+
if (java_lang_VirtualThread::is_instance(thread_obj)) {
1927+
if (!JvmtiExport::can_support_virtual_threads()) {
1928+
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
1929+
}
1930+
if (java_thread == NULL) {
1931+
// java_thread is NULL if virtual thread is unmounted
1932+
JvmtiThreadState *state = JvmtiThreadState::state_for(java_thread, thread_obj);
1933+
if (state == NULL) {
1934+
return JVMTI_ERROR_THREAD_NOT_ALIVE;
1935+
}
1936+
int frame_number = state->count_frames() - depth;
1937+
state->env_thread_state(this)->set_frame_pop(frame_number);
1938+
return JVMTI_ERROR_NONE;
1939+
}
1940+
}
1941+
19211942
JvmtiThreadState *state = JvmtiThreadState::state_for(java_thread);
19221943
if (state == NULL) {
19231944
return JVMTI_ERROR_THREAD_NOT_ALIVE;

src/hotspot/share/prims/jvmtiEnvThreadState.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ class JvmtiEnvThreadState : public CHeapObj<mtInternal> {
165165
return _single_stepping_posted;
166166
}
167167

168+
inline void set_thread(JavaThread* thread) { _thread = thread; }
168169
inline JavaThread *get_thread() { return _thread; }
169170
inline JvmtiEnv *get_env() { return _env; }
170171

src/hotspot/share/prims/jvmtiEventController.cpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -531,9 +531,15 @@ JvmtiEventControllerPrivate::recompute_thread_enabled(JvmtiThreadState *state) {
531531
// mark if event is truly enabled on this thread in any environment
532532
state->thread_event_enable()->_event_enabled.set_bits(any_env_enabled);
533533

534-
// update the JavaThread cached value for thread-specific should_post_on_exceptions value
535-
bool should_post_on_exceptions = (any_env_enabled & SHOULD_POST_ON_EXCEPTIONS_BITS) != 0;
536-
state->set_should_post_on_exceptions(should_post_on_exceptions);
534+
// pointer to JavaThread can be NULL for unmouted virtual thread
535+
if (state->get_thread() != NULL) {
536+
// update the JavaThread cached value for thread-specific should_post_on_exceptions value
537+
bool should_post_on_exceptions = (any_env_enabled & SHOULD_POST_ON_EXCEPTIONS_BITS) != 0;
538+
state->set_should_post_on_exceptions(should_post_on_exceptions);
539+
}
540+
}
541+
if (state->get_thread() == NULL) {
542+
return any_env_enabled;
537543
}
538544

539545
// compute interp_only mode
@@ -592,8 +598,18 @@ JvmtiEventControllerPrivate::recompute_enabled() {
592598
if ( (any_env_thread_enabled & THREAD_FILTERED_EVENT_BITS) != 0 &&
593599
(was_any_env_thread_enabled & THREAD_FILTERED_EVENT_BITS) == 0) {
594600
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *tp = jtiwh.next(); ) {
595-
// state_for_while_locked() makes tp->is_exiting() check
596-
JvmtiThreadState::state_for_while_locked(tp); // create the thread state if missing
601+
oop jt_oop = tp->threadObj();
602+
603+
// create the thread state if missing, state_for_while_locked() makes tp->is_exiting() check
604+
JvmtiThreadState* state = JvmtiThreadState::state_for_while_locked(tp, jt_oop);
605+
606+
// create the thread state for mounted virtual thread if missing
607+
if (JvmtiExport::can_support_virtual_threads()) {
608+
oop vt_oop = tp->mounted_vthread();
609+
if (vt_oop != NULL && java_lang_VirtualThread::is_instance(vt_oop)) {
610+
state = JvmtiThreadState::state_for_while_locked(tp, vt_oop);
611+
}
612+
}
597613
}
598614
}
599615

0 commit comments

Comments
 (0)