/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include"mozilla/BlockingResourceBase.h"
#ifdef DEBUG # include "prthread.h"
# ifndef MOZ_CALLSTACK_DISABLED # include "CodeAddressService.h" # include "nsHashKeys.h" # include "mozilla/StackWalk.h" # include "nsTHashtable.h" # endif
# include "mozilla/Attributes.h" # include "mozilla/CondVar.h" # include "mozilla/DeadlockDetector.h" # include "mozilla/RecursiveMutex.h" # include "mozilla/ReentrantMonitor.h" # include "mozilla/Mutex.h" # include "mozilla/RWLock.h" # include "mozilla/UniquePtr.h"
# ifdefined(MOZILLA_INTERNAL_API) # include "mozilla/ProfilerThreadSleep.h" # endif // MOZILLA_INTERNAL_API
#endif// ifdef DEBUG
namespace mozilla { // // BlockingResourceBase implementation //
// static members constchar* const BlockingResourceBase::kResourceTypeName[] = { // needs to be kept in sync with BlockingResourceType "Mutex", "ReentrantMonitor", "CondVar", "RecursiveMutex"};
void BlockingResourceBase::GetStackTrace(AcquisitionState& aState, constvoid* aFirstFramePC) { # ifndef MOZ_CALLSTACK_DISABLED // Clear the array...
aState.reset(); // ...and create a new one; this also puts the state to 'acquired' status // regardless of whether we obtain a stack trace or not.
aState.emplace();
/** * PrintCycle * Append to |aOut| detailed information about the circular * dependency in |aCycle|. Returns true if it *appears* that this * cycle may represent an imminent deadlock, but this is merely a * heuristic; the value returned may be a false positive or false * negative. * * *NOT* thread safe. Calls |Print()|. * * FIXME bug 456272 hack alert: because we can't write call * contexts into strings, all info is written to stderr, but only * some info is written into |aOut|
*/ staticbool PrintCycle( const BlockingResourceBase::DDT::ResourceAcquisitionArray& aCycle,
nsACString& aOut) {
NS_ASSERTION(aCycle.Length() > 1, "need > 1 element for cycle!");
const BlockingResourceBase::DDT::ResourceAcquisitionArray::value_type res =
aCycle.ElementAt(0);
maybeImminent &= res->Print(aOut);
BlockingResourceBase::DDT::ResourceAcquisitionArray::index_type i;
BlockingResourceBase::DDT::ResourceAcquisitionArray::size_type len =
aCycle.Length(); const BlockingResourceBase::DDT::ResourceAcquisitionArray::value_type* it =
1 + aCycle.Elements(); for (i = 1; i < len - 1; ++i, ++it) {
fputs("\n--- Next dependency:\n", stderr);
aOut += "\nNext dependency:\n";
BlockingResourceBase::BlockingResourceBase( constchar* aName, BlockingResourceBase::BlockingResourceType aType)
: mName(aName),
mType(aType) # ifdef MOZ_CALLSTACK_DISABLED
,
mAcquired(false) # else
,
mAcquired() # endif
{
MOZ_ASSERT(mName, "Name must be nonnull"); // PR_CallOnce guaranatees that InitStatics is called in a // thread-safe way if (PR_SUCCESS != PR_CallOnce(&sCallOnce, InitStatics)) {
MOZ_CRASH("can't initialize blocking resource static members");
}
mChainPrev = 0;
sDeadlockDetector->Add(this);
}
BlockingResourceBase::~BlockingResourceBase() { // we don't check for really obviously bad things like freeing // Mutexes while they're still locked. it is assumed that the // base class, or its underlying primitive, will check for such // stupid mistakes.
mChainPrev = 0; // racy only for stupidly buggy client code if (sDeadlockDetector) {
sDeadlockDetector->Remove(this);
}
}
void BlockingResourceBase::AssertSafeToProcessEventLoop() { for (BlockingResourceBase* res = ResourceChainFront(); res != nullptr;
res = res->mChainPrev) { // It's more OK to hold reentrant/recursive types across nested event loops, // as it should hopefully not lead to deadlocks, so allow them for now. if (res->mType == eReentrantMonitor || res->mType == eRecursiveMutex) { continue;
}
// Allow specifically named mutexes to be held across a nested event loop, // in order to get this check landed.
nsDependentCString name(res->mName); if (name != "GraphRunner::mMonitor"_ns || // Bug 1928770
name != "SourceSurfaceSkia::mChangeMutex"_ns) { // Bug 1928772 continue;
}
// This is a potential deadlock, as ProcessNextEvent could process any // runnable on the current thread, including one which could attempt to // acquire the mutex.
fputs( "###!!! ERROR: Potential deadlock detected:\n" "=== Holding blocking resource across call to ProcessNextEvent\n",
stderr);
nsAutoCString out( "Potential deadlock detected:\n" "Holding blocking resource across call to ProcessNextEvent\n");
res->Print(out);
NS_ERROR(out.get());
MOZ_CRASH_UNSAFE_PRINTF("Holding '%s' across call to ProcessNextEvent",
res->mName);
}
}
if (maybeImminent) {
fputs("\n###!!! Deadlock may happen NOW!\n\n", stderr);
out.AppendLiteral("\n###!!! Deadlock may happen NOW!\n\n");
} else {
fputs("\nDeadlock may happen for some other execution\n\n", stderr);
out.AppendLiteral("\nDeadlock may happen for some other execution\n\n");
}
// Only error out if we think a deadlock is imminent. if (maybeImminent) {
NS_ERROR(out.get());
} else {
NS_WARNING(out.get());
}
}
void RWLock::ReadLock() { // All we want to ensure here is that we're not attempting to acquire the // read lock while this thread is holding the write lock.
CheckAcquire();
this->detail::RWLockImpl::readLock();
MOZ_ASSERT(mOwningThread == nullptr);
}
// the code below implements monitor reentrancy semantics
if (this == chainFront) { // immediately re-entered the monitor: acceptable
PR_EnterMonitor(mReentrantMonitor);
++mEntryCount; return;
}
// this is sort of a hack around not recording the thread that // owns this monitor if (chainFront) { for (BlockingResourceBase* br = ResourceChainPrev(chainFront); br;
br = ResourceChainPrev(br)) { if (br == this) {
NS_WARNING( "Re-entering ReentrantMonitor after acquiring other resources.");
// show the caller why this is potentially bad
CheckAcquire();
// save monitor state and reset it to empty
int32_t savedEntryCount = mEntryCount;
AcquisitionState savedAcquisitionState = TakeAcquisitionState();
BlockingResourceBase* savedChainPrev = mChainPrev;
mEntryCount = 0;
mChainPrev = 0;
nsresult rv;
{ # ifdefined(MOZILLA_INTERNAL_API)
AUTO_PROFILER_THREAD_SLEEP; # endif // give up the monitor until we're back from Wait()
rv = PR_Wait(mReentrantMonitor, aInterval) == PR_SUCCESS ? NS_OK
: NS_ERROR_FAILURE;
}
// the code below implements mutex reentrancy semantics
if (this == chainFront) { // immediately re-entered the mutex: acceptable
LockInternal();
++mEntryCount; return;
}
// this is sort of a hack around not recording the thread that // owns this monitor if (chainFront) { for (BlockingResourceBase* br = ResourceChainPrev(chainFront); br;
br = ResourceChainPrev(br)) { if (br == this) {
NS_WARNING( "Re-entering RecursiveMutex after acquiring other resources.");
// show the caller why this is potentially bad
CheckAcquire();
LockInternal();
++mEntryCount; return;
}
}
}
CheckAcquire();
LockInternal();
NS_ASSERTION(mEntryCount == 0, "RecursiveMutex isn't free!");
Acquire(); // protected by us
mOwningThread = PR_GetCurrentThread();
mEntryCount = 1;
}
void RecursiveMutex::Unlock() { if (--mEntryCount == 0) {
Release(); // protected by us
mOwningThread = nullptr;
}
UnlockInternal();
}
// // Debug implementation of CondVar void OffTheBooksCondVar::Wait() { // Forward to the timed version of OffTheBooksCondVar::Wait to avoid code // duplication.
CVStatus status = Wait(TimeDuration::Forever());
MOZ_ASSERT(status == CVStatus::NoTimeout);
}
// save mutex state and reset to empty
AcquisitionState savedAcquisitionState = mLock->TakeAcquisitionState();
BlockingResourceBase* savedChainPrev = mLock->mChainPrev;
PRThread* savedOwningThread = mLock->mOwningThread;
mLock->mChainPrev = 0;
mLock->mOwningThread = nullptr;
// give up mutex until we're back from Wait()
CVStatus status;
{ # ifdefined(MOZILLA_INTERNAL_API)
AUTO_PROFILER_THREAD_SLEEP; # endif
status = mImpl.wait_for(*mLock, aDuration);
}
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 ist noch experimentell.