| /* | |
| * Copyright (c) 2003, 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 "memory/allocation.inline.hpp" | |
| #include "prims/jvmtiRawMonitor.hpp" | |
| #include "runtime/atomic.hpp" | |
| #include "runtime/interfaceSupport.inline.hpp" | |
| #include "runtime/javaThread.hpp" | |
| #include "runtime/orderAccess.hpp" | |
| #include "runtime/threads.hpp" | |
| JvmtiRawMonitor::QNode::QNode(Thread* thread) : _next(nullptr), _prev(nullptr), | |
| _event(thread->_ParkEvent), | |
| _notified(0), _t_state(TS_RUN) { | |
| } | |
| GrowableArray<JvmtiRawMonitor*>* JvmtiPendingMonitors::_monitors = | |
| new (mtServiceability) GrowableArray<JvmtiRawMonitor*>(1, mtServiceability); | |
| void JvmtiPendingMonitors::transition_raw_monitors() { | |
| assert((Threads::number_of_threads()==1), | |
| "Java thread has not been created yet or more than one java thread " | |
| "is running. Raw monitor transition will not work"); | |
| JavaThread* current_java_thread = JavaThread::current(); | |
| { | |
| ThreadToNativeFromVM ttnfvm(current_java_thread); | |
| for (int i = 0; i < count(); i++) { | |
| JvmtiRawMonitor* rmonitor = monitors()->at(i); | |
| rmonitor->raw_enter(current_java_thread); | |
| } | |
| } | |
| // pending monitors are converted to real monitor so delete them all. | |
| dispose(); | |
| } | |
| // | |
| // class JvmtiRawMonitor | |
| // | |
| JvmtiRawMonitor::JvmtiRawMonitor(const char* name) : _owner(nullptr), | |
| _recursions(0), | |
| _entry_list(nullptr), | |
| _wait_set(nullptr), | |
| _magic(JVMTI_RM_MAGIC), | |
| _name(nullptr) { | |
| #ifdef ASSERT | |
| _name = strcpy(NEW_C_HEAP_ARRAY(char, strlen(name) + 1, mtInternal), name); | |
| #endif | |
| } | |
| JvmtiRawMonitor::~JvmtiRawMonitor() { | |
| #ifdef ASSERT | |
| FreeHeap(_name); | |
| #endif | |
| _magic = 0; | |
| } | |
| bool | |
| JvmtiRawMonitor::is_valid() { | |
| jlong value = 0; | |
| // This object might not be a JvmtiRawMonitor so we can't assume | |
| // the _magic field is properly aligned. Get the value in a safe | |
| // way and then check against JVMTI_RM_MAGIC. | |
| switch (sizeof(_magic)) { | |
| case 2: | |
| value = Bytes::get_native_u2((address)&_magic); | |
| break; | |
| case 4: | |
| value = Bytes::get_native_u4((address)&_magic); | |
| break; | |
| case 8: | |
| value = Bytes::get_native_u8((address)&_magic); | |
| break; | |
| default: | |
| guarantee(false, "_magic field is an unexpected size"); | |
| } | |
| return value == JVMTI_RM_MAGIC; | |
| } | |
| // ------------------------------------------------------------------------- | |
| // The JVMTI raw monitor subsystem is entirely distinct from normal | |
| // java-synchronization or jni-synchronization. JVMTI raw monitors are not | |
| // associated with objects. They can be implemented in any manner | |
| // that makes sense. The original implementors decided to piggy-back | |
| // the raw-monitor implementation on the existing Java ObjectMonitor mechanism. | |
| // Now we just use a simplified form of that ObjectMonitor code. | |
| // | |
| // Note that we use the single RawMonitor_lock to protect queue operations for | |
| // _all_ raw monitors. This is a scalability impediment, but since raw monitor usage | |
| // is fairly rare, this is not of concern. The RawMonitor_lock can not | |
| // be held indefinitely. The critical sections must be short and bounded. | |
| // | |
| // ------------------------------------------------------------------------- | |
| void JvmtiRawMonitor::simple_enter(Thread* self) { | |
| for (;;) { | |
| if (Atomic::replace_if_null(&_owner, self)) { | |
| if (self->is_Java_thread()) { | |
| Continuation::pin(JavaThread::cast(self)); | |
| } | |
| return; | |
| } | |
| QNode node(self); | |
| self->_ParkEvent->reset(); // strictly optional | |
| node._t_state = QNode::TS_ENTER; | |
| RawMonitor_lock->lock_without_safepoint_check(); | |
| node._next = _entry_list; | |
| _entry_list = &node; | |
| OrderAccess::fence(); | |
| if (_owner == nullptr && Atomic::replace_if_null(&_owner, self)) { | |
| _entry_list = node._next; | |
| RawMonitor_lock->unlock(); | |
| if (self->is_Java_thread()) { | |
| Continuation::pin(JavaThread::cast(self)); | |
| } | |
| return; | |
| } | |
| RawMonitor_lock->unlock(); | |
| while (node._t_state == QNode::TS_ENTER) { | |
| self->_ParkEvent->park(); | |
| } | |
| } | |
| } | |
| void JvmtiRawMonitor::simple_exit(Thread* self) { | |
| guarantee(_owner == self, "invariant"); | |
| Atomic::release_store(&_owner, (Thread*)nullptr); | |
| OrderAccess::fence(); | |
| if (self->is_Java_thread()) { | |
| Continuation::unpin(JavaThread::cast(self)); | |
| } | |
| if (_entry_list == nullptr) { | |
| return; | |
| } | |
| RawMonitor_lock->lock_without_safepoint_check(); | |
| QNode* w = _entry_list; | |
| if (w != nullptr) { | |
| _entry_list = w->_next; | |
| } | |
| RawMonitor_lock->unlock(); | |
| if (w != nullptr) { | |
| guarantee(w ->_t_state == QNode::TS_ENTER, "invariant"); | |
| // Once we set _t_state to TS_RUN the waiting thread can complete | |
| // simple_enter and 'w' is pointing into random stack space. So we have | |
| // to ensure we extract the ParkEvent (which is in type-stable memory) | |
| // before we set the state, and then don't access 'w'. | |
| ParkEvent* ev = w->_event; | |
| OrderAccess::loadstore(); | |
| w->_t_state = QNode::TS_RUN; | |
| OrderAccess::fence(); | |
| ev->unpark(); | |
| } | |
| return; | |
| } | |
| inline void JvmtiRawMonitor::enqueue_waiter(QNode& node) { | |
| node._notified = 0; | |
| node._t_state = QNode::TS_WAIT; | |
| RawMonitor_lock->lock_without_safepoint_check(); | |
| node._next = _wait_set; | |
| _wait_set = &node; | |
| RawMonitor_lock->unlock(); | |
| } | |
| inline void JvmtiRawMonitor::dequeue_waiter(QNode& node) { | |
| // If thread still resides on the waitset then unlink it. | |
| // Double-checked locking -- the usage is safe in this context | |
| // as _t_state is volatile and the lock-unlock operators are | |
| // serializing (barrier-equivalent). | |
| if (node._t_state == QNode::TS_WAIT) { | |
| RawMonitor_lock->lock_without_safepoint_check(); | |
| if (node._t_state == QNode::TS_WAIT) { | |
| // Simple O(n) unlink, but performance isn't critical here. | |
| QNode* p; | |
| QNode* q = nullptr; | |
| for (p = _wait_set; p != &node; p = p->_next) { | |
| q = p; | |
| } | |
| guarantee(p == &node, "invariant"); | |
| if (q == nullptr) { | |
| guarantee (p == _wait_set, "invariant"); | |
| _wait_set = p->_next; | |
| } else { | |
| guarantee(p == q->_next, "invariant"); | |
| q->_next = p->_next; | |
| } | |
| node._t_state = QNode::TS_RUN; | |
| } | |
| RawMonitor_lock->unlock(); | |
| } | |
| guarantee(node._t_state == QNode::TS_RUN, "invariant"); | |
| } | |
| // simple_wait is not quite so simple as we have to deal with the interaction | |
| // with the Thread interrupt state, which resides in the java.lang.Thread object. | |
| // That state must only be accessed while _thread_in_vm and requires proper thread-state | |
| // transitions. | |
| // Returns M_OK usually, but M_INTERRUPTED if the thread is a JavaThread and was | |
| // interrupted. | |
| // Note: | |
| // - simple_wait never reenters the monitor. | |
| // - A JavaThread must be in native. | |
| int JvmtiRawMonitor::simple_wait(Thread* self, jlong millis) { | |
| guarantee(_owner == self , "invariant"); | |
| guarantee(_recursions == 0, "invariant"); | |
| QNode node(self); | |
| enqueue_waiter(node); | |
| simple_exit(self); | |
| guarantee(_owner != self, "invariant"); | |
| int ret = M_OK; | |
| if (self->is_Java_thread()) { | |
| JavaThread* jt = JavaThread::cast(self); | |
| guarantee(jt->thread_state() == _thread_in_native, "invariant"); | |
| { | |
| // This transition must be after we exited the monitor. | |
| ThreadInVMfromNative tivmfn(jt); | |
| if (jt->get_and_clear_interrupted()) { | |
| ret = M_INTERRUPTED; | |
| } else { | |
| ThreadBlockInVM tbivm(jt); | |
| if (millis <= 0) { | |
| self->_ParkEvent->park(); | |
| } else { | |
| self->_ParkEvent->park(millis); | |
| } | |
| // Return to VM before post-check of interrupt state | |
| } | |
| if (jt->get_and_clear_interrupted()) { | |
| ret = M_INTERRUPTED; | |
| } | |
| } | |
| } else { | |
| if (millis <= 0) { | |
| self->_ParkEvent->park(); | |
| } else { | |
| self->_ParkEvent->park(millis); | |
| } | |
| } | |
| dequeue_waiter(node); | |
| return ret; | |
| } | |
| void JvmtiRawMonitor::simple_notify(Thread* self, bool all) { | |
| guarantee(_owner == self, "invariant"); | |
| if (_wait_set == nullptr) { | |
| return; | |
| } | |
| // We have two options: | |
| // A. Transfer the threads from the _wait_set to the _entry_list | |
| // B. Remove the thread from the _wait_set and unpark() it. | |
| // | |
| // We use (B), which is crude and results in lots of futile | |
| // context switching. In particular (B) induces lots of contention. | |
| ParkEvent* ev = nullptr; // consider using a small auto array ... | |
| RawMonitor_lock->lock_without_safepoint_check(); | |
| for (;;) { | |
| QNode* w = _wait_set; | |
| if (w == nullptr) break; | |
| _wait_set = w->_next; | |
| if (ev != nullptr) { | |
| ev->unpark(); | |
| ev = nullptr; | |
| } | |
| ev = w->_event; | |
| OrderAccess::loadstore(); | |
| w->_t_state = QNode::TS_RUN; | |
| OrderAccess::storeload(); | |
| if (!all) { | |
| break; | |
| } | |
| } | |
| RawMonitor_lock->unlock(); | |
| if (ev != nullptr) { | |
| ev->unpark(); | |
| } | |
| return; | |
| } | |
| void JvmtiRawMonitor::ExitOnSuspend::operator()(JavaThread* current) { | |
| // We must exit the monitor in case of a safepoint. | |
| _rm->simple_exit(current); | |
| _rm_exited = true; | |
| } | |
| // JavaThreads will enter here with state _thread_in_native. | |
| void JvmtiRawMonitor::raw_enter(Thread* self) { | |
| // TODO Atomic::load on _owner field | |
| if (_owner == self) { | |
| _recursions++; | |
| return; | |
| } | |
| self->set_current_pending_raw_monitor(this); | |
| if (!self->is_Java_thread()) { | |
| simple_enter(self); | |
| } else { | |
| JavaThread* jt = JavaThread::cast(self); | |
| guarantee(jt->thread_state() == _thread_in_native, "invariant"); | |
| ThreadInVMfromNative tivmfn(jt); | |
| for (;;) { | |
| ExitOnSuspend eos(this); | |
| { | |
| ThreadBlockInVMPreprocess<ExitOnSuspend> tbivmp(jt, eos, true /* allow_suspend */); | |
| simple_enter(jt); | |
| } | |
| if (!eos.monitor_exited()) { | |
| break; | |
| } | |
| } | |
| } | |
| self->set_current_pending_raw_monitor(nullptr); | |
| guarantee(_owner == self, "invariant"); | |
| guarantee(_recursions == 0, "invariant"); | |
| } | |
| int JvmtiRawMonitor::raw_exit(Thread* self) { | |
| if (self != _owner) { | |
| return M_ILLEGAL_MONITOR_STATE; | |
| } | |
| if (_recursions > 0) { | |
| _recursions--; | |
| } else { | |
| simple_exit(self); | |
| } | |
| return M_OK; | |
| } | |
| int JvmtiRawMonitor::raw_wait(jlong millis, Thread* self) { | |
| if (self != _owner) { | |
| return M_ILLEGAL_MONITOR_STATE; | |
| } | |
| int ret = M_OK; | |
| // To avoid spurious wakeups we reset the parkevent. This is strictly optional. | |
| // The caller must be able to tolerate spurious returns from raw_wait(). | |
| self->_ParkEvent->reset(); | |
| OrderAccess::fence(); | |
| int save = _recursions; | |
| _recursions = 0; | |
| ret = simple_wait(self, millis); | |
| // Now we need to re-enter the monitor. For JavaThreads | |
| // we need to manage suspend requests. | |
| if (self->is_Java_thread()) { // JavaThread re-enter | |
| JavaThread* jt = JavaThread::cast(self); | |
| ThreadInVMfromNative tivmfn(jt); | |
| for (;;) { | |
| ExitOnSuspend eos(this); | |
| { | |
| ThreadBlockInVMPreprocess<ExitOnSuspend> tbivmp(jt, eos, true /* allow_suspend */); | |
| simple_enter(jt); | |
| } | |
| if (!eos.monitor_exited()) { | |
| break; | |
| } | |
| } | |
| if (jt->get_and_clear_interrupted()) { | |
| ret = M_INTERRUPTED; | |
| } | |
| } else { // Non-JavaThread re-enter | |
| assert(ret != M_INTERRUPTED, "Only JavaThreads can be interrupted"); | |
| simple_enter(self); | |
| } | |
| _recursions = save; | |
| guarantee(self == _owner, "invariant"); | |
| return ret; | |
| } | |
| int JvmtiRawMonitor::raw_notify(Thread* self) { | |
| if (self != _owner) { | |
| return M_ILLEGAL_MONITOR_STATE; | |
| } | |
| simple_notify(self, false); | |
| return M_OK; | |
| } | |
| int JvmtiRawMonitor::raw_notifyAll(Thread* self) { | |
| if (self != _owner) { | |
| return M_ILLEGAL_MONITOR_STATE; | |
| } | |
| simple_notify(self, true); | |
| return M_OK; | |
| } |