From e591f869fb476b16f8abe1b7fa95a9f9b2f63ff0 Mon Sep 17 00:00:00 2001 From: Serguei Spitsyn Date: Wed, 18 Feb 2026 03:41:56 -0800 Subject: [PATCH] review: simplify interp-only; remove _thread_saved; add more asserts --- src/hotspot/share/prims/jvmtiEnvBase.cpp | 4 +- .../share/prims/jvmtiEnvThreadState.cpp | 9 +--- .../share/prims/jvmtiEnvThreadState.hpp | 4 +- .../share/prims/jvmtiEventController.cpp | 25 ++++++---- src/hotspot/share/prims/jvmtiThreadState.cpp | 48 ++++++++----------- src/hotspot/share/prims/jvmtiThreadState.hpp | 11 +++-- .../share/prims/jvmtiThreadState.inline.hpp | 21 ++++---- 7 files changed, 57 insertions(+), 65 deletions(-) diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp index 4894a4dd21a..401bb4dfdb8 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.cpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -2491,7 +2491,7 @@ SetOrClearFramePopClosure::do_thread(Thread *target) { _result = JVMTI_ERROR_NO_MORE_FRAMES; return; } - assert(_state->get_thread_or_saved() == java_thread, "Must be"); + assert(_state->get_thread() == java_thread, "Must be"); RegisterMap reg_map(java_thread, RegisterMap::UpdateMap::include, diff --git a/src/hotspot/share/prims/jvmtiEnvThreadState.cpp b/src/hotspot/share/prims/jvmtiEnvThreadState.cpp index 571c4ca5528..303923076b1 100644 --- a/src/hotspot/share/prims/jvmtiEnvThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiEnvThreadState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -151,11 +151,6 @@ bool JvmtiEnvThreadState::is_virtual() { return _state->is_virtual(); } -// Use _thread_saved if cthread is detached from JavaThread (_thread == nullptr). -JavaThread* JvmtiEnvThreadState::get_thread_or_saved() { - return _state->get_thread_or_saved(); -} - JavaThread* JvmtiEnvThreadState::get_thread() { return _state->get_thread(); } @@ -344,7 +339,7 @@ void JvmtiEnvThreadState::reset_current_location(jvmtiEvent event_type, bool ena if (enabled) { // If enabling breakpoint, no need to reset. // Can't do anything if empty stack. - JavaThread* thread = get_thread_or_saved(); + JavaThread* thread = get_thread(); if (event_type == JVMTI_EVENT_SINGLE_STEP && ((thread == nullptr && is_virtual()) || thread->has_last_Java_frame())) { diff --git a/src/hotspot/share/prims/jvmtiEnvThreadState.hpp b/src/hotspot/share/prims/jvmtiEnvThreadState.hpp index a2ab25a59dd..16b369e0857 100644 --- a/src/hotspot/share/prims/jvmtiEnvThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiEnvThreadState.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -170,8 +170,6 @@ public: inline JvmtiThreadState* jvmti_thread_state() { return _state; } - // use _thread_saved if cthread is detached from JavaThread - JavaThread *get_thread_or_saved(); JavaThread *get_thread(); inline JvmtiEnv *get_env() { return _env; } diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp index 9df3bbb4b3e..14c782856a6 100644 --- a/src/hotspot/share/prims/jvmtiEventController.cpp +++ b/src/hotspot/share/prims/jvmtiEventController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -217,6 +217,10 @@ class EnterInterpOnlyModeClosure : public HandshakeClosure { assert(state != nullptr, "sanity check"); assert(state->get_thread() == jt, "handshake unsafe conditions"); + assert(jt->jvmti_thread_state() == state, "sanity check"); + assert(!jt->is_interp_only_mode(), "sanity check"); + assert(!state->is_interp_only_mode(), "sanity check"); + if (!state->is_pending_interp_only_mode()) { _completed = true; return; // The pending flag has been already cleared, so bail out. @@ -361,7 +365,8 @@ void VM_ChangeSingleStep::doit() { void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state) { EC_TRACE(("[%s] # Entering interpreter only mode", - JvmtiTrace::safe_get_thread_name(state->get_thread_or_saved()))); + JvmtiTrace::safe_get_thread_name(state->get_thread()))); + JavaThread *target = state->get_thread(); Thread *current = Thread::current(); @@ -371,7 +376,8 @@ void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state } // This flag will be cleared in EnterInterpOnlyModeClosure handshake. state->set_pending_interp_only_mode(true); - if (target == nullptr) { // an unmounted virtual thread + if (target == nullptr || // an unmounted virtual thread + JvmtiEnvBase::is_thread_carrying_vthread(target, state->get_thread_oop())) { // a vthread carrying thread return; // EnterInterpOnlyModeClosure will be executed right after mount. } EnterInterpOnlyModeClosure hs(state); @@ -388,7 +394,8 @@ void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state void JvmtiEventControllerPrivate::leave_interp_only_mode(JvmtiThreadState *state) { EC_TRACE(("[%s] # Leaving interpreter only mode", - JvmtiTrace::safe_get_thread_name(state->get_thread_or_saved()))); + JvmtiTrace::safe_get_thread_name(state->get_thread()))); + if (state->is_pending_interp_only_mode()) { state->set_pending_interp_only_mode(false); // Just clear the pending flag. assert(!state->is_interp_only_mode(), "sanity check"); @@ -409,7 +416,7 @@ JvmtiEventControllerPrivate::trace_changed(JvmtiThreadState *state, jlong now_en if (changed & bit) { // it changed, print it log_trace(jvmti)("[%s] # %s event %s", - JvmtiTrace::safe_get_thread_name(state->get_thread_or_saved()), + JvmtiTrace::safe_get_thread_name(state->get_thread()), (now_enabled & bit)? "Enabling" : "Disabling", JvmtiTrace::event_name((jvmtiEvent)ei)); } } @@ -932,7 +939,7 @@ JvmtiEventControllerPrivate::set_user_enabled(JvmtiEnvBase *env, JavaThread *thr void JvmtiEventControllerPrivate::set_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { EC_TRACE(("[%s] # set frame pop - frame=%d", - JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()), + JvmtiTrace::safe_get_thread_name(ets->get_thread()), fpop.frame_number() )); ets->get_frame_pops()->set(fpop); @@ -943,7 +950,7 @@ JvmtiEventControllerPrivate::set_frame_pop(JvmtiEnvThreadState *ets, JvmtiFrameP void JvmtiEventControllerPrivate::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { EC_TRACE(("[%s] # clear frame pop - frame=%d", - JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()), + JvmtiTrace::safe_get_thread_name(ets->get_thread()), fpop.frame_number() )); ets->get_frame_pops()->clear(fpop); @@ -953,7 +960,7 @@ JvmtiEventControllerPrivate::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFram void JvmtiEventControllerPrivate::clear_all_frame_pops(JvmtiEnvThreadState *ets) { EC_TRACE(("[%s] # clear all frame pops", - JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()) + JvmtiTrace::safe_get_thread_name(ets->get_thread()) )); ets->get_frame_pops()->clear_all(); @@ -965,7 +972,7 @@ JvmtiEventControllerPrivate::clear_to_frame_pop(JvmtiEnvThreadState *ets, JvmtiF int cleared_cnt = ets->get_frame_pops()->clear_to(fpop); EC_TRACE(("[%s] # clear to frame pop - frame=%d, count=%d", - JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()), + JvmtiTrace::safe_get_thread_name(ets->get_thread()), fpop.frame_number(), cleared_cnt )); diff --git a/src/hotspot/share/prims/jvmtiThreadState.cpp b/src/hotspot/share/prims/jvmtiThreadState.cpp index 489a5fc44e2..1264b8b9e37 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiThreadState.cpp @@ -58,19 +58,10 @@ JvmtiThreadState::JvmtiThreadState(JavaThread* thread, oop thread_oop) assert(JvmtiThreadState_lock->is_locked(), "sanity check"); // The _thread field is a link to the JavaThread associated with JvmtiThreadState. - // The _thread_saved field is used for carrier threads only when a virtual thread - // is mounted. Otherwise, it must be set to nullptr. - // Carrier and virtual threads can temporarily share same JavaThread. In such a case, - // only virtual _thread should have a link from JvmtiThreadState to JavaThread. - // The carrier thread _thread field is set to nullptr if a virtual thread is monted. - // This is important for interp-only mechanism. - if (JvmtiEnvBase::is_thread_carrying_vthread(thread, thread_oop)) { - _thread = nullptr; - _thread_saved = thread; - } else { // virtual or non-carrying platform thread - _thread = thread; - _thread_saved = nullptr; - } + // A carrier thread shgould always have a stable link to its JavaThread. + // The _thread field of a virtual thread should point to the JavaThread when + // virtual thread is mounted. It should be set to null when it is unmounted. + _thread = thread; _exception_state = ES_CLEARED; _hide_single_stepping = false; _pending_interp_only_mode = false; @@ -131,11 +122,11 @@ JvmtiThreadState::JvmtiThreadState(JavaThread* thread, oop thread_oop) if (thread != nullptr) { if (thread_oop == nullptr || thread->jvmti_vthread() == nullptr || thread->jvmti_vthread() == thread_oop) { - // The JavaThread for carrier or mounted virtual thread case. + // The JavaThread for an active carrier or a mounted virtual thread case. // Set this only if thread_oop is current thread->jvmti_vthread(). thread->set_jvmti_thread_state(this); + thread->set_interp_only_mode(false); } - thread->set_interp_only_mode(false); } } @@ -148,7 +139,11 @@ JvmtiThreadState::~JvmtiThreadState() { } // clear this as the state for the thread - get_thread()->set_jvmti_thread_state(nullptr); + assert(get_thread() != nullptr, "sanity check"); + if (get_thread()->jvmti_thread_state() == this) { // check for safety + get_thread()->set_jvmti_thread_state(nullptr); + get_thread()->set_interp_only_mode(false); + } // zap our env thread states { @@ -335,6 +330,9 @@ void JvmtiThreadState::add_env(JvmtiEnvBase *env) { void JvmtiThreadState::enter_interp_only_mode() { assert(_thread != nullptr, "sanity check"); assert(!is_interp_only_mode(), "entering interp only when in interp only mode"); + assert(_thread->jvmti_vthread() == nullptr || _thread->jvmti_vthread() == get_thread_oop(), "sanity check"); + assert(_thread->jvmti_thread_state() == this, "sanity check"); + _saved_interp_only_mode = true; _seen_interp_only_mode = true; _thread->set_interp_only_mode(true); invalidate_cur_stack_depth(); @@ -342,10 +340,9 @@ void JvmtiThreadState::enter_interp_only_mode() { void JvmtiThreadState::leave_interp_only_mode() { assert(is_interp_only_mode(), "leaving interp only when not in interp only mode"); - if (_thread == nullptr) { - // Unmounted virtual thread updates the saved value. - _saved_interp_only_mode = false; - } else { + _saved_interp_only_mode = false; + if (_thread != nullptr && _thread->jvmti_thread_state() == this) { + assert(_thread->jvmti_vthread() == nullptr || _thread->jvmti_vthread() == get_thread_oop(), "sanity check"); _thread->set_interp_only_mode(false); } } @@ -353,7 +350,7 @@ void JvmtiThreadState::leave_interp_only_mode() { // Helper routine used in several places int JvmtiThreadState::count_frames() { - JavaThread* thread = get_thread_or_saved(); + JavaThread* thread = get_thread(); javaVFrame *jvf; ResourceMark rm; if (thread == nullptr) { @@ -492,8 +489,6 @@ void JvmtiThreadState::update_for_pop_top_frame() { } // force stack depth to be recalculated invalidate_cur_stack_depth(); - } else { - assert(!is_enabled(JVMTI_EVENT_FRAME_POP), "Must have no framepops set"); } } @@ -592,11 +587,8 @@ void JvmtiThreadState::update_thread_oop_during_vm_start() { } } +// For virtual threads only. void JvmtiThreadState::set_thread(JavaThread* thread) { - _thread_saved = nullptr; // Common case. - if (!_is_virtual && thread == nullptr) { - // Save JavaThread* if carrier thread is being detached. - _thread_saved = _thread; - } + assert(is_virtual(), "sanity check"); _thread = thread; } diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp index 43b568cf1fc..5f2d03a2e81 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -124,7 +124,6 @@ class JvmtiThreadState : public CHeapObj { private: friend class JvmtiEnv; JavaThread *_thread; - JavaThread *_thread_saved; OopHandle _thread_oop_h; // Jvmti Events that cannot be posted in their current context. JvmtiDeferredEventQueue* _jvmti_event_queue; @@ -216,7 +215,7 @@ class JvmtiThreadState : public CHeapObj { // Used by the interpreter for fullspeed debugging support bool is_interp_only_mode() { - return _thread == nullptr ? _saved_interp_only_mode : _thread->is_interp_only_mode(); + return _saved_interp_only_mode; } void enter_interp_only_mode(); void leave_interp_only_mode(); @@ -245,8 +244,10 @@ class JvmtiThreadState : public CHeapObj { int count_frames(); - inline JavaThread *get_thread() { return _thread; } - inline JavaThread *get_thread_or_saved(); // return _thread_saved if _thread is null + inline JavaThread *get_thread() { + assert(is_virtual() || _thread != nullptr, "sanity check"); + return _thread; + } // Needed for virtual threads as they can migrate to different JavaThread's. // Also used for carrier threads to clear/restore _thread. diff --git a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp index 2b060f1a2e4..da6ff43e9dc 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, 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 @@ -130,22 +130,21 @@ inline JvmtiThreadState* JvmtiThreadState::state_for(JavaThread *thread, Handle return state; } -inline JavaThread* JvmtiThreadState::get_thread_or_saved() { - // Use _thread_saved if cthread is detached from JavaThread (_thread == null). - return (_thread == nullptr && !is_virtual()) ? _thread_saved : _thread; -} - inline void JvmtiThreadState::set_should_post_on_exceptions(bool val) { - get_thread_or_saved()->set_should_post_on_exceptions_flag(val ? JNI_TRUE : JNI_FALSE); + get_thread()->set_should_post_on_exceptions_flag(val ? JNI_TRUE : JNI_FALSE); } inline void JvmtiThreadState::unbind_from(JvmtiThreadState* state, JavaThread* thread) { if (state == nullptr) { + assert(!thread->is_interp_only_mode(), "sanity check"); return; } - // Save thread's interp_only_mode. - state->_saved_interp_only_mode = thread->is_interp_only_mode(); - state->set_thread(nullptr); // Make sure stale _thread value is never used. + assert(thread->jvmti_thread_state() == state, "sanity check"); + assert(state->get_thread() == thread, "sanity check"); + assert(thread->is_interp_only_mode() == state->_saved_interp_only_mode, "sanity check"); + if (state->is_virtual()) { // clean _thread link for virtual threads only + state->set_thread(nullptr); // make sure stale _thread value is never used + } } inline void JvmtiThreadState::bind_to(JvmtiThreadState* state, JavaThread* thread) { @@ -158,7 +157,7 @@ inline void JvmtiThreadState::bind_to(JvmtiThreadState* state, JavaThread* threa // Bind JavaThread to JvmtiThreadState. thread->set_jvmti_thread_state(state); - if (state != nullptr) { + if (state != nullptr && state->is_virtual()) { // Bind to JavaThread. state->set_thread(thread); }