/* * Copyright (c) 2022, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * Copyright (c) 2024, 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 * 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_LOCKSTACK_INLINE_HPP #define SHARE_RUNTIME_LOCKSTACK_INLINE_HPP #include "runtime/lockStack.hpp" #include "memory/iterator.hpp" #include "runtime/javaThread.hpp" #include "runtime/objectMonitor.inline.hpp" #include "runtime/safepoint.hpp" #include "runtime/stackWatermark.hpp" #include "runtime/stackWatermarkSet.inline.hpp" #include "runtime/synchronizer.hpp" #include "utilities/align.hpp" #include "utilities/globalDefinitions.hpp" inline int LockStack::to_index(uint32_t offset) { assert(is_aligned(offset, oopSize), "Bad alignment: %u", offset); assert((offset <= end_offset()), "lockstack overflow: offset %d end_offset %d", offset, end_offset()); assert((offset >= start_offset()), "lockstack underflow: offset %d start_offset %d", offset, start_offset()); return (offset - lock_stack_base_offset) / oopSize; } JavaThread* LockStack::get_thread() const { char* addr = reinterpret_cast(const_cast(this)); return reinterpret_cast(addr - lock_stack_offset); } inline bool LockStack::is_full() const { return to_index(_top) == CAPACITY; } inline bool LockStack::is_owning_thread() const { Thread* current = Thread::current(); if (current->is_Java_thread()) { JavaThread* thread = JavaThread::cast(current); bool is_owning = &thread->lock_stack() == this; assert(is_owning == (get_thread() == thread), "is_owning sanity"); return is_owning; } return false; } inline void LockStack::push(oop o) { verify("pre-push"); assert(oopDesc::is_oop(o), "must be"); assert(!contains(o), "entries must be unique"); assert(!is_full(), "must have room"); assert(_base[to_index(_top)] == nullptr, "expect zapped entry"); _base[to_index(_top)] = o; _top += oopSize; verify("post-push"); } inline oop LockStack::bottom() const { assert(to_index(_top) > 0, "must contain an oop"); return _base[0]; } inline bool LockStack::is_empty() const { return to_index(_top) == 0; } inline bool LockStack::is_recursive(oop o) const { if (!VM_Version::supports_recursive_fast_locking()) { return false; } verify("pre-is_recursive"); // This will succeed iff there is a consecutive run of oops on the // lock-stack with a length of at least 2. assert(contains(o), "at least one entry must exist"); int end = to_index(_top); // Start iterating from the top because the runtime code is more // interested in the balanced locking case when the top oop on the // lock-stack matches o. This will cause the for loop to break out // in the first loop iteration if it is non-recursive. for (int i = end - 1; i > 0; i--) { if (_base[i - 1] == o && _base[i] == o) { verify("post-is_recursive"); return true; } if (_base[i] == o) { // o can only occur in one consecutive run on the lock-stack. // Only one of the two oops checked matched o, so this run // must be of length 1 and thus not be recursive. Stop the search. break; } } verify("post-is_recursive"); return false; } inline bool LockStack::try_recursive_enter(oop o) { if (!VM_Version::supports_recursive_fast_locking()) { return false; } verify("pre-try_recursive_enter"); // This will succeed iff the top oop on the stack matches o. // When successful o will be pushed to the lock-stack creating // a consecutive run at least 2 oops that matches o on top of // the lock-stack. assert(!is_full(), "precond"); int end = to_index(_top); if (end == 0 || _base[end - 1] != o) { // Topmost oop does not match o. verify("post-try_recursive_enter"); return false; } _base[end] = o; _top += oopSize; verify("post-try_recursive_enter"); return true; } inline bool LockStack::try_recursive_exit(oop o) { if (!VM_Version::supports_recursive_fast_locking()) { return false; } verify("pre-try_recursive_exit"); // This will succeed iff the top two oops on the stack matches o. // When successful the top oop will be popped of the lock-stack. // When unsuccessful the lock may still be recursive, in which // case the locking is unbalanced. This case is handled externally. assert(contains(o), "entries must exist"); int end = to_index(_top); if (end <= 1 || _base[end - 1] != o || _base[end - 2] != o) { // The two topmost oops do not match o. verify("post-try_recursive_exit"); return false; } _top -= oopSize; DEBUG_ONLY(_base[to_index(_top)] = nullptr;) verify("post-try_recursive_exit"); return true; } inline size_t LockStack::remove(oop o) { verify("pre-remove"); assert(contains(o), "entry must be present: " PTR_FORMAT, p2i(o)); int end = to_index(_top); int inserted = 0; for (int i = 0; i < end; i++) { if (_base[i] != o) { if (inserted != i) { _base[inserted] = _base[i]; } inserted++; } } #ifdef ASSERT for (int i = inserted; i < end; i++) { _base[i] = nullptr; } #endif uint32_t removed = end - inserted; _top -= removed * oopSize; assert(!contains(o), "entry must have been removed: " PTR_FORMAT, p2i(o)); verify("post-remove"); return removed; } inline bool LockStack::contains(oop o) const { verify("pre-contains"); // Can't poke around in thread oops without having started stack watermark processing. assert(StackWatermarkSet::processing_started(get_thread()), "Processing must have started!"); int end = to_index(_top); for (int i = end - 1; i >= 0; i--) { if (_base[i] == o) { verify("post-contains"); return true; } } verify("post-contains"); return false; } inline int LockStack::monitor_count() const { int end = to_index(_top); assert(end <= CAPACITY, "invariant"); return end; } inline void LockStack::move_to_address(oop* start) { int end = to_index(_top); for (int i = 0; i < end; i++) { start[i] = _base[i]; DEBUG_ONLY(_base[i] = nullptr;) } _top = lock_stack_base_offset; } inline void LockStack::move_from_address(oop* start, int count) { assert(to_index(_top) == 0, "lockstack should be empty"); for (int i = 0; i < count; i++) { _base[i] = start[i]; _top += oopSize; } } inline void LockStack::oops_do(OopClosure* cl) { // We don't perform pre oops_do verify here because this function // is used by the GC to fix the oops. int end = to_index(_top); for (int i = 0; i < end; i++) { cl->do_oop(&_base[i]); } verify("post-oops-do"); } inline void OMCache::set_monitor(ObjectMonitor *monitor) { oop obj = monitor->object_peek(); assert(obj != nullptr, "must be alive"); assert(monitor == ObjectSynchronizer::get_monitor_from_table(obj), "must exist in table"); _monitor = monitor; _obj = obj; } inline ObjectMonitor* OMCache::get_monitor(oop o) { if (_obj == o) { assert(_monitor != nullptr, "monitor must exist"); if (!_monitor->is_being_async_deflated()) { return _monitor; } // Bad monitor, so clear the cache. _obj = nullptr; _monitor = nullptr; } return nullptr; } inline void OMCache::clear() { _obj = nullptr; _monitor = nullptr; } #endif // SHARE_RUNTIME_LOCKSTACK_INLINE_HPP