Bug 1392235, part 2 - Fix AutoReferenceChainGuard to never set/unset state when it breaks a reference chain. r=longsonr

MozReview-Commit-ID: I4y8eNGU0Er
This commit is contained in:
Jonathan Watt 2017-08-22 11:43:15 +01:00
parent 37ee4415b8
commit 45e8436173

View File

@ -82,22 +82,23 @@ public:
MOZ_GUARD_OBJECT_NOTIFIER_PARAM) MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mFrame(aFrame) : mFrame(aFrame)
, mFrameInUse(aFrameInUse) , mFrameInUse(aFrameInUse)
, mChainCounter(aChainCounter)
, mMaxChainLength(aMaxChainLength) , mMaxChainLength(aMaxChainLength)
, mBrokeReference(false)
{ {
MOZ_GUARD_OBJECT_NOTIFIER_INIT; MOZ_GUARD_OBJECT_NOTIFIER_INIT;
MOZ_ASSERT(aFrame && aFrameInUse && aChainCounter); MOZ_ASSERT(aFrame && aFrameInUse && aChainCounter);
MOZ_ASSERT(aMaxChainLength > 0); MOZ_ASSERT(aMaxChainLength > 0);
MOZ_ASSERT(*aChainCounter == noChain || MOZ_ASSERT(*aChainCounter == noChain ||
(*aChainCounter >= 0 && *aChainCounter < aMaxChainLength)); (*aChainCounter >= 0 && *aChainCounter < aMaxChainLength));
if (*aChainCounter == noChain) {
// Initialize - we start at aMaxChainLength and decrement towards zero.
*aChainCounter = aMaxChainLength;
}
mChainCounter = aChainCounter;
} }
~AutoReferenceChainGuard() { ~AutoReferenceChainGuard() {
if (mBrokeReference) {
// We didn't change mFrameInUse or mChainCounter
return;
}
*mFrameInUse = false; *mFrameInUse = false;
// If we fail this assert then there were more destructor calls than // If we fail this assert then there were more destructor calls than
@ -121,22 +122,30 @@ public:
*/ */
MOZ_MUST_USE bool Reference() { MOZ_MUST_USE bool Reference() {
if (MOZ_UNLIKELY(*mFrameInUse)) { if (MOZ_UNLIKELY(*mFrameInUse)) {
mBrokeReference = true;
ReportErrorToConsole(); ReportErrorToConsole();
return false; return false;
} }
if (*mChainCounter == noChain) {
// Initialize - we start at aMaxChainLength and decrement towards zero.
*mChainCounter = mMaxChainLength;
} else {
// If we fail this assertion then either a consumer failed to break a
// reference loop/chain, or else they called Reference() more than once
MOZ_ASSERT(*mChainCounter >= 0);
if (MOZ_UNLIKELY(*mChainCounter < 1)) {
mBrokeReference = true;
ReportErrorToConsole();
return false;
}
}
// We only set these once we know we're returing true.
*mFrameInUse = true; *mFrameInUse = true;
// If we fail this assertion then either a consumer failed to break a
// reference loop/chain, or else they called Reference() more than once
MOZ_ASSERT(*mChainCounter >= 0);
(*mChainCounter)--; (*mChainCounter)--;
if (MOZ_UNLIKELY(*mChainCounter < 0)) {
ReportErrorToConsole();
return false;
}
return true; return true;
} }
@ -157,6 +166,7 @@ private:
bool* mFrameInUse; bool* mFrameInUse;
int16_t* mChainCounter; int16_t* mChainCounter;
const int16_t mMaxChainLength; const int16_t mMaxChainLength;
bool mBrokeReference;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
}; };