| /* | |
| * 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 | |
| * 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 "classfile/javaClasses.hpp" | |
| #include "classfile/vmSymbols.hpp" | |
| #include "jvm_io.h" | |
| #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/os.hpp" | |
| #include "runtime/osThread.hpp" | |
| #include "runtime/stackWatermarkSet.hpp" | |
| #include "runtime/task.hpp" | |
| #include "runtime/threadSMR.hpp" | |
| #include "runtime/vmThread.hpp" | |
| #include "utilities/formatBuffer.hpp" | |
| #include "utilities/filterQueue.inline.hpp" | |
| #include "utilities/globalDefinitions.hpp" | |
| #include "utilities/preserveException.hpp" | |
| #include "utilities/systemMemoryBarrier.hpp" | |
| class HandshakeOperation : public CHeapObj<mtThread> { | |
| friend class HandshakeState; | |
| protected: | |
| HandshakeClosure* _handshake_cl; | |
| // Keeps track of emitted and completed handshake operations. | |
| // Once it reaches zero all handshake operations have been performed. | |
| int32_t _pending_threads; | |
| JavaThread* _target; | |
| Thread* _requester; | |
| // Must use AsyncHandshakeOperation when using AsyncHandshakeClosure. | |
| HandshakeOperation(AsyncHandshakeClosure* cl, JavaThread* target, Thread* requester) : | |
| _handshake_cl(cl), | |
| _pending_threads(1), | |
| _target(target), | |
| _requester(requester) {} | |
| public: | |
| HandshakeOperation(HandshakeClosure* cl, JavaThread* target, Thread* requester) : | |
| _handshake_cl(cl), | |
| _pending_threads(1), | |
| _target(target), | |
| _requester(requester) {} | |
| virtual ~HandshakeOperation() {} | |
| void prepare(JavaThread* current_target, Thread* executing_thread); | |
| void do_handshake(JavaThread* thread); | |
| bool is_completed() { | |
| int32_t val = Atomic::load(&_pending_threads); | |
| assert(val >= 0, "_pending_threads=%d cannot be negative", val); | |
| return val == 0; | |
| } | |
| void add_target_count(int count) { Atomic::add(&_pending_threads, count); } | |
| int32_t pending_threads() { return Atomic::load(&_pending_threads); } | |
| const char* name() { return _handshake_cl->name(); } | |
| bool is_async() { return _handshake_cl->is_async(); } | |
| bool is_suspend() { return _handshake_cl->is_suspend(); } | |
| bool is_async_exception() { return _handshake_cl->is_async_exception(); } | |
| }; | |
| class AsyncHandshakeOperation : public HandshakeOperation { | |
| private: | |
| jlong _start_time_ns; | |
| public: | |
| AsyncHandshakeOperation(AsyncHandshakeClosure* cl, JavaThread* target, jlong start_ns) | |
| : HandshakeOperation(cl, target, nullptr), _start_time_ns(start_ns) {} | |
| virtual ~AsyncHandshakeOperation() { delete _handshake_cl; } | |
| jlong start_time() const { return _start_time_ns; } | |
| }; | |
| // Performing handshakes requires a custom yielding strategy because without it | |
| // there is a clear performance regression vs plain spinning. We keep track of | |
| // when we last saw progress by looking at why each targeted thread has not yet | |
| // completed its handshake. After spinning for a while with no progress we will | |
| // yield, but as long as there is progress, we keep spinning. Thus we avoid | |
| // yielding when there is potential work to be done or the handshake is close | |
| // to being finished. | |
| class HandshakeSpinYield : public StackObj { | |
| private: | |
| jlong _start_time_ns; | |
| jlong _last_spin_start_ns; | |
| jlong _spin_time_ns; | |
| int _result_count[2][HandshakeState::_number_states]; | |
| int _prev_result_pos; | |
| int current_result_pos() { return (_prev_result_pos + 1) & 0x1; } | |
| void wait_raw(jlong now) { | |
| // We start with fine-grained nanosleeping until a millisecond has | |
| // passed, at which point we resort to plain naked_short_sleep. | |
| if (now - _start_time_ns < NANOSECS_PER_MILLISEC) { | |
| os::naked_short_nanosleep(10 * (NANOUNITS / MICROUNITS)); | |
| } else { | |
| os::naked_short_sleep(1); | |
| } | |
| } | |
| void wait_blocked(JavaThread* self, jlong now) { | |
| ThreadBlockInVM tbivm(self); | |
| wait_raw(now); | |
| } | |
| bool state_changed() { | |
| for (int i = 0; i < HandshakeState::_number_states; i++) { | |
| if (_result_count[0][i] != _result_count[1][i]) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| void reset_state() { | |
| _prev_result_pos++; | |
| for (int i = 0; i < HandshakeState::_number_states; i++) { | |
| _result_count[current_result_pos()][i] = 0; | |
| } | |
| } | |
| public: | |
| HandshakeSpinYield(jlong start_time) : | |
| _start_time_ns(start_time), _last_spin_start_ns(start_time), | |
| _spin_time_ns(0), _result_count(), _prev_result_pos(0) { | |
| const jlong max_spin_time_ns = 100 /* us */ * (NANOUNITS / MICROUNITS); | |
| int free_cpus = os::active_processor_count() - 1; | |
| _spin_time_ns = (5 /* us */ * (NANOUNITS / MICROUNITS)) * free_cpus; // zero on UP | |
| _spin_time_ns = _spin_time_ns > max_spin_time_ns ? max_spin_time_ns : _spin_time_ns; | |
| } | |
| void add_result(HandshakeState::ProcessResult pr) { | |
| _result_count[current_result_pos()][pr]++; | |
| } | |
| void process() { | |
| jlong now = os::javaTimeNanos(); | |
| if (state_changed()) { | |
| reset_state(); | |
| // We spin for x amount of time since last state change. | |
| _last_spin_start_ns = now; | |
| return; | |
| } | |
| jlong wait_target = _last_spin_start_ns + _spin_time_ns; | |
| if (wait_target < now) { | |
| // On UP this is always true. | |
| Thread* self = Thread::current(); | |
| if (self->is_Java_thread()) { | |
| wait_blocked(JavaThread::cast(self), now); | |
| } else { | |
| wait_raw(now); | |
| } | |
| _last_spin_start_ns = os::javaTimeNanos(); | |
| } | |
| reset_state(); | |
| } | |
| }; | |
| static void handle_timeout(HandshakeOperation* op, JavaThread* target) { | |
| JavaThreadIteratorWithHandle jtiwh; | |
| log_error(handshake)("Handshake timeout: %s(" INTPTR_FORMAT "), pending threads: " INT32_FORMAT, | |
| op->name(), p2i(op), op->pending_threads()); | |
| if (target == nullptr) { | |
| for ( ; JavaThread* thr = jtiwh.next(); ) { | |
| if (thr->handshake_state()->operation_pending(op)) { | |
| log_error(handshake)("JavaThread " INTPTR_FORMAT " has not cleared handshake op: " INTPTR_FORMAT, p2i(thr), p2i(op)); | |
| // Remember the last one found for more diagnostics below. | |
| target = thr; | |
| } | |
| } | |
| } else { | |
| log_error(handshake)("JavaThread " INTPTR_FORMAT " has not cleared handshake op: " INTPTR_FORMAT, p2i(target), p2i(op)); | |
| } | |
| if (target != nullptr) { | |
| if (os::signal_thread(target, SIGILL, "cannot be handshaked")) { | |
| // Give target a chance to report the error and terminate the VM. | |
| os::naked_sleep(3000); | |
| } | |
| } else { | |
| log_error(handshake)("No thread with an unfinished handshake op(" INTPTR_FORMAT ") found.", p2i(op)); | |
| } | |
| fatal("Handshake timeout"); | |
| } | |
| static void check_handshake_timeout(jlong start_time, HandshakeOperation* op, JavaThread* target = nullptr) { | |
| // Check if handshake operation has timed out | |
| jlong timeout_ns = millis_to_nanos(HandshakeTimeout); | |
| if (timeout_ns > 0) { | |
| if (os::javaTimeNanos() >= (start_time + timeout_ns)) { | |
| handle_timeout(op, target); | |
| } | |
| } | |
| } | |
| static void log_handshake_info(jlong start_time_ns, const char* name, int targets, int emitted_handshakes_executed, const char* extra = nullptr) { | |
| if (log_is_enabled(Info, handshake)) { | |
| jlong completion_time = os::javaTimeNanos() - start_time_ns; | |
| log_info(handshake)("Handshake \"%s\", Targeted threads: %d, Executed by requesting thread: %d, Total completion time: " JLONG_FORMAT " ns%s%s", | |
| name, targets, | |
| emitted_handshakes_executed, | |
| completion_time, | |
| extra != nullptr ? ", " : "", | |
| extra != nullptr ? extra : ""); | |
| } | |
| } | |
| class VM_HandshakeAllThreads: public VM_Operation { | |
| HandshakeOperation* const _op; | |
| public: | |
| VM_HandshakeAllThreads(HandshakeOperation* op) : _op(op) {} | |
| const char* cause() const { return _op->name(); } | |
| bool evaluate_at_safepoint() const { return false; } | |
| void doit() { | |
| jlong start_time_ns = os::javaTimeNanos(); | |
| JavaThreadIteratorWithHandle jtiwh; | |
| int number_of_threads_issued = 0; | |
| for (JavaThread* thr = jtiwh.next(); thr != nullptr; thr = jtiwh.next()) { | |
| thr->handshake_state()->add_operation(_op); | |
| number_of_threads_issued++; | |
| } | |
| // Separate the arming of the poll in add_operation() above from | |
| // the read of JavaThread state in the try_process() call below. | |
| if (UseSystemMemoryBarrier) { | |
| SystemMemoryBarrier::emit(); | |
| } else { | |
| OrderAccess::fence(); | |
| } | |
| if (number_of_threads_issued < 1) { | |
| log_handshake_info(start_time_ns, _op->name(), 0, 0, "no threads alive"); | |
| return; | |
| } | |
| // _op was created with a count == 1 so don't double count. | |
| _op->add_target_count(number_of_threads_issued - 1); | |
| log_trace(handshake)("Threads signaled, begin processing blocked threads by VMThread"); | |
| HandshakeSpinYield hsy(start_time_ns); | |
| // Keeps count on how many of own emitted handshakes | |
| // this thread execute. | |
| int emitted_handshakes_executed = 0; | |
| do { | |
| // Check if handshake operation has timed out | |
| check_handshake_timeout(start_time_ns, _op); | |
| // Have VM thread perform the handshake operation for blocked threads. | |
| // Observing a blocked state may of course be transient but the processing is guarded | |
| // by mutexes and we optimistically begin by working on the blocked threads | |
| jtiwh.rewind(); | |
| for (JavaThread* thr = jtiwh.next(); thr != nullptr; thr = jtiwh.next()) { | |
| // A new thread on the ThreadsList will not have an operation, | |
| // hence it is skipped in handshake_try_process. | |
| HandshakeState::ProcessResult pr = thr->handshake_state()->try_process(_op); | |
| hsy.add_result(pr); | |
| if (pr == HandshakeState::_succeeded) { | |
| emitted_handshakes_executed++; | |
| } | |
| } | |
| hsy.process(); | |
| } while (!_op->is_completed()); | |
| // This pairs up with the release store in do_handshake(). It prevents future | |
| // loads from floating above the load of _pending_threads in is_completed() | |
| // and thus prevents reading stale data modified in the handshake closure | |
| // by the Handshakee. | |
| OrderAccess::acquire(); | |
| log_handshake_info(start_time_ns, _op->name(), number_of_threads_issued, emitted_handshakes_executed); | |
| } | |
| VMOp_Type type() const { return VMOp_HandshakeAllThreads; } | |
| }; | |
| void HandshakeOperation::prepare(JavaThread* current_target, Thread* executing_thread) { | |
| if (current_target->is_terminated()) { | |
| // Will never execute any handshakes on this thread. | |
| return; | |
| } | |
| if (current_target != executing_thread) { | |
| // Only when the target is not executing the handshake itself. | |
| StackWatermarkSet::start_processing(current_target, StackWatermarkKind::gc); | |
| } | |
| if (_requester != nullptr && _requester != executing_thread && _requester->is_Java_thread()) { | |
| // The handshake closure may contain oop Handles from the _requester. | |
| // We must make sure we can use them. | |
| StackWatermarkSet::start_processing(JavaThread::cast(_requester), StackWatermarkKind::gc); | |
| } | |
| } | |
| void HandshakeOperation::do_handshake(JavaThread* thread) { | |
| jlong start_time_ns = 0; | |
| if (log_is_enabled(Debug, handshake, task)) { | |
| start_time_ns = os::javaTimeNanos(); | |
| } | |
| // Only actually execute the operation for non terminated threads. | |
| if (!thread->is_terminated()) { | |
| _handshake_cl->do_thread(thread); | |
| } | |
| if (start_time_ns != 0) { | |
| jlong completion_time = os::javaTimeNanos() - start_time_ns; | |
| log_debug(handshake, task)("Operation: %s for thread " PTR_FORMAT ", is_vm_thread: %s, completed in " JLONG_FORMAT " ns", | |
| name(), p2i(thread), BOOL_TO_STR(Thread::current()->is_VM_thread()), completion_time); | |
| } | |
| // Inform VMThread/Handshaker that we have completed the operation. | |
| // When this is executed by the Handshakee we need a release store | |
| // here to make sure memory operations executed in the handshake | |
| // closure are visible to the VMThread/Handshaker after it reads | |
| // that the operation has completed. | |
| Atomic::dec(&_pending_threads); | |
| // Trailing fence, used to make sure removal of the operation strictly | |
| // happened after we completed the operation. | |
| // It is no longer safe to refer to 'this' as the VMThread/Handshaker may have destroyed this operation | |
| } | |
| void Handshake::execute(HandshakeClosure* hs_cl) { | |
| HandshakeOperation cto(hs_cl, nullptr, Thread::current()); | |
| VM_HandshakeAllThreads handshake(&cto); | |
| VMThread::execute(&handshake); | |
| } | |
| void Handshake::execute(HandshakeClosure* hs_cl, JavaThread* target) { | |
| // tlh == nullptr means we rely on a ThreadsListHandle somewhere | |
| // in the caller's context (and we sanity check for that). | |
| Handshake::execute(hs_cl, nullptr, target); | |
| } | |
| void Handshake::execute(HandshakeClosure* hs_cl, ThreadsListHandle* tlh, JavaThread* target) { | |
| JavaThread* self = JavaThread::current(); | |
| HandshakeOperation op(hs_cl, target, Thread::current()); | |
| jlong start_time_ns = os::javaTimeNanos(); | |
| guarantee(target != nullptr, "must be"); | |
| if (tlh == nullptr) { | |
| guarantee(Thread::is_JavaThread_protected_by_TLH(target), | |
| "missing ThreadsListHandle in calling context."); | |
| target->handshake_state()->add_operation(&op); | |
| } else if (tlh->includes(target)) { | |
| target->handshake_state()->add_operation(&op); | |
| } else { | |
| char buf[128]; | |
| jio_snprintf(buf, sizeof(buf), "(thread= " INTPTR_FORMAT " dead)", p2i(target)); | |
| log_handshake_info(start_time_ns, op.name(), 0, 0, buf); | |
| return; | |
| } | |
| // Separate the arming of the poll in add_operation() above from | |
| // the read of JavaThread state in the try_process() call below. | |
| if (UseSystemMemoryBarrier) { | |
| SystemMemoryBarrier::emit(); | |
| } else { | |
| OrderAccess::fence(); | |
| } | |
| // Keeps count on how many of own emitted handshakes | |
| // this thread execute. | |
| int emitted_handshakes_executed = 0; | |
| HandshakeSpinYield hsy(start_time_ns); | |
| while (!op.is_completed()) { | |
| HandshakeState::ProcessResult pr = target->handshake_state()->try_process(&op); | |
| if (pr == HandshakeState::_succeeded) { | |
| emitted_handshakes_executed++; | |
| } | |
| if (op.is_completed()) { | |
| break; | |
| } | |
| // Check if handshake operation has timed out | |
| check_handshake_timeout(start_time_ns, &op, target); | |
| hsy.add_result(pr); | |
| // Check for pending handshakes to avoid possible deadlocks where our | |
| // target is trying to handshake us. | |
| if (SafepointMechanism::should_process(self)) { | |
| // Will not suspend here. | |
| ThreadBlockInVM tbivm(self); | |
| } | |
| hsy.process(); | |
| } | |
| // This pairs up with the release store in do_handshake(). It prevents future | |
| // loads from floating above the load of _pending_threads in is_completed() | |
| // and thus prevents reading stale data modified in the handshake closure | |
| // by the Handshakee. | |
| OrderAccess::acquire(); | |
| log_handshake_info(start_time_ns, op.name(), 1, emitted_handshakes_executed); | |
| } | |
| void Handshake::execute(AsyncHandshakeClosure* hs_cl, JavaThread* target) { | |
| jlong start_time_ns = os::javaTimeNanos(); | |
| AsyncHandshakeOperation* op = new AsyncHandshakeOperation(hs_cl, target, start_time_ns); | |
| guarantee(target != nullptr, "must be"); | |
| Thread* current = Thread::current(); | |
| if (current != target) { | |
| // Another thread is handling the request and it must be protecting | |
| // the target. | |
| guarantee(Thread::is_JavaThread_protected_by_TLH(target), | |
| "missing ThreadsListHandle in calling context."); | |
| } | |
| // Implied else: | |
| // The target is handling the request itself so it can't be dead. | |
| target->handshake_state()->add_operation(op); | |
| } | |
| // Filters | |
| static bool non_self_executable_filter(HandshakeOperation* op) { | |
| return !op->is_async(); | |
| } | |
| static bool no_async_exception_filter(HandshakeOperation* op) { | |
| return !op->is_async_exception(); | |
| } | |
| static bool async_exception_filter(HandshakeOperation* op) { | |
| return op->is_async_exception(); | |
| } | |
| static bool no_suspend_no_async_exception_filter(HandshakeOperation* op) { | |
| return !op->is_suspend() && !op->is_async_exception(); | |
| } | |
| static bool all_ops_filter(HandshakeOperation* op) { | |
| return true; | |
| } | |
| HandshakeState::HandshakeState(JavaThread* target) : | |
| _handshakee(target), | |
| _queue(), | |
| _lock(Monitor::nosafepoint, "HandshakeState_lock"), | |
| _active_handshaker(), | |
| _async_exceptions_blocked(false), | |
| _suspended(false), | |
| _async_suspend_handshake(false) { | |
| } | |
| HandshakeState::~HandshakeState() { | |
| while (has_operation()) { | |
| HandshakeOperation* op = _queue.pop(all_ops_filter); | |
| guarantee(op->is_async(), "Only async operations may still be present on queue"); | |
| delete op; | |
| } | |
| } | |
| void HandshakeState::add_operation(HandshakeOperation* op) { | |
| // Adds are done lock free and so is arming. | |
| _queue.push(op); | |
| SafepointMechanism::arm_local_poll_release(_handshakee); | |
| } | |
| bool HandshakeState::operation_pending(HandshakeOperation* op) { | |
| MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag); | |
| MatchOp mo(op); | |
| return _queue.contains(mo); | |
| } | |
| HandshakeOperation* HandshakeState::get_op_for_self(bool allow_suspend, bool check_async_exception) { | |
| assert(_handshakee == Thread::current(), "Must be called by self"); | |
| assert(_lock.owned_by_self(), "Lock must be held"); | |
| assert(allow_suspend || !check_async_exception, "invalid case"); | |
| #if INCLUDE_JVMTI | |
| if (allow_suspend && _handshakee->is_disable_suspend()) { | |
| // filter out suspend operations while JavaThread is in disable_suspend mode | |
| allow_suspend = false; | |
| } | |
| #endif | |
| if (!allow_suspend) { | |
| return _queue.peek(no_suspend_no_async_exception_filter); | |
| } else if (check_async_exception && !_async_exceptions_blocked) { | |
| return _queue.peek(); | |
| } else { | |
| return _queue.peek(no_async_exception_filter); | |
| } | |
| } | |
| bool HandshakeState::has_operation(bool allow_suspend, bool check_async_exception) { | |
| // We must not block here as that could lead to deadlocks if we already hold an | |
| // "external" mutex. If the try_lock fails then we assume that there is an operation | |
| // and force the caller to check more carefully in a safer context. If we can't get | |
| // the lock it means another thread is trying to handshake with us, so it can't | |
| // happen during thread termination and destruction. | |
| bool ret = true; | |
| if (_lock.try_lock()) { | |
| ret = get_op_for_self(allow_suspend, check_async_exception) != nullptr; | |
| _lock.unlock(); | |
| } | |
| return ret; | |
| } | |
| bool HandshakeState::has_async_exception_operation() { | |
| if (!has_operation()) return false; | |
| ConditionalMutexLocker ml(&_lock, !_lock.owned_by_self(), Mutex::_no_safepoint_check_flag); | |
| return _queue.peek(async_exception_filter) != nullptr; | |
| } | |
| void HandshakeState::clean_async_exception_operation() { | |
| while (has_async_exception_operation()) { | |
| MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag); | |
| HandshakeOperation* op; | |
| op = _queue.peek(async_exception_filter); | |
| remove_op(op); | |
| delete op; | |
| } | |
| } | |
| bool HandshakeState::have_non_self_executable_operation() { | |
| assert(_handshakee != Thread::current(), "Must not be called by self"); | |
| assert(_lock.owned_by_self(), "Lock must be held"); | |
| return _queue.contains(non_self_executable_filter); | |
| } | |
| HandshakeOperation* HandshakeState::get_op() { | |
| assert(_handshakee != Thread::current(), "Must not be called by self"); | |
| assert(_lock.owned_by_self(), "Lock must be held"); | |
| return _queue.peek(non_self_executable_filter); | |
| }; | |
| void HandshakeState::remove_op(HandshakeOperation* op) { | |
| assert(_lock.owned_by_self(), "Lock must be held"); | |
| MatchOp mo(op); | |
| HandshakeOperation* ret = _queue.pop(mo); | |
| assert(ret == op, "Popped op must match requested op"); | |
| }; | |
| bool HandshakeState::process_by_self(bool allow_suspend, bool check_async_exception) { | |
| assert(Thread::current() == _handshakee, "should call from _handshakee"); | |
| assert(!_handshakee->is_terminated(), "should not be a terminated thread"); | |
| _handshakee->frame_anchor()->make_walkable(); | |
| // Threads shouldn't block if they are in the middle of printing, but... | |
| ttyLocker::break_tty_lock_for_safepoint(os::current_thread_id()); | |
| // Separate all the writes above for other threads reading state | |
| // set by this thread in case the operation is ThreadSuspendHandshake. | |
| OrderAccess::fence(); | |
| while (has_operation()) { | |
| // Handshakes cannot safely safepoint. The exceptions to this rule are | |
| // the asynchronous suspension and unsafe access error handshakes. | |
| MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag); | |
| HandshakeOperation* op = get_op_for_self(allow_suspend, check_async_exception); | |
| if (op != nullptr) { | |
| assert(op->_target == nullptr || op->_target == Thread::current(), "Wrong thread"); | |
| bool async = op->is_async(); | |
| log_trace(handshake)("Proc handshake %s " INTPTR_FORMAT " on " INTPTR_FORMAT " by self", | |
| async ? "asynchronous" : "synchronous", p2i(op), p2i(_handshakee)); | |
| op->prepare(_handshakee, _handshakee); | |
| if (!async) { | |
| HandleMark hm(_handshakee); | |
| PreserveExceptionMark pem(_handshakee); | |
| op->do_handshake(_handshakee); // acquire, op removed after | |
| remove_op(op); | |
| } else { | |
| // An asynchronous handshake may put the JavaThread in blocked state (safepoint safe). | |
| // The destructor ~PreserveExceptionMark touches the exception oop so it must not be executed, | |
| // since a safepoint may be in-progress when returning from the async handshake. | |
| remove_op(op); | |
| op->do_handshake(_handshakee); | |
| log_handshake_info(((AsyncHandshakeOperation*)op)->start_time(), op->name(), 1, 0, "asynchronous"); | |
| delete op; | |
| return true; // Must check for safepoints | |
| } | |
| } else { | |
| return false; | |
| } | |
| } | |
| return false; | |
| } | |
| bool HandshakeState::can_process_handshake() { | |
| // handshake_safe may only be called with polls armed. | |
| // Handshaker controls this by first claiming the handshake via claim_handshake(). | |
| return SafepointSynchronize::handshake_safe(_handshakee); | |
| } | |
| bool HandshakeState::possibly_can_process_handshake() { | |
| // Note that this method is allowed to produce false positives. | |
| if (_handshakee->is_terminated()) { | |
| return true; | |
| } | |
| switch (_handshakee->thread_state()) { | |
| case _thread_in_native: | |
| // native threads are safe if they have no java stack or have walkable stack | |
| return !_handshakee->has_last_Java_frame() || _handshakee->frame_anchor()->walkable(); | |
| case _thread_blocked: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool HandshakeState::claim_handshake() { | |
| if (!_lock.try_lock()) { | |
| return false; | |
| } | |
| // Operations are added lock free and then the poll is armed. | |
| // If all handshake operations for the handshakee are finished and someone | |
| // just adds an operation we may see it here. But if the handshakee is not | |
| // armed yet it is not safe to proceed. | |
| if (have_non_self_executable_operation()) { | |
| OrderAccess::loadload(); // Matches the implicit storestore in add_operation() | |
| if (SafepointMechanism::local_poll_armed(_handshakee)) { | |
| return true; | |
| } | |
| } | |
| _lock.unlock(); | |
| return false; | |
| } | |
| HandshakeState::ProcessResult HandshakeState::try_process(HandshakeOperation* match_op) { | |
| if (!has_operation()) { | |
| // JT has already cleared its handshake | |
| return HandshakeState::_no_operation; | |
| } | |
| if (!possibly_can_process_handshake()) { | |
| // JT is observed in an unsafe state, it must notice the handshake itself | |
| return HandshakeState::_not_safe; | |
| } | |
| // Claim the mutex if there still an operation to be executed. | |
| if (!claim_handshake()) { | |
| return HandshakeState::_claim_failed; | |
| } | |
| // If we own the mutex at this point and while owning the mutex we | |
| // can observe a safe state the thread cannot possibly continue without | |
| // getting caught by the mutex. | |
| if (!can_process_handshake()) { | |
| _lock.unlock(); | |
| return HandshakeState::_not_safe; | |
| } | |
| Thread* current_thread = Thread::current(); | |
| HandshakeOperation* op = get_op(); | |
| assert(op != nullptr, "Must have an op"); | |
| assert(SafepointMechanism::local_poll_armed(_handshakee), "Must be"); | |
| assert(op->_target == nullptr || _handshakee == op->_target, "Wrong thread"); | |
| log_trace(handshake)("Processing handshake " INTPTR_FORMAT " by %s(%s)", p2i(op), | |
| op == match_op ? "handshaker" : "cooperative", | |
| current_thread->is_VM_thread() ? "VM Thread" : "JavaThread"); | |
| op->prepare(_handshakee, current_thread); | |
| set_active_handshaker(current_thread); | |
| op->do_handshake(_handshakee); // acquire, op removed after | |
| set_active_handshaker(nullptr); | |
| remove_op(op); | |
| _lock.unlock(); | |
| log_trace(handshake)("%s(" INTPTR_FORMAT ") executed an op for JavaThread: " INTPTR_FORMAT " %s target op: " INTPTR_FORMAT, | |
| current_thread->is_VM_thread() ? "VM Thread" : "JavaThread", | |
| p2i(current_thread), p2i(_handshakee), | |
| op == match_op ? "including" : "excluding", p2i(match_op)); | |
| 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()) { | |
| // 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 | |
| // back to Java until resumed we cannot create the exception | |
| // object yet. Add a new unsafe access error operation to | |
| // the end of the queue and try again in the next attempt. | |
| Handshake::execute(new UnsafeAccessErrorHandshake(), _handshakee); | |
| log_info(handshake)("JavaThread " INTPTR_FORMAT " skipping unsafe access processing due to suspend.", p2i(_handshakee)); | |
| return; | |
| } | |
| // Release the handshake lock before constructing the oop to | |
| // avoid deadlocks since that can block. This will allow the | |
| // JavaThread to execute normally as if it was outside a handshake. | |
| // We will reacquire the handshake lock at return from ~MutexUnlocker. | |
| MutexUnlocker ml(&_lock, Mutex::_no_safepoint_check_flag); | |
| // We may be at method entry which requires we save the do-not-unlock flag. | |
| UnlockFlagSaver fs(_handshakee); | |
| Handle h_exception = Exceptions::new_exception(_handshakee, vmSymbols::java_lang_InternalError(), "a fault occurred in an unsafe memory access operation"); | |
| if (h_exception()->is_a(vmClasses::InternalError_klass())) { | |
| java_lang_InternalError::set_during_unsafe_access(h_exception()); | |
| } | |
| _handshakee->handle_async_exception(h_exception()); | |
| } |