// 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) { }
staticbool 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) { returnfalse;
}
void MonitorEnter(art::Thread* self, bool suspend) NO_THREAD_SAFETY_ANALYSIS { // Perform a suspend-check. The spec doesn't require this but real-world agents depend on this // behavior. We do this by performing a suspend-check then retrying if the thread is suspended // before or after locking the internal mutex. do { if (suspend) {
ThreadUtil::SuspendCheck(self); if (ThreadUtil::WouldSuspendForUserCode(self)) { continue;
}
}
// Check for recursive enter. if (IsOwner(self)) {
count_++; return;
}
// Checking for user-code suspension takes acquiring 2 art::Mutexes so we want to avoid doing // that if possible. To avoid it we try to get the internal mutex without sleeping. If we do // this we don't bother doing another suspend check since it can linearize after the lock. if (mutex_.try_lock()) { break;
} else { // Lock with sleep. We will need to check for suspension after this to make sure that agents // won't deadlock.
mutex_.lock(); if (!suspend || !ThreadUtil::WouldSuspendForUserCode(self)) { break;
} else { // We got suspended in the middle of waiting for the mutex. We should release the mutex // and try again so we can get it while not suspended. This lets some other // (non-suspended) thread acquire the mutex in case it's waiting to wake us up.
mutex_.unlock(); continue;
}
}
} while (true);
private: bool IsOwner(art::Thread* self) const { // 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)) { returnfalse;
}
{
std::unique_lock<std::mutex> lk(mutex_, std::adopt_lock);
how_to_wait(lk); // Here we release the mutex. We will get it back below. We first need to do a suspend-check // without holding it however. This is done in the MonitorEnter function. // TODO We could do this more efficiently. // We hold the mutex_ but the overall monitor is not owned at this point.
CHECK(owner_.load(std::memory_order_relaxed) == nullptr);
DCHECK_EQ(0u, count_);
}
// Reaquire the mutex/monitor, also go to sleep if we were suspended. // TODO Give an extension to wait without suspension as well.
MonitorEnter(self, /*suspend=*/ true);
CHECK(owner_.load(std::memory_order_relaxed) == self);
DCHECK_EQ(1u, count_); // Reset the count.
count_ = old_count;
returntrue;
}
template <typename T> bool Notify(art::Thread* self, T how_to_notify) { if (!IsOwner(self)) { returnfalse;
}
// What millis < 0 means is not defined in the spec. Real world agents seem to assume that it is a // valid call though. We treat it as though it was 0 and wait indefinitely. 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([[maybe_unused]] jvmtiEnv* env, jrawMonitorID id) { if (id == nullptr) { return ERR(INVALID_MONITOR);
}
void Run(art::Thread* target_thread) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
art::ScopedAssertNoThreadSuspension sants("GetContendedMonitorClosure::Run"); switch (target_thread->GetState()) { // These three we are actually currently waiting on a monitor and have sent the appropriate // events (if anyone is listening). case art::ThreadState::kBlocked: case art::ThreadState::kTimedWaiting: case art::ThreadState::kWaiting: {
out_ = art::GcRoot<art::mirror::Object>(art::Monitor::GetContendedMonitor(target_thread)); return;
} case art::ThreadState::kTerminated: case art::ThreadState::kRunnable: case art::ThreadState::kSleeping: case art::ThreadState::kWaitingForLockInflation: case art::ThreadState::kWaitingForTaskProcessor: case art::ThreadState::kWaitingForGcToComplete: case art::ThreadState::kWaitingForCheckPointsToRun: case art::ThreadState::kWaitingPerformingGc: case art::ThreadState::kWaitingForDebuggerSend: case art::ThreadState::kWaitingForDebuggerToAttach: case art::ThreadState::kWaitingInMainDebuggerLoop: case art::ThreadState::kWaitingForDebuggerSuspension: case art::ThreadState::kWaitingForJniOnLoad: case art::ThreadState::kWaitingForSignalCatcherOutput: case art::ThreadState::kWaitingInMainSignalCatcherLoop: case art::ThreadState::kWaitingForDeoptimization: case art::ThreadState::kWaitingForMethodTracingStart: case art::ThreadState::kWaitingForVisitObjects: case art::ThreadState::kWaitingForGetObjectsAllocated: case art::ThreadState::kWaitingWeakGcRootRead: case art::ThreadState::kWaitingForGcThreadFlip: case art::ThreadState::kNativeForAbort: case art::ThreadState::kStarting: case art::ThreadState::kNative: case art::ThreadState::kSuspended: { // We aren't currently (explicitly) waiting for a monitor so just return null. return; case art::ThreadState::kObsoleteRunnable: case art::ThreadState::kInvalidState:
LOG(FATAL) << "UNREACHABLE"; // Obsolete or invalid value.
UNREACHABLE();
}
}
}
private:
art::GcRoot<art::mirror::Object> out_;
};
art::ScopedAssertNoThreadSuspension sants("Performing GetCurrentContendedMonitor");
GetContendedMonitorClosure closure; // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. We // need to avoid suspending as we wait for the checkpoint to occur since we are (potentially) // transfering a GcRoot across threads. if (!target->RequestSynchronousCheckpoint(&closure, art::ThreadState::kRunnable)) { return ERR(THREAD_NOT_ALIVE);
}
*monitor = closure.GetResult(); return OK;
}
} // namespace openjdkjvmti
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.