/* Copyright (C) 2017 The Android Open Source Project * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This file implements interfaces from the file jvmti.h. This implementation * is licensed under the same terms as the file jvmti.h. The * copyright and license information for the file jvmti.h follows. * * Copyright (c) 2003, 2011, 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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 "ti_monitor.h" #include <atomic> #include <chrono> #include <condition_variable> #include <mutex> #include "art_jvmti.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" namespace openjdkjvmti { // We cannot use ART monitors, as they require the mutator lock for contention locking. We // also cannot use pthread mutexes and condition variables (or C++11 abstractions) directly, // as the do not have the right semantics for recursive mutexes and waiting (wait only unlocks // the mutex once). // So go ahead and use a wrapper that does the counting explicitly. class JvmtiMonitor { public: JvmtiMonitor() : owner_(nullptr), count_(0) { } static bool Destroy(art::Thread* self, JvmtiMonitor* monitor) NO_THREAD_SAFETY_ANALYSIS { // Check whether this thread holds the monitor, or nobody does. art::Thread* owner_thread = monitor->owner_.load(std::memory_order_relaxed); if (owner_thread != nullptr && self != owner_thread) { return false; } if (monitor->count_ > 0) { monitor->count_ = 0; monitor->owner_.store(nullptr, std::memory_order_relaxed); monitor->mutex_.unlock(); } delete monitor; return true; } void MonitorEnter(art::Thread* self) NO_THREAD_SAFETY_ANALYSIS { // Check for recursive enter. if (IsOwner(self)) { count_++; return; } mutex_.lock(); DCHECK(owner_.load(std::memory_order_relaxed) == nullptr); owner_.store(self, std::memory_order_relaxed); DCHECK_EQ(0u, count_); count_ = 1; } bool MonitorExit(art::Thread* self) NO_THREAD_SAFETY_ANALYSIS { if (!IsOwner(self)) { return false; } --count_; if (count_ == 0u) { owner_.store(nullptr, std::memory_order_relaxed); mutex_.unlock(); } return true; } bool Wait(art::Thread* self) { auto wait_without_timeout = [&](std::unique_lock<std::mutex>& lk) { cond_.wait(lk); }; return Wait(self, wait_without_timeout); } bool Wait(art::Thread* self, uint64_t timeout_in_ms) { auto wait_with_timeout = [&](std::unique_lock<std::mutex>& lk) { cond_.wait_for(lk, std::chrono::milliseconds(timeout_in_ms)); }; return Wait(self, wait_with_timeout); } bool Notify(art::Thread* self) { return Notify(self, [&]() { cond_.notify_one(); }); } bool NotifyAll(art::Thread* self) { return Notify(self, [&]() { cond_.notify_all(); }); } private: bool IsOwner(art::Thread* self) { // There's a subtle correctness argument here for a relaxed load outside the critical section. // A thread is guaranteed to see either its own latest store or another thread's store. If a // thread sees another thread's store than it cannot be holding the lock. art::Thread* owner_thread = owner_.load(std::memory_order_relaxed); return self == owner_thread; } template <typename T> bool Wait(art::Thread* self, T how_to_wait) { if (!IsOwner(self)) { return false; } size_t old_count = count_; count_ = 0; owner_.store(nullptr, std::memory_order_relaxed); { std::unique_lock<std::mutex> lk(mutex_, std::adopt_lock); how_to_wait(lk); lk.release(); // Do not unlock the mutex. } DCHECK(owner_.load(std::memory_order_relaxed) == nullptr); owner_.store(self, std::memory_order_relaxed); DCHECK_EQ(0u, count_); count_ = old_count; return true; } template <typename T> bool Notify(art::Thread* self, T how_to_notify) { if (!IsOwner(self)) { return false; } how_to_notify(); return true; } std::mutex mutex_; std::condition_variable cond_; std::atomic<art::Thread*> owner_; size_t count_; }; static jrawMonitorID EncodeMonitor(JvmtiMonitor* monitor) { return reinterpret_cast<jrawMonitorID>(monitor); } static JvmtiMonitor* DecodeMonitor(jrawMonitorID id) { return reinterpret_cast<JvmtiMonitor*>(id); } jvmtiError MonitorUtil::CreateRawMonitor(jvmtiEnv* env ATTRIBUTE_UNUSED, const char* name, jrawMonitorID* monitor_ptr) { if (name == nullptr || monitor_ptr == nullptr) { return ERR(NULL_POINTER); } JvmtiMonitor* monitor = new JvmtiMonitor(); *monitor_ptr = EncodeMonitor(monitor); return ERR(NONE); } jvmtiError MonitorUtil::DestroyRawMonitor(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) { if (id == nullptr) { return ERR(INVALID_MONITOR); } JvmtiMonitor* monitor = DecodeMonitor(id); art::Thread* self = art::Thread::Current(); if (!JvmtiMonitor::Destroy(self, monitor)) { return ERR(NOT_MONITOR_OWNER); } return ERR(NONE); } jvmtiError MonitorUtil::RawMonitorEnter(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) { if (id == nullptr) { return ERR(INVALID_MONITOR); } JvmtiMonitor* monitor = DecodeMonitor(id); art::Thread* self = art::Thread::Current(); monitor->MonitorEnter(self); return ERR(NONE); } jvmtiError MonitorUtil::RawMonitorExit(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) { if (id == nullptr) { return ERR(INVALID_MONITOR); } JvmtiMonitor* monitor = DecodeMonitor(id); art::Thread* self = art::Thread::Current(); if (!monitor->MonitorExit(self)) { return ERR(NOT_MONITOR_OWNER); } return ERR(NONE); } jvmtiError MonitorUtil::RawMonitorWait(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id, jlong millis) { if (id == nullptr) { return ERR(INVALID_MONITOR); } JvmtiMonitor* monitor = DecodeMonitor(id); art::Thread* self = art::Thread::Current(); // This is not in the spec, but it's the only thing that makes sense (and agrees with // Object.wait). if (millis < 0) { return ERR(ILLEGAL_ARGUMENT); } bool result = (millis > 0) ? monitor->Wait(self, static_cast<uint64_t>(millis)) : monitor->Wait(self); if (!result) { return ERR(NOT_MONITOR_OWNER); } // TODO: Make sure that is really what we should be checking here. if (self->IsInterrupted()) { return ERR(INTERRUPT); } return ERR(NONE); } jvmtiError MonitorUtil::RawMonitorNotify(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) { if (id == nullptr) { return ERR(INVALID_MONITOR); } JvmtiMonitor* monitor = DecodeMonitor(id); art::Thread* self = art::Thread::Current(); if (!monitor->Notify(self)) { return ERR(NOT_MONITOR_OWNER); } return ERR(NONE); } jvmtiError MonitorUtil::RawMonitorNotifyAll(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) { if (id == nullptr) { return ERR(INVALID_MONITOR); } JvmtiMonitor* monitor = DecodeMonitor(id); art::Thread* self = art::Thread::Current(); if (!monitor->NotifyAll(self)) { return ERR(NOT_MONITOR_OWNER); } return ERR(NONE); } } // namespace openjdkjvmti