From 42ab8fcfb98eacb2d93f59c012360a99a16e5450 Mon Sep 17 00:00:00 2001 From: Anton Artemov Date: Wed, 11 Jun 2025 12:42:57 +0000 Subject: [PATCH] 8265754: Move suspend/resume API from HandshakeState Reviewed-by: coleenp, dholmes, pchilanomate --- src/hotspot/share/runtime/handshake.cpp | 126 +------------- src/hotspot/share/runtime/handshake.hpp | 34 +--- src/hotspot/share/runtime/javaThread.cpp | 5 +- src/hotspot/share/runtime/javaThread.hpp | 7 +- .../share/runtime/suspendResumeManager.cpp | 158 ++++++++++++++++++ .../share/runtime/suspendResumeManager.hpp | 70 ++++++++ 6 files changed, 241 insertions(+), 159 deletions(-) create mode 100644 src/hotspot/share/runtime/suspendResumeManager.cpp create mode 100644 src/hotspot/share/runtime/suspendResumeManager.hpp diff --git a/src/hotspot/share/runtime/handshake.cpp b/src/hotspot/share/runtime/handshake.cpp index 6bed5e9d546..2c827b61602 100644 --- a/src/hotspot/share/runtime/handshake.cpp +++ b/src/hotspot/share/runtime/handshake.cpp @@ -465,9 +465,7 @@ HandshakeState::HandshakeState(JavaThread* target) : _queue(), _lock(Monitor::nosafepoint, "HandshakeState_lock"), _active_handshaker(), - _async_exceptions_blocked(false), - _suspended(false), - _async_suspend_handshake(false) { + _async_exceptions_blocked(false) { } HandshakeState::~HandshakeState() { @@ -699,128 +697,8 @@ HandshakeState::ProcessResult HandshakeState::try_process(HandshakeOperation* ma return op == match_op ? HandshakeState::_succeeded : HandshakeState::_processed; } -void HandshakeState::do_self_suspend() { - assert(Thread::current() == _handshakee, "should call from _handshakee"); - assert(_lock.owned_by_self(), "Lock must be held"); - assert(!_handshakee->has_last_Java_frame() || _handshakee->frame_anchor()->walkable(), "should have walkable stack"); - assert(_handshakee->thread_state() == _thread_blocked, "Caller should have transitioned to _thread_blocked"); - - while (is_suspended()) { - log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended", p2i(_handshakee)); - _lock.wait_without_safepoint_check(); - } - log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " resumed", p2i(_handshakee)); -} - -// This is the closure that prevents a suspended JavaThread from -// escaping the suspend request. -class ThreadSelfSuspensionHandshake : public AsyncHandshakeClosure { - public: - ThreadSelfSuspensionHandshake() : AsyncHandshakeClosure("ThreadSelfSuspensionHandshake") {} - void do_thread(Thread* thr) { - JavaThread* current = JavaThread::cast(thr); - assert(current == Thread::current(), "Must be self executed."); - JavaThreadState jts = current->thread_state(); - - current->set_thread_state(_thread_blocked); - current->handshake_state()->do_self_suspend(); - current->set_thread_state(jts); - current->handshake_state()->set_async_suspend_handshake(false); - } - virtual bool is_suspend() { return true; } -}; - -bool HandshakeState::suspend_with_handshake(bool register_vthread_SR) { - assert(_handshakee->threadObj() != nullptr, "cannot suspend with a null threadObj"); - if (_handshakee->is_exiting()) { - log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " exiting", p2i(_handshakee)); - return false; - } - if (has_async_suspend_handshake()) { - if (is_suspended()) { - // Target is already suspended. - log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " already suspended", p2i(_handshakee)); - return false; - } else { - // Target is going to wake up and leave suspension. - // Let's just stop the thread from doing that. - log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " re-suspended", p2i(_handshakee)); - set_suspended(true, register_vthread_SR); - return true; - } - } - // no suspend request - assert(!is_suspended(), "cannot be suspended without a suspend request"); - // Thread is safe, so it must execute the request, thus we can count it as suspended - // from this point. - set_suspended(true, register_vthread_SR); - set_async_suspend_handshake(true); - log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended, arming ThreadSuspension", p2i(_handshakee)); - ThreadSelfSuspensionHandshake* ts = new ThreadSelfSuspensionHandshake(); - Handshake::execute(ts, _handshakee); - return true; -} - -// This is the closure that synchronously honors the suspend request. -class SuspendThreadHandshake : public HandshakeClosure { - bool _register_vthread_SR; - bool _did_suspend; -public: - SuspendThreadHandshake(bool register_vthread_SR) : HandshakeClosure("SuspendThread"), - _register_vthread_SR(register_vthread_SR), _did_suspend(false) {} - void do_thread(Thread* thr) { - JavaThread* target = JavaThread::cast(thr); - _did_suspend = target->handshake_state()->suspend_with_handshake(_register_vthread_SR); - } - bool did_suspend() { return _did_suspend; } -}; - -bool HandshakeState::suspend(bool register_vthread_SR) { - JVMTI_ONLY(assert(!_handshakee->is_in_VTMS_transition(), "no suspend allowed in VTMS transition");) - JavaThread* self = JavaThread::current(); - if (_handshakee == self) { - // If target is the current thread we can bypass the handshake machinery - // and just suspend directly - ThreadBlockInVM tbivm(self); - MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag); - set_suspended(true, register_vthread_SR); - do_self_suspend(); - return true; - } else { - SuspendThreadHandshake st(register_vthread_SR); - Handshake::execute(&st, _handshakee); - return st.did_suspend(); - } -} - -bool HandshakeState::resume(bool register_vthread_SR) { - MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag); - if (!is_suspended()) { - assert(!_handshakee->is_suspended(), "cannot be suspended without a suspend request"); - return false; - } - // Resume the thread. - set_suspended(false, register_vthread_SR); - _lock.notify(); - return true; -} - -void HandshakeState::set_suspended(bool is_suspend, bool register_vthread_SR) { -#if INCLUDE_JVMTI - if (register_vthread_SR) { - assert(_handshakee->is_vthread_mounted(), "sanity check"); - if (is_suspend) { - JvmtiVTSuspender::register_vthread_suspend(_handshakee->vthread()); - } else { - JvmtiVTSuspender::register_vthread_resume(_handshakee->vthread()); - } - } -#endif - Atomic::store(&_suspended, is_suspend); -} - void HandshakeState::handle_unsafe_access_error() { - if (is_suspended()) { + if (_handshakee->is_suspended()) { // A suspend handshake was added to the queue after the // unsafe access error. Since the suspender has already // considered this JT as suspended and assumes it won't go diff --git a/src/hotspot/share/runtime/handshake.hpp b/src/hotspot/share/runtime/handshake.hpp index 9e963003053..ca9cef76f5d 100644 --- a/src/hotspot/share/runtime/handshake.hpp +++ b/src/hotspot/share/runtime/handshake.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, 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 @@ -35,8 +35,6 @@ class HandshakeOperation; class AsyncHandshakeOperation; class JavaThread; -class SuspendThreadHandshake; -class ThreadSelfSuspensionHandshake; class UnsafeAccessErrorHandshake; class ThreadsListHandle; @@ -88,8 +86,6 @@ class JvmtiRawMonitor; // operation is only done by either VMThread/Handshaker on behalf of the // JavaThread or by the target JavaThread itself. class HandshakeState { - friend ThreadSelfSuspensionHandshake; - friend SuspendThreadHandshake; friend UnsafeAccessErrorHandshake; friend JavaThread; // This a back reference to the JavaThread, @@ -98,7 +94,7 @@ class HandshakeState { // The queue containing handshake operations to be performed on _handshakee. FilterQueue _queue; // Provides mutual exclusion to this state and queue. Also used for - // JavaThread suspend/resume operations. + // JavaThread suspend/resume operations performed by SuspendResumeManager. Monitor _lock; // Set to the thread executing the handshake operation. Thread* volatile _active_handshaker; @@ -160,31 +156,5 @@ class HandshakeState { bool async_exceptions_blocked() { return _async_exceptions_blocked; } void set_async_exceptions_blocked(bool b) { _async_exceptions_blocked = b; } void handle_unsafe_access_error(); - - // Suspend/resume support - private: - // This flag is true when the thread owning this - // HandshakeState (the _handshakee) is suspended. - volatile bool _suspended; - // This flag is true while there is async handshake (trap) - // on queue. Since we do only need one, we can reuse it if - // thread gets suspended again (after a resume) - // and we have not yet processed it. - bool _async_suspend_handshake; - - // Called from the suspend handshake. - bool suspend_with_handshake(bool register_vthread_SR); - // Called from the async handshake (the trap) - // to stop a thread from continuing execution when suspended. - void do_self_suspend(); - - bool is_suspended() { return Atomic::load(&_suspended); } - void set_suspended(bool to, bool register_vthread_SR); - bool has_async_suspend_handshake() { return _async_suspend_handshake; } - void set_async_suspend_handshake(bool to) { _async_suspend_handshake = to; } - - bool suspend(bool register_vthread_SR); - bool resume(bool register_vthread_SR); }; - #endif // SHARE_RUNTIME_HANDSHAKE_HPP diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index f3ad0e0a325..37ecb1c6f9a 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -498,6 +498,7 @@ JavaThread::JavaThread(MemTag mem_tag) : _pending_interrupted_exception(false), _handshake(this), + _suspend_resume_manager(this, &_handshake._lock), _popframe_preserved_args(nullptr), _popframe_preserved_args_size(0), @@ -1200,13 +1201,13 @@ bool JavaThread::java_suspend(bool register_vthread_SR) { guarantee(Thread::is_JavaThread_protected(/* target */ this), "target JavaThread is not protected in calling context."); - return this->handshake_state()->suspend(register_vthread_SR); + return this->suspend_resume_manager()->suspend(register_vthread_SR); } bool JavaThread::java_resume(bool register_vthread_SR) { guarantee(Thread::is_JavaThread_protected_by_TLH(/* target */ this), "missing ThreadsListHandle in calling context."); - return this->handshake_state()->resume(register_vthread_SR); + return this->suspend_resume_manager()->resume(register_vthread_SR); } // Wait for another thread to perform object reallocation and relocking on behalf of diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index 968dfd0ce48..af46492622d 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -40,6 +40,7 @@ #include "runtime/safepointMechanism.hpp" #include "runtime/stackWatermarkSet.hpp" #include "runtime/stackOverflow.hpp" +#include "runtime/suspendResumeManager.hpp" #include "runtime/thread.hpp" #include "runtime/threadHeapSampler.hpp" #include "runtime/threadIdentifier.hpp" @@ -694,9 +695,13 @@ private: // Suspend/resume support for JavaThread // higher-level suspension/resume logic called by the public APIs +private: + SuspendResumeManager _suspend_resume_manager; +public: bool java_suspend(bool register_vthread_SR); bool java_resume(bool register_vthread_SR); - bool is_suspended() { return _handshake.is_suspended(); } + bool is_suspended() { return _suspend_resume_manager.is_suspended(); } + SuspendResumeManager* suspend_resume_manager() { return &_suspend_resume_manager; } // Check for async exception in addition to safepoint. static void check_special_condition_for_native_trans(JavaThread *thread); diff --git a/src/hotspot/share/runtime/suspendResumeManager.cpp b/src/hotspot/share/runtime/suspendResumeManager.cpp new file mode 100644 index 00000000000..fd14f73f553 --- /dev/null +++ b/src/hotspot/share/runtime/suspendResumeManager.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2025, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "logging/log.hpp" +#include "logging/logStream.hpp" +#include "memory/resourceArea.hpp" +#include "prims/jvmtiThreadState.hpp" +#include "runtime/atomic.hpp" +#include "runtime/globals.hpp" +#include "runtime/handshake.hpp" +#include "runtime/interfaceSupport.inline.hpp" +#include "runtime/javaThread.inline.hpp" +#include "runtime/suspendResumeManager.hpp" + +// This is the closure that prevents a suspended JavaThread from +// escaping the suspend request. +class ThreadSelfSuspensionHandshake : public AsyncHandshakeClosure { +public: + ThreadSelfSuspensionHandshake() : AsyncHandshakeClosure("ThreadSelfSuspensionHandshake") {} + void do_thread(Thread* thr) { + JavaThread* current = JavaThread::cast(thr); + assert(current == Thread::current(), "Must be self executed."); + JavaThreadState jts = current->thread_state(); + + current->set_thread_state(_thread_blocked); + current->suspend_resume_manager()->do_owner_suspend(); + current->set_thread_state(jts); + current->suspend_resume_manager()->set_async_suspend_handshake(false); + } + virtual bool is_suspend() { return true; } +}; + +// This is the closure that synchronously honors the suspend request. +class SuspendThreadHandshake : public HandshakeClosure { + bool _register_vthread_SR; + bool _did_suspend; +public: + SuspendThreadHandshake(bool register_vthread_SR) : HandshakeClosure("SuspendThread"), + _register_vthread_SR(register_vthread_SR), _did_suspend(false) { + } + void do_thread(Thread* thr) { + JavaThread* target = JavaThread::cast(thr); + _did_suspend = target->suspend_resume_manager()->suspend_with_handshake(_register_vthread_SR); + } + bool did_suspend() { return _did_suspend; } +}; + +void SuspendResumeManager::set_suspended(bool is_suspend, bool register_vthread_SR) { +#if INCLUDE_JVMTI + if (register_vthread_SR) { + assert(_target->is_vthread_mounted(), "sanity check"); + if (is_suspend) { + JvmtiVTSuspender::register_vthread_suspend(_target->vthread()); + } + else { + JvmtiVTSuspender::register_vthread_resume(_target->vthread()); + } + } +#endif + Atomic::store(&_suspended, is_suspend); +} + +bool SuspendResumeManager::suspend(bool register_vthread_SR) { + JVMTI_ONLY(assert(!_target->is_in_VTMS_transition(), "no suspend allowed in VTMS transition");) + JavaThread* self = JavaThread::current(); + if (_target == self) { + // If target is the current thread we can bypass the handshake machinery + // and just suspend directly + ThreadBlockInVM tbivm(self); + MutexLocker ml(_state_lock, Mutex::_no_safepoint_check_flag); + set_suspended(true, register_vthread_SR); + do_owner_suspend(); + return true; + } else { + SuspendThreadHandshake st(register_vthread_SR); + Handshake::execute(&st, _target); + return st.did_suspend(); + } +} + +bool SuspendResumeManager::resume(bool register_vthread_SR) { + MutexLocker ml(_state_lock, Mutex::_no_safepoint_check_flag); + if (!is_suspended()) { + assert(!_target->is_suspended(), "cannot be suspended without a suspend request"); + return false; + } + // Resume the thread. + set_suspended(false, register_vthread_SR); + _state_lock->notify(); + return true; +} + +void SuspendResumeManager::do_owner_suspend() { + assert(Thread::current() == _target, "should call from _target"); + assert(_state_lock->owned_by_self(), "Lock must be held"); + assert(!_target->has_last_Java_frame() || _target->frame_anchor()->walkable(), "should have walkable stack"); + assert(_target->thread_state() == _thread_blocked, "Caller should have transitioned to _thread_blocked"); + + while (is_suspended()) { + log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended", p2i(_target)); + _state_lock->wait_without_safepoint_check(); + } + log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " resumed", p2i(_target)); +} + +bool SuspendResumeManager::suspend_with_handshake(bool register_vthread_SR) { + assert(_target->threadObj() != nullptr, "cannot suspend with a null threadObj"); + if (_target->is_exiting()) { + log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " exiting", p2i(_target)); + return false; + } + if (has_async_suspend_handshake()) { + if (is_suspended()) { + // Target is already suspended. + log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " already suspended", p2i(_target)); + return false; + } else { + // Target is going to wake up and leave suspension. + // Let's just stop the thread from doing that. + log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " re-suspended", p2i(_target)); + set_suspended(true, register_vthread_SR); + return true; + } + } + // no suspend request + assert(!is_suspended(), "cannot be suspended without a suspend request"); + // Thread is safe, so it must execute the request, thus we can count it as suspended + // from this point. + set_suspended(true, register_vthread_SR); + set_async_suspend_handshake(true); + log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended, arming ThreadSuspension", p2i(_target)); + ThreadSelfSuspensionHandshake* ts = new ThreadSelfSuspensionHandshake(); + Handshake::execute(ts, _target); + return true; +} + +SuspendResumeManager::SuspendResumeManager(JavaThread* thread, Monitor* state_lock) : _target(thread), _state_lock(state_lock), _suspended(false), _async_suspend_handshake(false) {} diff --git a/src/hotspot/share/runtime/suspendResumeManager.hpp b/src/hotspot/share/runtime/suspendResumeManager.hpp new file mode 100644 index 00000000000..fed3b34055e --- /dev/null +++ b/src/hotspot/share/runtime/suspendResumeManager.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2025, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_RUNTIME_SUSPENDRESUMEMANAGER_HPP +#define SHARE_RUNTIME_SUSPENDRESUMEMANAGER_HPP + +class SuspendThreadHandshake; +class ThreadSelfSuspensionHandshake; + +class SuspendResumeManager { + friend SuspendThreadHandshake; + friend ThreadSelfSuspensionHandshake; + friend JavaThread; + + JavaThread* _target; + Monitor* _state_lock; + + SuspendResumeManager(JavaThread* thread, Monitor* state_lock); + + // This flag is true when the thread owning this + // SuspendResumeManager (the _target) is suspended. + volatile bool _suspended; + // This flag is true while there is async handshake (trap) + // on queue. Since we do only need one, we can reuse it if + // thread gets suspended again (after a resume) + // and we have not yet processed it. + bool _async_suspend_handshake; + + bool suspend(bool register_vthread_SR); + bool resume(bool register_vthread_SR); + + // Called from the async handshake (the trap) + // to stop a thread from continuing execution when suspended. + void do_owner_suspend(); + + // Called from the suspend handshake. + bool suspend_with_handshake(bool register_vthread_SR); + + void set_suspended(bool to, bool register_vthread_SR); + + bool is_suspended() { + return Atomic::load(&_suspended); + } + + bool has_async_suspend_handshake() { return _async_suspend_handshake; } + void set_async_suspend_handshake(bool to) { _async_suspend_handshake = to; } +}; + +#endif // SHARE_RUNTIME_SUSPENDRESUMEMANAGER_HPP