/*
 * 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
