void Monitor::Init(uint32_t lock_profiling_threshold,
uint32_t stack_dump_lock_profiling_threshold) { // It isn't great to always include the debug build fudge factor for command- // line driven arguments, but it's easier to adjust here than in the build.
lock_profiling_threshold_ =
lock_profiling_threshold * kDebugThresholdFudgeFactor;
stack_dump_lock_profiling_threshold_ =
stack_dump_lock_profiling_threshold * kDebugThresholdFudgeFactor;
}
Monitor::Monitor(
Thread* self, MonitorOwner owner, ObjPtr<mirror::Object> obj, int32_t hash_code, MonitorId id)
: monitor_lock_(),
num_waiters_(0),
owner_(owner),
lock_count_(0),
obj_(GcRoot<mirror::Object>(obj)),
wait_set_(nullptr),
wake_set_(nullptr),
hash_code_(hash_code),
lock_owner_(),
lock_owner_method_(nullptr),
lock_owner_dex_pc_(0),
lock_owner_sum_(0),
lock_owner_request_(),
monitor_id_(id) { #ifdef __LP64__
next_free_ = nullptr; #endif // We should only inflate a lock if the owner is ourselves or suspended. This avoids a race // with the owner unlocking the thin-lock.
CHECK(owner.IsNull() || owner == self ||
(!owner.IsVirtualThread() && owner.GetThreadPtr()->IsSuspended()) ||
(owner.IsVirtualThread())); // Disable this check of virtual thread suspension due to a lock ordering issue because // thread_list_lock is acquired in IsVirtualThreadSuspended(id) but the current thread // holds the allocated_monitor_ids_lock earlier in `MonitorPool::CreateMonitorInPool`. // It's okay not to check here because the caller should have ensured the virtual thread owner // has been suspended. // // && Runtime::Current()->GetThreadList()->IsVirtualThreadSuspended(owner.GetVirtualId())));
// The identity hash code is set for the life time of the monitor.
bool monitor_timeout_enabled = Runtime::Current()->IsMonitorTimeoutEnabled(); if (monitor_timeout_enabled) {
MaybeEnableTimeout();
}
}
int32_t Monitor::GetHashCode() {
int32_t hc = hash_code_.load(std::memory_order_relaxed); if (!HasHashCode()) { // Use a strong CAS to prevent spurious failures since these can make the boot image // non-deterministic.
hash_code_.CompareAndSetStrongRelaxed(0, mirror::Object::GenerateIdentityHashCode());
hc = hash_code_.load(std::memory_order_relaxed);
}
DCHECK(HasHashCode()); return hc;
}
void Monitor::SetLockingMethod(Thread* owner) {
DCHECK(owner == Thread::Current() || owner->IsSuspended()); // Do not abort on dex pc errors. This can easily happen when we want to dump a stack trace on // abort.
ArtMethod* lock_owner_method;
uint32_t lock_owner_dex_pc;
lock_owner_method = owner->GetCurrentMethod(&lock_owner_dex_pc, false); if (lock_owner_method != nullptr && UNLIKELY(lock_owner_method->IsProxyMethod())) { // Grab another frame. Proxy methods are not helpful for lock profiling. This should be rare // enough that it's OK to walk the stack twice. struct NextMethodVisitor final : public StackVisitor { explicit NextMethodVisitor(Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_)
: StackVisitor(thread,
nullptr,
StackVisitor::StackWalkKind::kIncludeInlinedFrames, false),
count_(0),
method_(nullptr),
dex_pc_(0) {} bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* m = GetMethod(); if (m->IsRuntimeMethod()) { // Continue if this is a runtime method. returntrue;
}
count_++; if (count_ == 2u) {
method_ = m;
dex_pc_ = GetDexPc(false); returnfalse;
} returntrue;
}
size_t count_;
ArtMethod* method_;
uint32_t dex_pc_;
};
NextMethodVisitor nmv(owner);
nmv.WalkStack();
lock_owner_method = nmv.method_;
lock_owner_dex_pc = nmv.dex_pc_;
}
SetLockOwnerInfo(lock_owner_method, lock_owner_dex_pc, MonitorOwner::FromPlatformThread(owner));
DCHECK(lock_owner_method == nullptr || !lock_owner_method->IsProxyMethod());
}
bool Monitor::Install(Thread* self) NO_THREAD_SAFETY_ANALYSIS { // This may or may not result in acquiring monitor_lock_. Its behavior is much more complicated // than what clang thread safety analysis understands. // Monitor is not yet public.
MonitorOwner owner = owner_.load(std::memory_order_relaxed);
ThreadList* thread_list = Runtime::Current()->GetThreadList();
DCHECK(owner == nullptr || owner == self ||
(!owner.IsVirtualThread() && owner.GetThreadPtr()->IsSuspended()) ||
(owner.IsVirtualThread()) &&
thread_list->IsVirtualThreadSuspended(self, owner.GetVirtualThreadId())); // Propagate the lock state.
LockWord lw(GetObject()->GetLockWord(false)); switch (lw.GetState()) { case LockWord::kThinLocked: {
DCHECK(owner != nullptr);
CHECK_EQ(owner.GetThreadId(), lw.ThinLockOwner())
<< " my thread id = " << self->GetThreadId()
<< " my monitor thread id = " << self->GetMonitorThreadId();
lock_count_ = lw.ThinLockCount();
DCHECK_EQ(monitor_lock_.GetExclusiveOwnerTid(), 0) << " my tid = " << SafeGetTid(self); if (kIsVirtualThreadEnabled && UNLIKELY(owner.IsVirtualThread())) {
monitor_lock_.ExclusiveLockUncontendedForVirtualThreadId(owner.GetVirtualThreadId());
} else {
monitor_lock_.ExclusiveLockUncontendedFor(owner.GetThreadPtr());
}
DCHECK_EQ(monitor_lock_.GetExclusiveOwnerTid(), owner.GetMutexOwnerId())
<< " my tid = " << SafeGetTid(self);
LockWord fat(this, lw.GCState()); // Publish the updated lock word, which may race with other threads. bool success = GetObject()->CasLockWord(lw, fat, CASMode::kWeak, std::memory_order_release); if (success) { if (ATraceEnabled()) { if (owner == self) {
SetLockingMethod(self);
} elseif (UNLIKELY(owner.IsVirtualThread())) {
MutexLock mu(self, *Locks::thread_list_lock_);
uint32_t carrier_id = thread_list->GetCarrierThreadIdByVirtualThreadId(
owner.GetVirtualThreadId()); if (carrier_id == ThreadList::kInvalidThreadId) { // The virtual thread is unmounted. // TODO(b/460438903): Walk the stack of an unmounted virtual thread instead of // hard-coding the current method. However, this hard-coded value is quite accurate at // the time of writing because this is the only method that can unmount a virtual // thread. Also, when the owner thread isn't the current thread, the current method // and dex PC are just the points when the thread is suspended, but not the dex // instruction acquiring the monitor. The locking method and dex pc are later updated // when exiting the monitor. This value is probably used for a long contention only.
SetLockOwnerInfo(WellKnownClasses::jdk_internal_vm_Continuation_doYieldNative,
dex::kDexNoIndex,
owner);
} else { // The virtual thread is mounted.
Thread* carrier = thread_list->FindThreadByThreadId(carrier_id);
DCHECK_NE(carrier, nullptr);
DCHECK(carrier->IsSuspended());
SetLockingMethod(carrier);
}
} else {
SetLockingMethod(owner.GetThreadPtr());
}
} returntrue;
} else {
monitor_lock_.ExclusiveUnlockUncontended(); returnfalse;
}
} case LockWord::kHashCode: {
CHECK_EQ(hash_code_.load(std::memory_order_relaxed), static_cast<int32_t>(lw.GetHashCode()));
DCHECK_EQ(monitor_lock_.GetExclusiveOwnerTid(), 0) << " my tid = " << SafeGetTid(self);
LockWord fat(this, lw.GCState()); return GetObject()->CasLockWord(lw, fat, CASMode::kWeak, std::memory_order_release);
} case LockWord::kFatLocked: { // The owner_ is suspended but another thread beat us to install a monitor. returnfalse;
} case LockWord::kUnlocked: {
LOG(FATAL) << "Inflating unlocked lock word";
UNREACHABLE();
} default: {
LOG(FATAL) << "Invalid monitor state " << lw.GetState();
UNREACHABLE();
}
}
}
Monitor::~Monitor() { // Deflated monitors have a null object.
}
void Monitor::AppendToWaitSet(Thread* thread) { // Not checking that the owner is equal to this thread, since we've released // the monitor by the time this method is called.
DCHECK(thread != nullptr);
DCHECK(thread->GetWaitNext() == nullptr) << thread->GetWaitNext(); if (wait_set_ == nullptr) {
wait_set_ = thread; return;
}
// push_back.
Thread* t = wait_set_; while (t->GetWaitNext() != nullptr) {
t = t->GetWaitNext();
}
t->SetWaitNext(thread);
}
void Monitor::RemoveFromWaitSet(Thread *thread) {
DCHECK(owner_.load() == Thread::Current());
DCHECK(thread != nullptr); auto remove = [&](Thread*& set){ if (set != nullptr) { if (set == thread) {
set = thread->GetWaitNext();
thread->SetWaitNext(nullptr); returntrue;
}
Thread* t = set; while (t->GetWaitNext() != nullptr) { if (t->GetWaitNext() == thread) {
t->SetWaitNext(thread->GetWaitNext());
thread->SetWaitNext(nullptr); returntrue;
}
t = t->GetWaitNext();
}
} returnfalse;
}; if (remove(wait_set_)) { return;
}
remove(wake_set_);
}
// This function is inlined and just helps to not have the VLOG and ATRACE check at all the // potential tracing points. void Monitor::AtraceMonitorLock(Thread* self, ObjPtr<mirror::Object> obj, bool is_wait) { if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging) && ATraceEnabled())) {
AtraceMonitorLockImpl(self, obj, is_wait);
}
}
void Monitor::AtraceMonitorLockImpl(Thread* self, ObjPtr<mirror::Object> obj, bool is_wait) { // Wait() requires a deeper call stack to be useful. Otherwise you'll see "Waiting at // Object.java". Assume that we'll wait a nontrivial amount, so it's OK to do a longer // stack walk than if !is_wait. const size_t wanted_frame_number = is_wait ? 1U : 0U;
size_t current_frame_number = 0u;
StackVisitor::WalkStack( // Note: Adapted from CurrentMethodVisitor in thread.cc. We must not resolve here.
[&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* m = stack_visitor->GetMethod(); if (m == nullptr || m->IsRuntimeMethod()) { // Runtime method, upcall, or resolution issue. Skip. returntrue;
}
// Is this the requested frame? if (current_frame_number == wanted_frame_number) {
method = m;
dex_pc = stack_visitor->GetDexPc(false/* abort_on_error*/); returnfalse;
}
// Look for more.
current_frame_number++; returntrue;
},
self, /* context= */ nullptr,
art::StackVisitor::StackWalkKind::kIncludeInlinedFrames);
// It would be nice to have a stable "ID" for the object here. However, the only stable thing // would be the identity hashcode. But we cannot use IdentityHashcode here: For one, there are // times when it is unsafe to make that call (see stack dumping for an explanation). More // importantly, we would have to give up on thin-locking when adding systrace locks, as the // identity hashcode is stored in the lockword normally (so can't be used with thin-locks). // // Because of thin-locks we also cannot use the monitor id (as there is no monitor). Monitor ids // also do not have to be stable, as the monitor may be deflated.
std::string tmp = StringPrintf("%s %d at %s:%d",
prefix,
(obj == nullptr ? -1 : static_cast<int32_t>(reinterpret_cast<uintptr_t>(obj.Ptr()))),
(filename != nullptr ? filename : "null"),
line_number);
ATraceBegin(tmp.c_str());
}
void Monitor::AtraceMonitorUnlock() { if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging))) {
ATraceEnd();
}
}
// Do this before releasing the mutator lock so that we don't get deflated.
size_t num_waiters = num_waiters_.fetch_add(1, std::memory_order_relaxed);
bool started_trace = false; if (ATraceEnabled() && owner_.load(std::memory_order_relaxed) != nullptr) { // Acquiring thread_list_lock_ ensures that owner doesn't disappear while // we're looking at it.
Locks::thread_list_lock_->ExclusiveLock(self);
orig_owner = owner_.load(std::memory_order_relaxed); if (orig_owner != nullptr) { // Did the owner_ give the lock up?
Thread* owner_thread; if (orig_owner.IsVirtualThread()) {
ThreadList* thread_list = Runtime::Current()->GetThreadList();
uint32_t carrier_id = thread_list->GetCarrierThreadIdByVirtualThreadId(
orig_owner.GetVirtualThreadId());
owner_thread = thread_list->FindThreadByThreadId(carrier_id);
} else {
owner_thread = orig_owner.GetThreadPtr();
}
pid_t owner_thread_tid = 0;
std::string owner_name; if (owner_thread != nullptr) {
owner_thread_tid = owner_thread->GetTid();
owner_thread->GetThreadName(owner_name);
} const uint32_t virtual_thread_id = orig_owner.IsVirtualThread() ?
orig_owner.GetVirtualThreadId() : ThreadList::kInvalidThreadId;
GetLockOwnerInfo(&owners_method, &owners_dex_pc, orig_owner);
std::ostringstream oss;
oss << PrettyContentionInfo(owner_name,
owner_thread_tid,
virtual_thread_id,
owners_method,
owners_dex_pc,
num_waiters);
Locks::thread_list_lock_->ExclusiveUnlock(self); // Add info for contending thread.
uint32_t pc;
ArtMethod* m = self->GetCurrentMethod(&pc); constchar* filename;
int32_t line_number;
TranslateLocation(m, pc, &filename, &line_number);
oss << " blocking from "
<< ArtMethod::PrettyMethod(m) << "(" << (filename != nullptr ? filename : "null")
<< ":" << line_number << ")";
ATraceBegin(oss.str().c_str());
started_trace = true;
} else {
Locks::thread_list_lock_->ExclusiveUnlock(self);
}
} if (log_contention) { // Request the current holder to set lock_owner_info. // Do this even if tracing is enabled, so we semi-consistently get the information // corresponding to MonitorExit. // TODO: Consider optionally obtaining a stack trace here via a checkpoint. That would allow // us to see what the other thread is doing while we're waiting.
orig_owner = owner_.load(std::memory_order_relaxed);
lock_owner_request_.store(orig_owner, std::memory_order_relaxed);
} // Call the contended locking cb once and only once. Also only call it if we are locking for // the first time, not during a Wait wakeup. if (reason == LockReason::kForLock && !called_monitors_callback) {
called_monitors_callback = true;
Runtime::Current()->GetRuntimeCallbacks()->MonitorContendedLocking(this);
}
self->SetMonitorEnterObject(GetObject().Ptr());
{ // Change to blocked and give up mutator_lock_.
ScopedThreadSuspension tsc(self, ThreadState::kBlocked);
// Acquire monitor_lock_ without mutator_lock_, expecting to block this time. // We already tried spinning above. The shutdown procedure currently assumes we stop // touching monitors shortly after we suspend, so don't spin again here.
monitor_lock_.ExclusiveLock(self);
if (log_contention && orig_owner != nullptr) { // Woken from contention.
uint64_t wait_ms = MilliTime() - wait_start_ms;
uint32_t sample_percent; if (wait_ms >= lock_profiling_threshold_) {
sample_percent = 100;
} else {
sample_percent = 100 * wait_ms / lock_profiling_threshold_;
} if (sample_percent != 0 && (static_cast<uint32_t>(rand() % 100) < sample_percent)) { // Do this unconditionally for consistency. It's possible another thread // snuck in in the middle, and tracing was enabled. In that case, we may get its // MonitorEnter information. We can live with that.
GetLockOwnerInfo(&owners_method, &owners_dex_pc, orig_owner);
// Reacquire mutator_lock_ for logging.
ScopedObjectAccess soa(self);
// Acquire thread-list lock to find thread and keep it from dying until we've got all // the info we need.
Locks::thread_list_lock_->ExclusiveLock(self);
// Is there still a thread at the same address as the original owner? // We tolerate the fact that it may occasionally be the wrong one.
ThreadList* thread_list = Runtime::Current()->GetThreadList(); if (orig_owner.IsVirtualThread() || thread_list->Contains(orig_owner.GetThreadPtr())) {
Thread* owner_thread; if (orig_owner.IsVirtualThread()) {
uint32_t carrier_id =
thread_list->GetCarrierThreadIdByVirtualThreadId(orig_owner.GetVirtualThreadId());
owner_thread = thread_list->FindThreadByThreadId(carrier_id);
} else {
owner_thread = orig_owner.GetThreadPtr();
}
pid_t owner_thread_tid = 0;
std::string owner_name; if (owner_thread != nullptr) {
owner_thread_tid = owner_thread->GetTid();
owner_thread->GetThreadName(owner_name);
} const uint32_t virtual_thread_id = orig_owner.IsVirtualThread() ?
orig_owner.GetVirtualThreadId() : ThreadList::kInvalidThreadId;
std::string owner_stack_dump; if (should_dump_stacks && owner_thread != nullptr) { // Very long contention. Dump stacks. struct CollectStackTrace : public Closure { void Run(art::Thread* thread) override
REQUIRES_SHARED(art::Locks::mutator_lock_) {
thread->DumpJavaStack(oss);
}
std::ostringstream oss;
};
CollectStackTrace owner_trace; // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its // execution.
owner_thread->RequestSynchronousCheckpoint(&owner_trace);
owner_stack_dump = owner_trace.oss.str();
} else {
Locks::thread_list_lock_->ExclusiveUnlock(self);
}
// This is all the data we need. We dropped the thread-list lock, it's OK for the // owner to go away now.
if (should_dump_stacks) { // Give the detailed traces for really long contention. // This must be here (and not above) because we cannot hold the thread-list lock // while running the checkpoint.
std::ostringstream self_trace_oss;
self->DumpJavaStack(self_trace_oss);
uint32_t pc;
ArtMethod* m = self->GetCurrentMethod(&pc);
LOG(WARNING) << "Long "
<< PrettyContentionInfo(owner_name, owner_thread_tid, virtual_thread_id,
owners_method, owners_dex_pc, num_waiters)
<< " in " << ArtMethod::PrettyMethod(m) << " for "
<< PrettyDuration(MsToNs(wait_ms)) << "\n"
<< "Current owner stack:\n"
<< owner_stack_dump << "Contender stack:\n"
<< self_trace_oss.str();
} elseif (wait_ms > kLongWaitMs && owners_method != nullptr) {
uint32_t pc;
ArtMethod* m = self->GetCurrentMethod(&pc); // TODO: We should maybe check that original_owner is still a live thread.
LOG(WARNING) << "Long "
<< PrettyContentionInfo(owner_name, owner_thread_tid, virtual_thread_id,
owners_method, owners_dex_pc, num_waiters)
<< " in " << ArtMethod::PrettyMethod(m) << " for "
<< PrettyDuration(MsToNs(wait_ms));
}
LogContentionEvent(self,
wait_ms,
sample_percent,
owners_method,
owners_dex_pc);
} else {
Locks::thread_list_lock_->ExclusiveUnlock(self);
}
}
}
} // We've successfully acquired monitor_lock_, released thread_list_lock, and are runnable.
// We avoided touching monitor fields while suspended, so set owner_ here.
owner_.store(MonitorOwner::FromThread(self), std::memory_order_relaxed);
DCHECK_EQ(lock_count_, 0u);
if (ATraceEnabled()) {
SetLockingMethodNoProxy(self);
} if (started_trace) {
ATraceEnd();
}
self->SetMonitorEnterObject(nullptr);
num_waiters_.fetch_sub(1, std::memory_order_relaxed);
DCHECK(monitor_lock_.IsExclusiveHeld(self)); // We need to pair this with a single contended locking call. NB we match the RI behavior and call // this even if MonitorEnter failed. if (called_monitors_callback) {
CHECK(reason == LockReason::kForLock);
Runtime::Current()->GetRuntimeCallbacks()->MonitorContendedLocked(this);
}
}
// Re-read owner now that we hold lock.
MonitorOwner current_owner = (monitor != nullptr) ? monitor->GetOwner() : MonitorOwner(); if (current_owner != nullptr) {
current_owner_thread_id = current_owner.GetThreadId();
} // Get short descriptions of the threads involved.
current_owner_string = MonitorOwnerToString(current_owner);
expected_owner_string = MonitorOwnerToString(expected_owner);
found_owner_string = found_owner != nullptr ? MonitorOwnerToString(found_owner) : "unnamed";
}
if (current_owner_thread_id == 0u) { if (found_owner_thread_id == 0u) {
ThrowIllegalMonitorStateExceptionF("unlock of unowned monitor on object of type '%s'" " on thread '%s'",
mirror::Object::PrettyTypeOf(o).c_str(),
expected_owner_string.c_str());
} else { // Race: the original read found an owner but now there is none
ThrowIllegalMonitorStateExceptionF("unlock of monitor owned by '%s' on object of type '%s'" " (where now the monitor appears unowned) on thread '%s'",
found_owner_string.c_str(),
mirror::Object::PrettyTypeOf(o).c_str(),
expected_owner_string.c_str());
}
} else { if (found_owner_thread_id == 0u) { // Race: originally there was no owner, there is now
ThrowIllegalMonitorStateExceptionF("unlock of monitor owned by '%s' on object of type '%s'" " (originally believed to be unowned) on thread '%s'",
current_owner_string.c_str(),
mirror::Object::PrettyTypeOf(o).c_str(),
expected_owner_string.c_str());
} else { if (found_owner_thread_id != current_owner_thread_id) { // Race: originally found and current owner have changed
ThrowIllegalMonitorStateExceptionF("unlock of monitor originally owned by '%s' (now" " owned by '%s') on object of type '%s' on thread '%s'",
found_owner_string.c_str(),
current_owner_string.c_str(),
mirror::Object::PrettyTypeOf(o).c_str(),
expected_owner_string.c_str());
} else {
ThrowIllegalMonitorStateExceptionF("unlock of monitor owned by '%s' on object of type '%s'" " on thread '%s",
current_owner_string.c_str(),
mirror::Object::PrettyTypeOf(o).c_str(),
expected_owner_string.c_str());
}
}
}
}
bool Monitor::Unlock(Thread* self) {
DCHECK(self != nullptr);
MonitorOwner owner = owner_.load(std::memory_order_relaxed); if (owner == self) { // We own the monitor, so nobody else can be in here.
CheckLockOwnerRequest(self);
AtraceMonitorUnlock(); if (lock_count_ == 0) {
owner_.store(MonitorOwner(), std::memory_order_relaxed);
SignalWaiterAndReleaseMonitorLock(self);
} else {
--lock_count_;
DCHECK(monitor_lock_.IsExclusiveHeld(self));
DCHECK(owner_.load(std::memory_order_relaxed) == self); // Keep monitor_lock_, but pretend we released it.
FakeUnlockMonitorLock();
} returntrue;
} // We don't own this, so we're not allowed to unlock it. // The JNI spec says that we should throw IllegalMonitorStateException in this case.
uint32_t owner_thread_id = 0u;
{
MutexLock mu(self, *Locks::thread_list_lock_);
owner = owner_.load(std::memory_order_relaxed); if (owner != nullptr) {
owner_thread_id = owner.GetThreadId();
}
}
FailedUnlock(GetObject(), self, owner_thread_id, this); // Pretend to release monitor_lock_, which we should not.
FakeUnlockMonitorLock(); returnfalse;
}
void Monitor::SignalWaiterAndReleaseMonitorLock(Thread* self) { // We want to release the monitor and signal up to one thread that was waiting // but has since been notified.
DCHECK_EQ(lock_count_, 0u);
DCHECK(monitor_lock_.IsExclusiveHeld(self)); while (wake_set_ != nullptr) { // No risk of waking ourselves here; since monitor_lock_ is not released until we're ready to // return, notify can't move the current thread from wait_set_ to wake_set_ until this // method is done checking wake_set_.
Thread* thread = wake_set_;
wake_set_ = thread->GetWaitNext();
thread->SetWaitNext(nullptr);
DCHECK(owner_.load(std::memory_order_relaxed) == nullptr);
// Check to see if the thread is still waiting.
{ // In the case of wait(), we'll be acquiring another thread's GetWaitMutex with // self's GetWaitMutex held. This does not risk deadlock, because we only acquire this lock // for threads in the wake_set_. A thread can only enter wake_set_ from Notify or NotifyAll, // and those hold monitor_lock_. Thus, the threads whose wait mutexes we acquire here must // have already been released from wait(), since we have not released monitor_lock_ until // after we've chosen our thread to wake, so there is no risk of the following lock ordering // leading to deadlock: // Thread 1 waits // Thread 2 waits // Thread 3 moves threads 1 and 2 from wait_set_ to wake_set_ // Thread 1 enters this block, and attempts to acquire Thread 2's GetWaitMutex to wake it // Thread 2 enters this block, and attempts to acquire Thread 1's GetWaitMutex to wake it // // Since monitor_lock_ is not released until the thread-to-be-woken-up's GetWaitMutex is // acquired, two threads cannot attempt to acquire each other's GetWaitMutex while holding // their own and cause deadlock.
MutexLock wait_mu(self, *thread->GetWaitMutex()); if (thread->GetWaitMonitor() != nullptr) { // Release the lock, so that a potentially awakened thread will not // immediately contend on it. The lock ordering here is: // monitor_lock_, self->GetWaitMutex, thread->GetWaitMutex
monitor_lock_.Unlock(self); // Releases contenders.
thread->GetWaitConditionVariable()->Signal(self); return;
}
}
}
monitor_lock_.Unlock(self);
DCHECK(!monitor_lock_.IsExclusiveHeld(self));
}
// Make sure that we hold the lock. if (owner_.load(std::memory_order_relaxed) != self) {
ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()"); return;
}
// We need to turn a zero-length timed wait into a regular wait because // Object.wait(0, 0) is defined as Object.wait(0), which is defined as Object.wait(). if (why == ThreadState::kTimedWaiting && (ms == 0 && ns == 0)) {
why = ThreadState::kWaiting;
}
// Enforce the timeout range. if (ms < 0 || ns < 0 || ns > 999999) {
self->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;", "timeout arguments out of range: ms=%" PRId64 " ns=%d", ms, ns); return;
}
AtraceMonitorUnlock(); // For the implict Unlock() just above. This will only end the deepest // nesting, but that is enough for the visualization, and corresponds to // the single Lock() we do afterwards.
AtraceMonitorLock(self, GetObject(), /* is_wait= */ true);
bool was_interrupted = false; bool timed_out = false; // Update monitor state now; it's not safe once we're "suspended".
owner_.store(MonitorOwner(), std::memory_order_relaxed);
num_waiters_.fetch_add(1, std::memory_order_relaxed);
{ // Update thread state. If the GC wakes up, it'll ignore us, knowing // that we won't touch any references in this state, and we'll check // our suspend mode before we transition out.
ScopedThreadSuspension sts(self, why);
// Pseudo-atomically wait on self's wait_cond_ and release the monitor lock.
MutexLock mu(self, *self->GetWaitMutex());
// Set wait_monitor_ to the monitor object we will be waiting on. When wait_monitor_ is // non-null a notifying or interrupting thread must signal the thread's wait_cond_ to wake it // up.
DCHECK(self->GetWaitMonitor() == nullptr);
self->SetWaitMonitor(this);
// Release the monitor lock.
DCHECK(monitor_lock_.IsExclusiveHeld(self));
SignalWaiterAndReleaseMonitorLock(self);
// Handle the case where the thread was interrupted before we called wait(). if (self->IsInterrupted()) {
was_interrupted = true;
} else { // Wait for a notification or a timeout to occur. if (why == ThreadState::kWaiting) {
self->GetWaitConditionVariable()->Wait(self);
} else {
DCHECK(why == ThreadState::kTimedWaiting || why == ThreadState::kSleeping) << why;
timed_out = self->GetWaitConditionVariable()->TimedWait(self, ms, ns);
}
was_interrupted = self->IsInterrupted();
}
}
{ // We reset the thread's wait_monitor_ field after transitioning back to runnable so // that a thread in a waiting/sleeping state has a non-null wait_monitor_ for debugging // and diagnostic purposes. (If you reset this earlier, stack dumps will claim that threads // are waiting on "null".)
MutexLock mu(self, *self->GetWaitMutex());
DCHECK(self->GetWaitMonitor() != nullptr);
self->SetWaitMonitor(nullptr);
}
// Allocate the interrupted exception not holding the monitor lock since it may cause a GC. // If the GC requires acquiring the monitor for enqueuing cleared references, this would // cause a deadlock if the monitor is held. if (was_interrupted && interruptShouldThrow) { /* *Wewereinterruptedwhilewaiting,orsomebodyinterruptedan *un-interruptiblethreadearlierandwe'rebailingoutimmediately. * *Thedocsayeth:"Theinterruptedstatusofthecurrentthreadis *clearedwhenthisexceptionisthrown."
*/
self->SetInterrupted(false);
self->ThrowNewException("Ljava/lang/InterruptedException;", nullptr);
}
AtraceMonitorUnlock(); // End Wait().
// We just slept, tell the runtime callbacks about this.
Runtime::Current()->GetRuntimeCallbacks()->MonitorWaitFinished(this, timed_out);
// Re-acquire the monitor and lock.
Lock<LockReason::kForWait>(self);
lock_count_ = prev_lock_count;
DCHECK(monitor_lock_.IsExclusiveHeld(self));
self->GetWaitMutex()->AssertNotHeld(self);
void Monitor::Notify(Thread* self) {
DCHECK(self != nullptr); // Make sure that we hold the lock. if (owner_.load(std::memory_order_relaxed) != self) {
ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()"); return;
} // Move one thread from waiters to wake set
Thread* to_move = wait_set_; if (to_move != nullptr) {
wait_set_ = to_move->GetWaitNext();
to_move->SetWaitNext(wake_set_);
wake_set_ = to_move;
}
}
void Monitor::NotifyAll(Thread* self) {
DCHECK(self != nullptr); // Make sure that we hold the lock. if (owner_.load(std::memory_order_relaxed) != self) {
ThrowIllegalMonitorStateExceptionF("object not locked by thread before notifyAll()"); return;
}
// Move all threads from waiters to wake set
Thread* to_move = wait_set_; if (to_move != nullptr) {
wait_set_ = nullptr;
Thread* move_to = wake_set_; if (move_to == nullptr) {
wake_set_ = to_move; return;
} while (move_to->GetWaitNext() != nullptr) {
move_to = move_to->GetWaitNext();
}
move_to->SetWaitNext(to_move);
}
}
bool Monitor::Deflate(Thread* self, ObjPtr<mirror::Object> obj) { // No other relevant code is running. We should hold mutator_lock_ exclusively, but // ImageWriter cheats, since it's single-threaded.
DCHECK(obj != nullptr); // Don't need volatile since we only deflate with mutators suspended.
LockWord lw(obj->GetLockWord(false)); // If the lock isn't an inflated monitor, then we don't need to deflate anything. if (lw.GetState() == LockWord::kFatLocked) {
Monitor* monitor = lw.FatLockMonitor();
DCHECK(monitor != nullptr); // Can't deflate if we have anybody waiting on the CV or trying to acquire the monitor. if (monitor->num_waiters_.load(std::memory_order_relaxed) > 0) { returnfalse;
} if (!monitor->monitor_lock_.ExclusiveTryLock</* check= */ false>(self)) { // We cannot deflate a monitor that's currently held. It's unclear whether we should if // we could. returnfalse;
}
DCHECK_EQ(monitor->lock_count_, 0u);
DCHECK(monitor->owner_.load(std::memory_order_relaxed).IsNull()); if (monitor->HasHashCode()) {
LockWord new_lw = LockWord::FromHashCode(monitor->GetHashCode(), lw.GCState()); // Assume no concurrent read barrier state changes as mutators are suspended.
obj->SetLockWord(new_lw, false);
VLOG(monitor) << "Deflated " << obj << " to hash monitor " << monitor->GetHashCode();
} else { // No lock and no hash, just put an empty lock word inside the object.
LockWord new_lw = LockWord::FromDefault(lw.GCState()); // Assume no concurrent read barrier state changes as mutators are suspended.
obj->SetLockWord(new_lw, false);
VLOG(monitor) << "Deflated" << obj << " to empty lock word";
}
monitor->monitor_lock_.ExclusiveUnlock(self);
DCHECK(!(monitor->monitor_lock_.IsExclusiveHeld(self))); // The monitor is deflated, mark the object as null so that we know to delete it during the // next GC.
monitor->obj_ = GcRoot<mirror::Object>(nullptr);
} returntrue;
}
void Monitor::Inflate(Thread* self,
MonitorOwner owner,
ObjPtr<mirror::Object> obj,
int32_t hash_code) NO_THREAD_SAFETY_ANALYSIS {
DCHECK(self != nullptr);
DCHECK(obj != nullptr); // Allocate and acquire a new monitor.
Monitor* m = MonitorPool::CreateMonitor(self, owner, obj, hash_code);
DCHECK(m != nullptr); if (m->Install(self)) { if (owner != nullptr) {
VLOG(monitor) << "monitor: thread" << owner.GetThreadId() << " created monitor " << m
<< " for object " << obj;
} else {
VLOG(monitor) << "monitor: Inflate with hashcode " << hash_code
<< " created monitor " << m << " for object " << obj;
}
Runtime::Current()->GetMonitorList()->Add(m);
CHECK_EQ(obj->GetLockWord(true).GetState(), LockWord::kFatLocked);
} else {
LOG(WARNING) << "Monitor::Inflate: Install failed " << obj;
MonitorPool::ReleaseMonitor(self, m);
}
}
bool Monitor::InflateThinLocked(Thread* self,
Handle<mirror::Object> obj,
LockWord lock_word,
uint32_t hash_code, int attempt_of_4) {
DCHECK_EQ(lock_word.GetState(), LockWord::kThinLocked);
uint32_t owner_thread_id = lock_word.ThinLockOwner(); bool result = true; if (owner_thread_id == self->GetMonitorThreadId()) { // We own the monitor, we can easily inflate it.
Inflate(self, MonitorOwner::FromThread(self), obj.Get(), hash_code);
} else {
ThreadList* thread_list = Runtime::Current()->GetThreadList(); // Suspend the owner, inflate. First change to blocked and give up mutator_lock_.
self->SetMonitorEnterObject(obj.Get());
MonitorOwner owner;
Thread* owner_thread;
{
ScopedThreadSuspension sts(self, ThreadState::kWaitingForLockInflation);
ThreadSuspensionResult res = thread_list->SuspendPlatformOrVirtualThread(
owner_thread_id, SuspendReason::kInternal, &owner_thread, attempt_of_4); switch (res) { case ThreadSuspensionResult::kResultFailure: {
owner = MonitorOwner(); break;
} case ThreadSuspensionResult::kResultSuccessPlatform: {
DCHECK_NE(owner_thread, nullptr);
owner = MonitorOwner::FromPlatformThread(owner_thread); break;
} case ThreadSuspensionResult::kResultSuccessVirtual: {
owner = MonitorOwner::FromVirtualThreadId(owner_thread_id); break;
}
}
} if (!owner.IsNull()) { // We succeeded in suspending the thread, check the lock's status didn't change.
lock_word = obj->GetLockWord(true); if (lock_word.GetState() == LockWord::kThinLocked &&
lock_word.ThinLockOwner() == owner_thread_id) { // Go ahead and inflate the lock.
Inflate(self, owner, obj.Get(), hash_code);
} else { // Owner has changed; inform caller.
result = false;
} bool resumed = thread_list->ResumePlatformOrVirtualThread(
owner_thread_id, owner_thread, owner.IsVirtualThread(), SuspendReason::kInternal);
DCHECK(resumed);
}
self->SetMonitorEnterObject(nullptr);
} return result;
}
// Fool annotalysis into thinking that the lock on obj is acquired. static ObjPtr<mirror::Object> FakeLock(ObjPtr<mirror::Object> obj)
EXCLUSIVE_LOCK_FUNCTION(obj.Ptr()) NO_THREAD_SAFETY_ANALYSIS { return obj;
}
// Fool annotalysis into thinking that the lock on obj is release. static ObjPtr<mirror::Object> FakeUnlock(ObjPtr<mirror::Object> obj)
UNLOCK_FUNCTION(obj.Ptr()) NO_THREAD_SAFETY_ANALYSIS { return obj;
}
ObjPtr<mirror::Object> Monitor::MonitorEnter(Thread* self,
ObjPtr<mirror::Object> obj, bool trylock) NO_THREAD_SAFETY_ANALYSIS { // NO_THREAD_SAFETY_ANALYSIS for <monitor>->monitor_lock_, etc.
DCHECK(self != nullptr);
DCHECK(obj != nullptr);
self->AssertThreadSuspensionIsAllowable();
obj = FakeLock(obj);
uint32_t thread_id = self->GetMonitorThreadId();
size_t contention_count = 0; // Initial pure spin iterations before the GetMaxSpinsBeforeThinLockInflation() calls to // sched_yield().
constexpr size_t kExtraSpinIters = 100; int inflation_attempt = 1;
StackHandleScope<1> hs(self);
Handle<mirror::Object> h_obj(hs.NewHandle(obj)); while (true) { static constexpr int kMaxInflationAttempts = 100; // Really > 5 should essentially never // happen. if (UNLIKELY(inflation_attempt >= kMaxInflationAttempts)) {
LockWord lw = h_obj.Get()->GetLockWord(/*as_volatile=*/true);
std::ostringstream oss;
lw.Dump(oss);
uint32_t owner_thread_id = 0; if (lw.GetState() == LockWord::kThinLocked) {
owner_thread_id = lw.ThinLockOwner();
} if (owner_thread_id != 0) {
MutexLock mu(self, *Locks::thread_list_lock_);
ThreadList* thread_list = Runtime::Current()->GetThreadList();
Thread* t; if (kIsVirtualThreadEnabled &&
thread_list->IsVirtualThreadSuspendCountAllocated(owner_thread_id)) {
uint32_t carrier_id = thread_list->GetCarrierThreadIdByVirtualThreadId(owner_thread_id); if (carrier_id == ThreadList::kInvalidThreadId) {
oss << "; virtual thread carrier not found";
t = nullptr;
} else {
oss << "; carrier id: " << carrier_id;
t = thread_list->FindThreadByThreadId(carrier_id);
}
} else {
t = thread_list->FindThreadByThreadId(owner_thread_id);
}
if (t == nullptr) {
oss << "; owner not found!";
} else {
oss << "; owner: " << *t; if (kill(t->GetTid(), 0) == 0) {
oss << " (live)";
}
}
}
LOG(FATAL) << "Too many inflation attempts: " << oss.str();
} // We initially read the lockword with ordinary Java/relaxed semantics. When stronger // semantics are needed, we address it below. Since GetLockWord bottoms out to a relaxed load, // we can fix it later, in an infrequently executed case, with a fence.
LockWord lock_word = h_obj->GetLockWord(false); switch (lock_word.GetState()) { case LockWord::kUnlocked: { // No ordering required for preceding lockword read, since we retest.
LockWord thin_locked(LockWord::FromThinLockId(thread_id, 0, lock_word.GCState())); if (h_obj->CasLockWord(lock_word, thin_locked, CASMode::kWeak, std::memory_order_acquire)) {
AtraceMonitorLock(self, h_obj.Get(), /* is_wait= */ false); return h_obj.Get(); // Success!
} continue; // Go again.
} case LockWord::kThinLocked: {
uint32_t owner_thread_id = lock_word.ThinLockOwner(); if (owner_thread_id == thread_id) { // No ordering required for initial lockword read. // We own the lock, increase the recursion count.
uint32_t new_count = lock_word.ThinLockCount() + 1; if (LIKELY(new_count <= LockWord::kThinLockMaxCount)) {
LockWord thin_locked(LockWord::FromThinLockId(thread_id,
new_count,
lock_word.GCState())); // Only this thread pays attention to the count. Thus there is no need for stronger // than relaxed memory ordering. if (!gUseReadBarrier) {
h_obj->SetLockWord(thin_locked, /* as_volatile= */ false);
AtraceMonitorLock(self, h_obj.Get(), /* is_wait= */ false); return h_obj.Get(); // Success!
} else { // Use CAS to preserve the read barrier state. if (h_obj->CasLockWord(lock_word,
thin_locked,
CASMode::kWeak,
std::memory_order_relaxed)) {
AtraceMonitorLock(self, h_obj.Get(), /* is_wait= */ false); return h_obj.Get(); // Success!
}
} continue; // Go again.
} else { // We'd overflow the recursion count, so inflate the monitor. bool unchanged =
InflateThinLocked(self, h_obj, lock_word, 0, std::min(inflation_attempt++, 4));
DCHECK(unchanged); // We hold the lock, and thus it shouldn't change.
}
} else { if (trylock) { return nullptr;
} // Contention.
contention_count++;
Runtime* runtime = Runtime::Current(); if (contention_count
<= kExtraSpinIters + runtime->GetMaxSpinsBeforeThinLockInflation()) { // Use sched_yield instead of NanoSleep since NanoSleep can wait much longer // than the parameter you pass in. This can cause thread suspension to take excessively // long and make long pauses. See b/16307460. if (contention_count > kExtraSpinIters) {
self->CheckSuspend();
sched_yield();
}
} else {
contention_count = 0; // No ordering required for initial lockword read. Install rereads it anyway. if (!InflateThinLocked(self, h_obj, lock_word, 0, std::min(inflation_attempt++, 4))) { // The above can fail without timing out if e.g. the owner exits. If that happens on // the last attempt, we retry with attempt = 4. // If it returned false, the owner or lock status changed. Our inflation strategy // actually doesn't work well for a briefly held, frequently acquired lock, since // ownership is likely to change before we can suspend the owner. Try again to just // use the thin lock, and reset inflation_attempt, since this can happen many times.
inflation_attempt = 1;
}
}
} continue; // Start from the beginning.
} case LockWord::kFatLocked: { // We should have done an acquire read of the lockword initially, to ensure // visibility of the monitor data structure. Use an explicit fence instead.
std::atomic_thread_fence(std::memory_order_acquire);
Monitor* mon = lock_word.FatLockMonitor(); if (trylock) { return mon->TryLock(self) ? h_obj.Get() : nullptr;
} else {
mon->Lock(self);
DCHECK(mon->monitor_lock_.IsExclusiveHeld(self)); return h_obj.Get(); // Success!
}
} case LockWord::kHashCode: // Inflate with the existing hashcode. // Again no ordering required for initial lockword read, since we don't rely // on the visibility of any prior computation.
Inflate(self, MonitorOwner(), h_obj.Get(), lock_word.GetHashCode()); continue; // Start from the beginning. default: {
LOG(FATAL) << "Invalid monitor state " << lock_word.GetState();
UNREACHABLE();
}
}
}
}
bool Monitor::MonitorExit(Thread* self, ObjPtr<mirror::Object> obj) NO_THREAD_SAFETY_ANALYSIS { // NO_THREAD_SAFETY_ANALYSIS for <monitor>->monitor_lock_, etc.
DCHECK(self != nullptr);
DCHECK(obj != nullptr);
self->AssertThreadSuspensionIsAllowable();
obj = FakeUnlock(obj);
StackHandleScope<1> hs(self);
Handle<mirror::Object> h_obj(hs.NewHandle(obj)); while (true) {
LockWord lock_word = obj->GetLockWord(true); switch (lock_word.GetState()) { case LockWord::kHashCode: // Fall-through. case LockWord::kUnlocked:
FailedUnlock(h_obj.Get(), self, 0u, nullptr); returnfalse; // Failure. case LockWord::kThinLocked: {
uint32_t thread_id = self->GetMonitorThreadId();
uint32_t owner_thread_id = lock_word.ThinLockOwner(); if (owner_thread_id != thread_id) {
FailedUnlock(h_obj.Get(), self, owner_thread_id, nullptr); returnfalse; // Failure.
} else { // We own the lock, decrease the recursion count.
LockWord new_lw = LockWord::Default(); if (lock_word.ThinLockCount() != 0) {
uint32_t new_count = lock_word.ThinLockCount() - 1;
new_lw = LockWord::FromThinLockId(thread_id, new_count, lock_word.GCState());
} else {
new_lw = LockWord::FromDefault(lock_word.GCState());
} if (!gUseReadBarrier) {
DCHECK_EQ(new_lw.ReadBarrierState(), 0U); // TODO: This really only needs memory_order_release, but we currently have // no way to specify that. In fact there seem to be no legitimate uses of SetLockWord // with a final argument of true. This slows down x86 and ARMv7, but probably not v8.
h_obj->SetLockWord(new_lw, true);
AtraceMonitorUnlock(); // Success! returntrue;
} else { // Use CAS to preserve the read barrier state. if (h_obj->CasLockWord(lock_word, new_lw, CASMode::kWeak, std::memory_order_release)) {
AtraceMonitorUnlock(); // Success! returntrue;
}
} continue; // Go again.
}
} case LockWord::kFatLocked: {
Monitor* mon = lock_word.FatLockMonitor(); return mon->Unlock(self);
} default: {
LOG(FATAL) << "Invalid monitor state " << lock_word.GetState();
UNREACHABLE();
}
}
}
}
Runtime::Current()->GetRuntimeCallbacks()->ObjectWaitStart(h_obj, ms); if (UNLIKELY(self->ObserveAsyncException() || self->IsExceptionPending())) { // See b/65558434 for information on handling of exceptions here. return;
}
LockWord lock_word = h_obj->GetLockWord(true); while (lock_word.GetState() != LockWord::kFatLocked) { switch (lock_word.GetState()) { case LockWord::kHashCode: // Fall-through. case LockWord::kUnlocked:
ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()"); return; // Failure. case LockWord::kThinLocked: {
uint32_t thread_id = self->GetMonitorThreadId();
uint32_t owner_thread_id = lock_word.ThinLockOwner(); if (owner_thread_id != thread_id) {
ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()"); return; // Failure.
} else { // We own the lock, inflate to enqueue ourself on the Monitor. May fail spuriously so // re-load.
Inflate(self, MonitorOwner::FromThread(self), h_obj.Get(), 0);
lock_word = h_obj->GetLockWord(true);
} break;
} case LockWord::kFatLocked: // Unreachable given the loop condition above. Fall-through. default: {
LOG(FATAL) << "Invalid monitor state " << lock_word.GetState();
UNREACHABLE();
}
}
}
Monitor* mon = lock_word.FatLockMonitor();
mon->Wait(self, ms, ns, interruptShouldThrow, why);
}
void Monitor::DoNotify(Thread* self, ObjPtr<mirror::Object> obj, bool notify_all) {
DCHECK(self != nullptr);
DCHECK(obj != nullptr);
LockWord lock_word = obj->GetLockWord(true); switch (lock_word.GetState()) { case LockWord::kHashCode: // Fall-through. case LockWord::kUnlocked:
ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()"); return; // Failure. case LockWord::kThinLocked: {
uint32_t thread_id = self->GetMonitorThreadId();
uint32_t owner_thread_id = lock_word.ThinLockOwner(); if (owner_thread_id != thread_id) {
ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()"); return; // Failure.
} else { // We own the lock but there's no Monitor and therefore no waiters. return; // Success.
}
} case LockWord::kFatLocked: {
Monitor* mon = lock_word.FatLockMonitor(); if (notify_all) {
mon->NotifyAll(self);
} else {
mon->Notify(self);
} return; // Success.
} default: {
LOG(FATAL) << "Invalid monitor state " << lock_word.GetState();
UNREACHABLE();
}
}
}
uint32_t Monitor::GetLockOwnerThreadId(ObjPtr<mirror::Object> obj) {
DCHECK(obj != nullptr);
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
LockWord lock_word = obj->GetLockWord(true); switch (lock_word.GetState()) { case LockWord::kHashCode: // Fall-through. case LockWord::kUnlocked: return ThreadList::kInvalidThreadId; case LockWord::kThinLocked: return lock_word.ThinLockOwner(); case LockWord::kFatLocked: {
Monitor* mon = lock_word.FatLockMonitor(); // Since we hold a share of the mutator lock, the obj lock cannot be deflated here. // Since our caller holds a reference to obj, mon cannot be reclaimed. return mon->GetOwnerThreadId();
} default: {
LOG(FATAL) << "Unreachable";
UNREACHABLE();
}
}
}
bool Monitor::IsOwnedByMe(const Thread* self, ObjPtr<mirror::Object> obj) {
DCHECK_EQ(self, Thread::Current());
DCHECK(obj != nullptr);
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
LockWord lock_word = obj->GetLockWord(true); switch (lock_word.GetState()) { case LockWord::kHashCode: // Fall-through. case LockWord::kUnlocked: returnfalse; case LockWord::kThinLocked: return lock_word.ThinLockOwner() == self->GetMonitorThreadId(); case LockWord::kFatLocked: {
Monitor* mon = lock_word.FatLockMonitor(); // Since we hold a share of the mutator lock, the obj lock cannot be deflated here. // Since our caller holds a reference to obj, mon cannot be reclaimed. return mon->IsOwnedByMe(self);
} default: {
LOG(FATAL) << "Unreachable";
UNREACHABLE();
}
}
}
switch (state) { case ThreadState::kWaiting: case ThreadState::kTimedWaiting: case ThreadState::kSleeping:
{
Thread* self = Thread::Current();
MutexLock mu(self, *thread->GetWaitMutex());
Monitor* monitor = thread->GetWaitMonitor(); if (monitor != nullptr) {
*monitor_object = monitor->GetObject();
}
} break;
case ThreadState::kBlocked: case ThreadState::kWaitingForLockInflation:
{
ObjPtr<mirror::Object> lock_object = thread->GetMonitorEnterObject(); if (lock_object != nullptr) { if (gUseReadBarrier && Thread::Current()->GetIsGcMarking()) { // We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack // may have not been flipped yet and "pretty_object" may be a from-space (stale) ref, in // which case the GetLockOwnerThreadId() call below will crash. So explicitly mark/forward // it here.
lock_object = ReadBarrier::Mark(lock_object.Ptr());
}
*monitor_object = lock_object;
*lock_owner_thread_id = GetLockOwnerThreadId(lock_object);
}
} break;
default: break;
}
return state;
}
ObjPtr<mirror::Object> Monitor::GetContendedMonitor(Thread* thread) { // This is used to implement JDWP's ThreadReference.CurrentContendedMonitor, and has a bizarre // definition of contended that includes a monitor a thread is trying to enter...
ObjPtr<mirror::Object> result = thread->GetMonitorEnterObject(); if (result == nullptr) { // ...but also a monitor that the thread is waiting on.
MutexLock mu(Thread::Current(), *thread->GetWaitMutex());
Monitor* monitor = thread->GetWaitMonitor(); if (monitor != nullptr) {
result = monitor->GetObject();
}
} return result;
}
// Native methods are an easy special case. // TODO: use the JNI implementation's table of explicit MonitorEnter calls and dump those too. if (m->IsNative()) { if (m->IsSynchronized()) {
DCHECK(!m->IsCriticalNative());
DCHECK(!m->IsFastNative());
ObjPtr<mirror::Object> lock; if (m->IsStatic()) { // Static methods synchronize on the declaring class object.
lock = m->GetDeclaringClass();
} else { // Instance methods synchronize on the `this` object. // The `this` reference is stored in the first out vreg in the caller's frame.
uint8_t* sp = reinterpret_cast<uint8_t*>(stack_visitor->GetCurrentQuickFrame());
size_t frame_size = stack_visitor->GetCurrentQuickFrameInfo().FrameSizeInBytes();
lock = reinterpret_cast<StackReference<mirror::Object>*>(
sp + frame_size + static_cast<size_t>(kRuntimePointerSize))->AsMirrorPtr();
}
callback(lock, callback_context);
} return;
}
// Proxy methods should not be synchronized. if (m->IsProxyMethod()) {
CHECK(!m->IsSynchronized()); return;
}
// Is there any reason to believe there's any synchronization in this method?
CHECK(m->GetCodeItem() != nullptr) << m->PrettyMethod();
CodeItemDataAccessor accessor(m->DexInstructionData()); if (accessor.TriesSize() == 0) { return; // No "tries" implies no synchronization, so no held locks to report.
}
// Get the dex pc. If abort_on_failure is false, GetDexPc will not abort in the case it cannot // find the dex pc, and instead return kDexNoIndex. Then bail out, as it indicates we have an // inconsistent stack anyways.
uint32_t dex_pc = stack_visitor->GetDexPc(abort_on_failure); if (!abort_on_failure && dex_pc == dex::kDexNoIndex) {
LOG(ERROR) << "Could not find dex_pc for " << m->PrettyMethod(); return;
}
// Ask the verifier for the dex pcs of all the monitor-enter instructions corresponding to // the locks held in this stack frame.
std::vector<verifier::MethodVerifier::DexLockInfo> monitor_enter_dex_pcs;
verifier::MethodVerifier::FindLocksAtDexPc(m,
dex_pc,
&monitor_enter_dex_pcs,
Runtime::Current()->GetTargetSdkVersion()); for (verifier::MethodVerifier::DexLockInfo& dex_lock_info : monitor_enter_dex_pcs) { // As a debug check, check that dex PC corresponds to a monitor-enter. if (kIsDebugBuild) { const Instruction& monitor_enter_instruction = accessor.InstructionAt(dex_lock_info.dex_pc);
CHECK_EQ(monitor_enter_instruction.Opcode(), Instruction::MONITOR_ENTER)
<< "expected monitor-enter @" << dex_lock_info.dex_pc << "; was "
<< reinterpret_cast<constvoid*>(&monitor_enter_instruction);
}
// Iterate through the set of dex registers, as the compiler may not have held all of them // live. bool success = false; for (uint32_t dex_reg : dex_lock_info.dex_registers) {
uint32_t value;
// For optimized code we expect the DexRegisterMap to be present - monitor information // not be optimized out.
success = stack_visitor->GetVReg(m, dex_reg, kReferenceVReg, &value); if (success) {
mirror::Object* mp = reinterpret_cast<mirror::Object*>(value); // TODO(b/299577730) Remove the extra checks here once the underlying bug is fixed. const gc::Verification* v = Runtime::Current()->GetHeap()->GetVerification(); if (v->IsValidObject(mp)) {
ObjPtr<mirror::Object> o = mp;
callback(o, callback_context); break;
} else {
LOG(ERROR) << "Encountered bad lock object: " << std::hex << value << std::dec;
success = false;
}
}
} if (!success) {
LOG(ERROR) << "Failed to find/read reference for monitor-enter at dex pc "
<< dex_lock_info.dex_pc << " in method " << m->PrettyMethod(); if (kIsDebugBuild) { // Crash only in debug ART builds.
LOG(FATAL) << "Had a lock reported for a dex pc " "but was not able to fetch a corresponding object!";
} else {
LOG(ERROR) << "Held monitor information in stack trace will be incomplete!";
}
}
}
}
bool Monitor::IsValidLockWord(LockWord lock_word) { switch (lock_word.GetState()) { case LockWord::kUnlocked: // Nothing to check. returntrue; case LockWord::kThinLocked: // Basic consistency check of owner. return lock_word.ThinLockOwner() != ThreadList::kInvalidThreadId; case LockWord::kFatLocked: { // Check the monitor appears in the monitor list.
Monitor* mon = lock_word.FatLockMonitor();
MonitorList* list = Runtime::Current()->GetMonitorList();
MutexLock mu(Thread::Current(), list->monitor_list_lock_); for (Monitor* list_mon : list->list_) { if (mon == list_mon) { returntrue; // Found our monitor.
}
} returnfalse; // Fail - unowned monitor in an object.
} case LockWord::kHashCode: returntrue; default:
LOG(FATAL) << "Unreachable";
UNREACHABLE();
}
}
MonitorList::~MonitorList() {
Thread* self = Thread::Current();
MutexLock mu(self, monitor_list_lock_); // Release all monitors to the pool. // TODO: Is it an invariant that *all* open monitors are in the list? Then we could // clear faster in the pool.
MonitorPool::ReleaseMonitors(self, &list_);
}
void MonitorList::Add(Monitor* m) {
Thread* self = Thread::Current();
MutexLock mu(self, monitor_list_lock_); // CMS needs this to block for concurrent reference processing because an object allocated during // the GC won't be marked and concurrent reference processing would incorrectly clear the JNI weak // ref. But CC (gUseReadBarrier == true) doesn't because of the to-space invariant. while (!gUseReadBarrier && UNLIKELY(!allow_new_monitors_)) { // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the // presence of threads blocking for weak ref access.
self->CheckEmptyCheckpointFromWeakRefAccess(&monitor_list_lock_);
monitor_add_condition_.WaitHoldingLocks(self);
}
list_.push_front(m);
}
void MonitorList::SweepMonitorList(IsMarkedVisitor* visitor) {
Thread* self = Thread::Current();
MutexLock mu(self, monitor_list_lock_); for (auto it = list_.begin(); it != list_.end(); ) {
Monitor* m = *it; // Disable the read barrier in GetObject() as this is called by GC.
ObjPtr<mirror::Object> obj = m->GetObject<kWithoutReadBarrier>(); // The object of a monitor can be null if we have deflated it.
ObjPtr<mirror::Object> new_obj = obj != nullptr ? visitor->IsMarked(obj.Ptr()) : nullptr; if (new_obj == nullptr) {
VLOG(monitor) << "freeing monitor " << m << " belonging to unmarked object "
<< obj;
MonitorPool::ReleaseMonitor(self, m);
it = list_.erase(it);
} else {
m->SetObject(new_obj);
++it;
}
}
}
class MonitorDeflateVisitor : public IsMarkedVisitor { public:
MonitorDeflateVisitor()
: self_(Thread::Current()), deflate_count_(0), heap_(Runtime::Current()->GetHeap()) {}
mirror::Object* IsMarked(mirror::Object* object) override REQUIRES(Locks::mutator_lock_) { // Avoid deflating monitors in zygote/image spaces because that could // end up dirtying otherwise shared/clean memory. if (heap_->IsInZygoteSpace(object) || heap_->ObjectIsInBootImageSpace(object)) { return object; // Monitor was not deflated.
}
if (Monitor::Deflate(self_, object)) {
DCHECK_NE(object->GetLockWord(true).GetState(), LockWord::kFatLocked);
++deflate_count_; // If we deflated, return null so that the monitor gets removed from the array. return nullptr;
} return object; // Monitor was not deflated.
}
MonitorInfo::MonitorInfo(ObjPtr<mirror::Object> obj) : owner_(), entry_count_(0) {
DCHECK(obj != nullptr);
LockWord lock_word = obj->GetLockWord(true); switch (lock_word.GetState()) { case LockWord::kUnlocked: // Fall-through. case LockWord::kForwardingAddress: // Fall-through. case LockWord::kHashCode: break; case LockWord::kThinLocked: {
ThreadList* thread_list = Runtime::Current()->GetThreadList();
uint32_t owner_thread_id = lock_word.ThinLockOwner();
DCHECK_NE(owner_thread_id, ThreadList::kInvalidThreadId) << "Thin-locked without owner!"; if (kIsVirtualThreadEnabled &&
thread_list->IsVirtualThreadSuspendCountAllocated(owner_thread_id)) {
owner_ = MonitorOwner::FromVirtualThreadId(owner_thread_id);
} else {
Thread* thread = thread_list->FindThreadByThreadId(owner_thread_id);
DCHECK_NE(thread, nullptr) << "Thin-locked without owner!";
owner_ = MonitorOwner::FromPlatformThread(thread);
}
entry_count_ = 1 + lock_word.ThinLockCount(); // Thin locks have no waiters. break;
} case LockWord::kFatLocked: {
Monitor* mon = lock_word.FatLockMonitor();
owner_ = mon->owner_.load(std::memory_order_relaxed); // Here it is okay for the owner to be null since we don't reset the LockWord back to // kUnlocked until we get a GC. In cases where this hasn't happened yet we will have a fat // lock without an owner. // Neither owner_ nor entry_count_ is touched by threads in "suspended" state, so // we must see consistent values. if (owner_ != nullptr) {
entry_count_ = 1 + mon->lock_count_;
} else {
DCHECK_EQ(mon->lock_count_, 0u) << "Monitor is fat-locked without any owner!";
} for (Thread* waiter = mon->wait_set_; waiter != nullptr; waiter = waiter->GetWaitNext()) {
waiters_.push_back(waiter);
} break;
}
}
}
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.