| /* |
| * Copyright (c) 2003, 2023, 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 "precompiled.hpp" |
| #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() { |
| int 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->is_interrupted(true)) { |
| 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->is_interrupted(true)) { |
| 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(); |
| |
| intptr_t 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->is_interrupted(true)) { |
| 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; |
| } |