| /* |
| * Copyright (c) 2022, Red Hat, Inc. All rights reserved. |
| * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. |
| * Copyright (c) 2024, 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/lightweightSynchronizer.hpp" |
| #include "runtime/objectMonitor.inline.hpp" |
| #include "runtime/safepoint.hpp" |
| #include "runtime/stackWatermark.hpp" |
| #include "runtime/stackWatermarkSet.inline.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<char*>(const_cast<LockStack*>(this)); |
| return reinterpret_cast<JavaThread*>(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_lightweight_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_lightweight_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_lightweight_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) { |
| const int end = OMCache::CAPACITY - 1; |
| |
| oop obj = monitor->object_peek(); |
| assert(obj != nullptr, "must be alive"); |
| assert(monitor == LightweightSynchronizer::get_monitor_from_table(JavaThread::current(), obj), "must exist in table"); |
| |
| OMCacheEntry to_insert = {obj, monitor}; |
| |
| for (int i = 0; i < end; ++i) { |
| if (_entries[i]._oop == obj || |
| _entries[i]._monitor == nullptr || |
| _entries[i]._monitor->is_being_async_deflated()) { |
| // Use stale slot. |
| _entries[i] = to_insert; |
| return; |
| } |
| // Swap with the most recent value. |
| ::swap(to_insert, _entries[i]); |
| } |
| _entries[end] = to_insert; |
| } |
| |
| inline ObjectMonitor* OMCache::get_monitor(oop o) { |
| for (int i = 0; i < CAPACITY; ++i) { |
| if (_entries[i]._oop == o) { |
| assert(_entries[i]._monitor != nullptr, "monitor must exist"); |
| if (_entries[i]._monitor->is_being_async_deflated()) { |
| // Bad monitor |
| // Shift down rest |
| for (; i < CAPACITY - 1; ++i) { |
| _entries[i] = _entries[i + 1]; |
| } |
| // Clear end |
| _entries[i] = {}; |
| return nullptr; |
| } |
| return _entries[i]._monitor; |
| } |
| } |
| return nullptr; |
| } |
| |
| inline void OMCache::clear() { |
| for (size_t i = 0; i < CAPACITY; ++i) { |
| // Clear |
| _entries[i] = {}; |
| } |
| } |
| |
| #endif // SHARE_RUNTIME_LOCKSTACK_INLINE_HPP |