/*
 * Copyright (c) 2020, 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 "runtime/interfaceSupport.inline.hpp"
#include "runtime/mutex.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/thread.hpp"
#include "utilities/formatBuffer.hpp"
#include "threadHelper.inline.hpp"
#include "unittest.hpp"

const int iterations = 10;
static Mutex* m[iterations];
static int i = 0;

static void create_mutex(Thread* thr) {
  m[i] = new Mutex(Mutex::nosafepoint, FormatBuffer<128>("MyLock#%u_lock", i));
  i++;
}

TEST_VM(MutexName, mutex_name) {
  // Create mutexes in threads, where the names are created on the thread
  // stacks and then check that their names are correct.
  for (int i = 0; i < iterations; i++) {
    nomt_test_doer(create_mutex);
  }
  for (int i = 0; i < iterations; i++) {
    FormatBuffer<128> f("MyLock#%u_lock", i);
    ASSERT_STREQ(m[i]->name(), f.buffer()) << "Wrong name!";
  }
}

#ifdef ASSERT

const Mutex::Rank rankA = Mutex::safepoint-5;
const Mutex::Rank rankAplusOne = Mutex::safepoint-4;
const Mutex::Rank rankAplusTwo = Mutex::safepoint-3;

TEST_OTHER_VM(MutexRank, mutex_lock_rank_in_order) {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Mutex* mutex_rankA = new Mutex(rankA, "mutex_rankA");
  Mutex* mutex_rankA_plus_one = new Mutex(rankAplusOne, "mutex_rankA_plus_one");

  mutex_rankA_plus_one->lock();
  mutex_rankA->lock();
  mutex_rankA->unlock();
  mutex_rankA_plus_one->unlock();
}

TEST_VM_ASSERT_MSG(MutexRank, mutex_lock_rank_out_of_orderA,
                   ".* Attempting to acquire lock mutex_rankA_plus_one/safepoint-4 out of order with lock mutex_rankA/safepoint-5 -- possible deadlock") {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Mutex* mutex_rankA = new Mutex(rankA, "mutex_rankA");
  Mutex* mutex_rankA_plus_one = new Mutex(rankAplusOne, "mutex_rankA_plus_one");

  mutex_rankA->lock();
  mutex_rankA_plus_one->lock();
  mutex_rankA_plus_one->unlock();
  mutex_rankA->unlock();
}

TEST_VM_ASSERT_MSG(MutexRank, mutex_lock_rank_out_of_orderB,
                   ".* Attempting to acquire lock mutex_rankB/.* out of order with lock mutex_rankA/.* -- possible deadlock") {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Mutex* mutex_rankA = new Mutex(rankA, "mutex_rankA");
  Mutex* mutex_rankB = new Mutex(rankA, "mutex_rankB");

  mutex_rankA->lock();
  mutex_rankB->lock();
  mutex_rankB->unlock();
  mutex_rankA->unlock();
}

TEST_OTHER_VM(MutexRank, mutex_trylock_rank_out_of_orderA) {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Mutex* mutex_rankA = new Mutex(rankA, "mutex_rankA");
  Mutex* mutex_rankA_plus_one = new Mutex(rankAplusOne, "mutex_rankA_plus_one");
  Mutex* mutex_rankA_plus_two = new Mutex(rankAplusTwo, "mutex_rankA_plus_two");

  mutex_rankA_plus_one->lock();
  mutex_rankA_plus_two->try_lock_without_rank_check();
  mutex_rankA->lock();
  mutex_rankA->unlock();
  mutex_rankA_plus_two->unlock();
  mutex_rankA_plus_one->unlock();
}

TEST_VM_ASSERT_MSG(MutexRank, mutex_trylock_rank_out_of_orderB,
                   ".* Attempting to acquire lock mutex_rankA_plus_one/.* out of order with lock mutex_rankA/.* -- possible deadlock") {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Mutex* mutex_rankA = new Mutex(rankA, "mutex_rankA");
  Mutex* mutex_rankA_plus_one = new Mutex(rankAplusOne, "mutex_rankA_plus_one");

  mutex_rankA->lock();
  mutex_rankA_plus_one->try_lock_without_rank_check();
  mutex_rankA_plus_one->unlock();
  mutex_rankA_plus_one->try_lock();
  mutex_rankA_plus_one->unlock();
  mutex_rankA->unlock();
}

TEST_VM_ASSERT_MSG(MutexRank, mutex_lock_event_nosafepoint,
                   ".* Attempting to acquire lock mutex_rank_nosafepoint/nosafepoint out of order with lock mutex_rank_event/event "
                   "-- possible deadlock") {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Mutex* mutex_rank_event = new Mutex(Mutex::event, "mutex_rank_event");
  Mutex* mutex_rank_safepoint = new Mutex(Mutex::nosafepoint, "mutex_rank_nosafepoint");

  mutex_rank_event->lock_without_safepoint_check();
  mutex_rank_safepoint->lock_without_safepoint_check();
  mutex_rank_safepoint->unlock();
  mutex_rank_event->unlock();
}

TEST_VM_ASSERT_MSG(MutexRank, mutex_lock_tty_nosafepoint,
                   ".* Attempting to acquire lock mutex_rank_nosafepoint/nosafepoint out of order with lock mutex_rank_tty/tty "
                   "-- possible deadlock") {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Mutex* mutex_rank_tty = new Mutex(Mutex::tty, "mutex_rank_tty");
  Mutex* mutex_rank_nosafepoint = new Mutex(Mutex::nosafepoint, "mutex_rank_nosafepoint");

  mutex_rank_tty->lock_without_safepoint_check();
  mutex_rank_nosafepoint->lock_without_safepoint_check();
  mutex_rank_nosafepoint->unlock();
  mutex_rank_tty->unlock();
}

TEST_OTHER_VM(MutexRank, monitor_wait_rank_in_order) {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Monitor* monitor_rankA = new Monitor(rankA, "monitor_rankA");
  Monitor* monitor_rankA_plus_one = new Monitor(rankAplusOne, "monitor_rankA_plus_one");

  monitor_rankA_plus_one->lock();
  monitor_rankA->lock();
  monitor_rankA->wait(1);
  monitor_rankA->unlock();
  monitor_rankA_plus_one->unlock();
}

TEST_VM_ASSERT_MSG(MutexRank, monitor_wait_rank_out_of_order,
                   ".* Attempting to wait on monitor monitor_rankA_plus_one/.* while holding lock monitor_rankA/.* "
                   "-- possible deadlock. Should wait on the least ranked monitor from all owned locks.") {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Monitor* monitor_rankA = new Monitor(rankA, "monitor_rankA");
  Monitor* monitor_rankA_plus_one = new Monitor(rankAplusOne, "monitor_rankA_plus_one");

  monitor_rankA_plus_one->lock();
  monitor_rankA->lock();
  monitor_rankA_plus_one->wait(1);
  monitor_rankA_plus_one->unlock();
  monitor_rankA->unlock();
}

TEST_VM_ASSERT_MSG(MutexRank, monitor_wait_rank_out_of_order_trylock,
                   ".* Attempting to wait on monitor monitor_rankA_plus_one/.* while holding lock monitor_rankA/.* "
                   "-- possible deadlock. Should wait on the least ranked monitor from all owned locks.") {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Monitor* monitor_rankA = new Monitor(rankA, "monitor_rankA");
  Monitor* monitor_rankA_plus_one = new Monitor(rankAplusOne, "monitor_rankA_plus_one");

  monitor_rankA->lock();
  monitor_rankA_plus_one->try_lock_without_rank_check();
  monitor_rankA_plus_one->wait();
  monitor_rankA_plus_one->unlock();
  monitor_rankA->unlock();
}

TEST_VM_ASSERT_MSG(MutexRank, monitor_wait_rank_nosafepoint,
                   ".* Attempting to wait on monitor monitor_rank_nosafepoint_minus_one/.* while holding lock monitor_rank_nosafepoint/.*"
                   "-- possible deadlock. Should not block\\(wait\\) while holding a lock of rank nosafepoint or below.") {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Monitor* monitor_rank_nosafepoint = new Monitor(Mutex::nosafepoint, "monitor_rank_nosafepoint");
  Monitor* monitor_rank_nosafepoint_minus_one = new Monitor(Mutex::nosafepoint - 1,
                                                            "monitor_rank_nosafepoint_minus_one");

  monitor_rank_nosafepoint->lock_without_safepoint_check();
  monitor_rank_nosafepoint_minus_one->lock_without_safepoint_check();
  monitor_rank_nosafepoint_minus_one->wait_without_safepoint_check(1);
  monitor_rank_nosafepoint_minus_one->unlock();
  monitor_rank_nosafepoint->unlock();
}

// NonJavaThreads can't wait while holding tty lock or below.
class VM_MutexWaitTTY : public VM_GTestExecuteAtSafepoint {
 public:
  void doit() {
    Monitor* monitor_rank_tty = new Monitor(Mutex::tty, "monitor_rank_tty");
    Monitor* monitor_rank_event = new Monitor(Mutex::event, "monitor_rank_event");

    monitor_rank_tty->lock_without_safepoint_check();
    monitor_rank_event->lock_without_safepoint_check();
    monitor_rank_event->wait_without_safepoint_check(1);
    monitor_rank_event->unlock();
    monitor_rank_tty->unlock();
  }
};

TEST_VM_ASSERT_MSG(MutexRank, monitor_wait_event_tty,
                   ".* Attempting to wait on monitor monitor_rank_event/event while holding lock monitor_rank_tty/tty "
                   "-- possible deadlock. Should not block\\(wait\\) while holding a lock of rank tty or below.") {
  VM_MutexWaitTTY op;
  ThreadInVMfromNative invm(JavaThread::current());
  VMThread::execute(&op);
}

TEST_VM_ASSERT_MSG(MutexRank, monitor_wait_tty_nosafepoint,
                   ".* Attempting to wait on monitor monitor_rank_tty/.* while holding lock monitor_rank_nosafepoint/.* "
                   "-- possible deadlock. Should not block\\(wait\\) while holding a lock of rank nosafepoint or below.") {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Monitor* monitor_rank_nosafepoint = new Monitor(Mutex::nosafepoint, "monitor_rank_nosafepoint");
  Monitor* monitor_rank_tty = new Monitor(Mutex::tty, "monitor_rank_tty");

  monitor_rank_nosafepoint->lock_without_safepoint_check();
  monitor_rank_tty->lock_without_safepoint_check();
  monitor_rank_tty->wait_without_safepoint_check(1);
  monitor_rank_tty->unlock();
  monitor_rank_nosafepoint->unlock();
}

TEST_VM_ASSERT_MSG(MutexRank, monitor_nosafepoint_vm_block,
                   ".*Locks that don't check for safepoint should always allow the vm to block: monitor_rank_nosafepoint") {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Monitor* monitor_rank_nosafepoint = new Monitor(Mutex::nosafepoint, "monitor_rank_nosafepoint", false);
  monitor_rank_nosafepoint->lock_without_safepoint_check();
  monitor_rank_nosafepoint->unlock();
}

TEST_VM_ASSERT_MSG(MutexRank, monitor_negative_rank,
                   ".*Bad lock rank") {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Monitor* monitor_rank_broken = new Monitor(Mutex::safepoint-100, "monitor_rank_broken");
  monitor_rank_broken->lock_without_safepoint_check();
  monitor_rank_broken->unlock();
}

TEST_VM_ASSERT_MSG(MutexRank, monitor_overlapping_oopstorage_rank,
                   ".*Rank oopstorage-4 overlaps with tty-1") {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Monitor* monitor_rank_broken = new Monitor(Mutex::oopstorage-4, "monitor_rank_broken");
}

TEST_VM_ASSERT_MSG(MutexRank, monitor_overlapping_safepoint_rank,
                   ".*Rank safepoint-40 overlaps with service-5") {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Monitor* monitor_rank_broken = new Monitor(Mutex::safepoint-40, "monitor_rank_broken");
}

TEST_VM_ASSERT_MSG(MutexRank, monitor_overlapping_safepoint_rank2,
                   ".*Rank safepoint-1-39 overlaps with service-5") {
  JavaThread* THREAD = JavaThread::current();
  ThreadInVMfromNative invm(THREAD);

  Monitor* monitor_rank_ok = new Monitor(Mutex::safepoint-1, "monitor_rank_ok");
  Monitor* monitor_rank_broken = new Monitor(monitor_rank_ok->rank()-39, "monitor_rank_broken");
}

// Test mismatched safepoint check flag on lock declaration vs. lock acquisition.
TEST_VM_ASSERT_MSG(MutexSafepoint, always_check,
    ".*This lock should always have a safepoint check for Java threads: SFPT_Test_lock") {
  MutexLocker ml(new Mutex(Mutex::safepoint, "SFPT_Test_lock"),
                 Mutex::_no_safepoint_check_flag);
}

TEST_VM_ASSERT_MSG(MutexSafepoint, never_check,
    ".*This lock should not be taken with a safepoint check: SFPT_Test_lock") {
  MutexLocker ml(new Mutex(Mutex::nosafepoint, "SFPT_Test_lock"),
                 Mutex::_safepoint_check_flag);
}

TEST_VM_ASSERT_MSG(MutexSafepoint, possible_safepoint_lock,
    ".* Possible safepoint reached by thread that does not allow it") {
  JavaThread* thread = JavaThread::current();
  ThreadInVMfromNative in_native(thread);
  MutexLocker ml(new Mutex(Mutex::nosafepoint, "SpecialTest_lock"),
                   Mutex::_no_safepoint_check_flag);
  thread->print_thread_state_on(tty);
  // If the lock above succeeds, try to safepoint to test the NSV implied with this nosafepoint lock.
  ThreadBlockInVM tbivm(thread);
  thread->print_thread_state_on(tty);
}
#endif // ASSERT