diff options
author | Pale Moon <git-repo@palemoon.org> | 2016-03-31 02:30:31 +0200 |
---|---|---|
committer | Pale Moon <git-repo@palemoon.org> | 2016-03-31 02:30:31 +0200 |
commit | 0d7c146acd32e6f4b313e45e599ccbb602b5b012 (patch) | |
tree | 06462c9c077822b3d48d8373cde79bc9fe1ed5ca /xpcom | |
parent | 5c90e4393ec4238a1519f92c36c74120727c0ccf (diff) | |
download | palemoon-gre-0d7c146acd32e6f4b313e45e599ccbb602b5b012.tar.gz |
Revert "Make refcounting of Cycle-Collectable objects faster and more straightforward by removing indirect refcounting."
This reverts commit ebcf789fcd6ec36d38a27cdeeb0427d9cc5c84b2.
Diffstat (limited to 'xpcom')
-rw-r--r-- | xpcom/base/CycleCollectedJSRuntime.cpp | 2 | ||||
-rw-r--r-- | xpcom/base/CycleCollectedJSRuntime.h | 4 | ||||
-rw-r--r-- | xpcom/base/nsAgg.h | 18 | ||||
-rw-r--r-- | xpcom/base/nsCycleCollector.cpp | 233 | ||||
-rw-r--r-- | xpcom/base/nsCycleCollector.h | 2 | ||||
-rw-r--r-- | xpcom/build/FrozenFunctions.cpp | 8 | ||||
-rw-r--r-- | xpcom/build/nsXPCOM.h | 7 | ||||
-rw-r--r-- | xpcom/build/nsXPCOMPrivate.h | 8 | ||||
-rw-r--r-- | xpcom/glue/nsCycleCollectionParticipant.h | 43 | ||||
-rw-r--r-- | xpcom/glue/nsISupportsImpl.h | 232 | ||||
-rw-r--r-- | xpcom/glue/standalone/nsXPCOMGlue.cpp | 9 |
11 files changed, 224 insertions, 342 deletions
diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index 1ae9c541f..e18e7aa34 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -252,7 +252,7 @@ public: { return NS_OK; } - static NS_METHOD_(void) DeleteCycleCollectableImpl(void *n) + static NS_METHOD_(void) UnmarkIfPurpleImpl(void *n) { } static NS_METHOD TraverseImpl(JSContextParticipant *that, void *n, diff --git a/xpcom/base/CycleCollectedJSRuntime.h b/xpcom/base/CycleCollectedJSRuntime.h index 191eef79b..a53a7ce02 100644 --- a/xpcom/base/CycleCollectedJSRuntime.h +++ b/xpcom/base/CycleCollectedJSRuntime.h @@ -37,7 +37,7 @@ public: return NS_OK; } - static NS_METHOD_(void) DeleteCycleCollectableImpl(void *n) + static NS_METHOD_(void) UnmarkIfPurpleImpl(void *n) { } @@ -64,7 +64,7 @@ public: return NS_OK; } - static NS_METHOD_(void) DeleteCycleCollectableImpl(void *n) + static NS_METHOD_(void) UnmarkIfPurpleImpl(void *n) { } diff --git a/xpcom/base/nsAgg.h b/xpcom/base/nsAgg.h index c37bfe687..cf7564e20 100644 --- a/xpcom/base/nsAgg.h +++ b/xpcom/base/nsAgg.h @@ -76,10 +76,9 @@ public: \ static NS_METHOD TraverseImpl(NS_CYCLE_COLLECTION_INNERCLASS *that, \ void *p, \ nsCycleCollectionTraversalCallback &cb); \ - static NS_METHOD_(void) DeleteCycleCollectableImpl(void* p) \ + static NS_METHOD_(void) UnmarkIfPurpleImpl(void *p) \ { \ - NS_CYCLE_COLLECTION_CLASSNAME(_class):: \ - Downcast(static_cast<nsISupports*>(p))->DeleteCycleCollectable(); \ + Downcast(static_cast<nsISupports *>(p))->UnmarkIfPurple(); \ } \ static _class* Downcast(nsISupports* s) \ { \ @@ -150,10 +149,11 @@ _class::Internal::AddRef(void) \ MOZ_ASSERT(int32_t(agg->mRefCnt) >= 0, "illegal refcnt"); \ NS_CheckThreadSafe(agg->_mOwningThread.GetThread(), \ #_class " not thread-safe"); \ - nsrefcnt count = agg->mRefCnt.incr(); \ + nsrefcnt count = agg->mRefCnt.incr(this); \ NS_LOG_ADDREF(this, count, #_class, sizeof(*agg)); \ return count; \ } \ + \ NS_IMETHODIMP_(nsrefcnt) \ _class::Internal::Release(void) \ { \ @@ -163,12 +163,12 @@ _class::Internal::Release(void) \ #_class " not thread-safe"); \ nsrefcnt count = agg->mRefCnt.decr(this); \ NS_LOG_RELEASE(this, count, #_class); \ + if (count == 0) { \ + agg->mRefCnt.stabilizeForDeletion(); \ + delete agg; \ + return 0; \ + } \ return count; \ -} \ -NS_IMETHODIMP_(void) \ -_class::DeleteCycleCollectable(void) \ -{ \ - delete this; \ } #define NS_IMPL_AGGREGATED_HELPER(_class) \ diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index ca4c28a11..0039f2a0d 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -23,9 +23,6 @@ // unlinking them they will self-destruct (since a garbage cycle is // only keeping itself alive with internal links, by definition). // -// Snow-white is an addition to the original algorithm. Snow-white object -// has reference count zero and is just waiting for deletion. -// // Grey nodes are being scanned. Nodes that turn grey will turn // either black if we determine that they're live, or white if we // determine that they're a garbage cycle. After the main collection @@ -62,6 +59,9 @@ // An object is purple-safe if it satisfies the following properties: // // - The object is scan-safe. +// - If the object calls |nsCycleCollector::suspect(this)|, +// it will null out the pointer from the purple buffer entry to +// the object before being destroyed. // // When we receive a pointer |ptr| via // |nsCycleCollector::suspect(ptr)|, we assume it is purple-safe. We @@ -84,7 +84,7 @@ // there should be no objects released from the scan-safe set during // the scan. // -// We *do* call |Root| and |Unroot| on every white object, on +// We *do* call |AddRef| and |Release| on every white object, on // either side of the calls to |Unlink|. This keeps the set of white // objects alive during the unlinking. // @@ -762,11 +762,12 @@ public: void Visit(nsPurpleBuffer &aBuffer, nsPurpleBufferEntry *aEntry) { - if (aEntry->mRefCnt) { - aEntry->mRefCnt->RemoveFromPurpleBuffer(); - aEntry->mRefCnt = nullptr; + if (aEntry->mObject) { + void *obj = aEntry->mObject; + nsCycleCollectionParticipant *cp = aEntry->mParticipant; + CanonicalizeParticipant(&obj, &cp); + cp->UnmarkIfPurple(obj); } - aEntry->mObject = nullptr; --aBuffer.mCount; } }; @@ -780,14 +781,13 @@ public: void SelectPointers(GCGraphBuilder &builder); // RemoveSkippable removes entries from the purple buffer if - // nsPurpleBufferEntry::mRefCnt is 0 or if the object's + // nsPurpleBufferEntry::mObject is null or if the object's // nsXPCOMCycleCollectionParticipant::CanSkip() returns true or - // if nsPurpleBufferEntry::mRefCnt->IsPurple() is false. + // if nsPurpleBufferEntry::mNotPurple is true. // If removeChildlessNodes is true, then any nodes in the purple buffer // that will have no children in the cycle collector graph will also be // removed. CanSkip() may be run on these children. - void RemoveSkippable(bool removeChildlessNodes, - CC_ForgetSkippableCallback aCb); + void RemoveSkippable(bool removeChildlessNodes); nsPurpleBufferEntry* NewEntry() { @@ -806,26 +806,24 @@ public: return e; } - void Put(void *p, nsCycleCollectionParticipant *cp, - nsCycleCollectingAutoRefCnt *aRefCnt) + nsPurpleBufferEntry* Put(void *p, nsCycleCollectionParticipant *cp) { nsPurpleBufferEntry *e = NewEntry(); ++mCount; e->mObject = p; - e->mRefCnt = aRefCnt; e->mParticipant = cp; + e->mNotPurple = false; + + // Caller is responsible for filling in result's mRefCnt. + return e; } void Remove(nsPurpleBufferEntry *e) { MOZ_ASSERT(mCount != 0, "must have entries"); - if (e->mRefCnt) { - e->mRefCnt->RemoveFromPurpleBuffer(); - e->mRefCnt = nullptr; - } e->mNextInFreeList = (nsPurpleBufferEntry*)(uintptr_t(mFreeList) | uintptr_t(1)); mFreeList = e; @@ -871,11 +869,14 @@ struct SelectPointersVisitor void Visit(nsPurpleBuffer &aBuffer, nsPurpleBufferEntry *aEntry) { - MOZ_ASSERT(!(aEntry->mObject && !aEntry->mRefCnt->get()), - "SelectPointersVisitor: snow-white object in the purple buffer"); - if (!aEntry->mObject || - !aEntry->mRefCnt->IsPurple() || - AddPurpleRoot(mBuilder, aEntry->mObject, aEntry->mParticipant)) { + if (aEntry->mObject && aEntry->mNotPurple) { + void* o = aEntry->mObject; + nsCycleCollectionParticipant* cp = aEntry->mParticipant; + CanonicalizeParticipant(&o, &cp); + cp->UnmarkIfPurple(o); + aBuffer.Remove(aEntry); + } else if (!aEntry->mObject || + AddPurpleRoot(mBuilder, aEntry->mObject, aEntry->mParticipant)) { aBuffer.Remove(aEntry); } } @@ -1049,8 +1050,7 @@ public: nsresult Init(); void ShutdownThreads(); - void Suspect(void *n, nsCycleCollectionParticipant *cp, - nsCycleCollectingAutoRefCnt *aRefCnt); + nsPurpleBufferEntry* Suspect(void *n, nsCycleCollectionParticipant *cp); void CheckThreadSafety(); @@ -1073,12 +1073,6 @@ public: bool BeginCollection(ccType aCCType, nsICycleCollectorListener *aListener); bool FinishCollection(nsICycleCollectorListener *aListener); - void FreeSnowWhite(bool aUntilNoSWInPurpleBuffer); - - // If there is a cycle collector available in the current thread, - // this calls FreeSnowWhite(false). - static void TryToFreeSnowWhite(); - uint32_t SuspectedCount(); void Shutdown(); @@ -1166,8 +1160,6 @@ nsCycleCollectorRunner::Collect(ccType aCCType, if (!mCollector->PrepareForCollection(aResults, &whiteNodes)) return; - mCollector->FreeSnowWhite(true); - MOZ_ASSERT(!mListener, "Should have cleared this already!"); if (aListener && NS_FAILED(aListener->Begin())) aListener = nullptr; @@ -2081,6 +2073,8 @@ AddPurpleRoot(GCGraphBuilder &builder, void *root, nsCycleCollectionParticipant } } + cp->UnmarkIfPurple(root); + return true; } @@ -2145,144 +2139,38 @@ MayHaveChild(void *o, nsCycleCollectionParticipant* cp) return cf.MayHaveChild(); } -struct SnowWhiteObject -{ - void* mPointer; - nsCycleCollectionParticipant* mParticipant; - nsCycleCollectingAutoRefCnt* mRefCnt; -}; - -class SnowWhiteKiller +class RemoveSkippableVisitor { public: - SnowWhiteKiller(uint32_t aMaxCount) - { - mObjects.SetCapacity(aMaxCount); - } - - ~SnowWhiteKiller() - { - for (uint32_t i = 0; i < mObjects.Length(); ++i) { - SnowWhiteObject& o = mObjects[i]; - if (!o.mRefCnt->get() && !o.mRefCnt->IsInPurpleBuffer()) { - o.mRefCnt->stabilizeForDeletion(); - o.mParticipant->DeleteCycleCollectable(o.mPointer); - } - } - } - - void - Visit(nsPurpleBuffer& aBuffer, nsPurpleBufferEntry* aEntry) - { - if (aEntry->mObject && !aEntry->mRefCnt->get()) { - void *o = aEntry->mObject; - nsCycleCollectionParticipant *cp = aEntry->mParticipant; - CanonicalizeParticipant(&o, &cp); - SnowWhiteObject swo = { o, cp, aEntry->mRefCnt }; - mObjects.AppendElement(swo); - aBuffer.Remove(aEntry); - } - } - - bool HasSnowWhiteObjects() - { - return mObjects.Length() > 0; - } -private: - nsTArray<SnowWhiteObject> mObjects; -}; - -class RemoveSkippableVisitor : public SnowWhiteKiller -{ -public: - RemoveSkippableVisitor(uint32_t aMaxCount, bool aRemoveChildlessNodes, - CC_ForgetSkippableCallback aCb) - : SnowWhiteKiller(aMaxCount), - mRemoveChildlessNodes(aRemoveChildlessNodes), - mCallback(aCb) + RemoveSkippableVisitor(bool aRemoveChildlessNodes) + : mRemoveChildlessNodes(aRemoveChildlessNodes) {} - ~RemoveSkippableVisitor() - { - // Note, we must call the callback before SnowWhiteKiller calls - // DeleteCycleCollectable! - if (mCallback) { - mCallback(); - } - } - void Visit(nsPurpleBuffer &aBuffer, nsPurpleBufferEntry *aEntry) { if (aEntry->mObject) { - if (!aEntry->mRefCnt->get()) { - SnowWhiteKiller::Visit(aBuffer, aEntry); - return; - } void *o = aEntry->mObject; nsCycleCollectionParticipant *cp = aEntry->mParticipant; CanonicalizeParticipant(&o, &cp); - if (aEntry->mRefCnt->IsPurple() && !cp->CanSkip(o, false) && + if (!aEntry->mNotPurple && !cp->CanSkip(o, false) && (!mRemoveChildlessNodes || MayHaveChild(o, cp))) { return; } + cp->UnmarkIfPurple(o); } aBuffer.Remove(aEntry); } private: bool mRemoveChildlessNodes; - CC_ForgetSkippableCallback mCallback; }; void -nsPurpleBuffer::RemoveSkippable(bool removeChildlessNodes, - CC_ForgetSkippableCallback aCb) +nsPurpleBuffer::RemoveSkippable(bool removeChildlessNodes) { - RemoveSkippableVisitor visitor(Count(), removeChildlessNodes, aCb); + RemoveSkippableVisitor visitor(removeChildlessNodes); VisitEntries(visitor); - // If we're about to delete some objects when visitor goes out of scope, - // try to delete some more soon. - if (visitor.HasSnowWhiteObjects()) { - nsCycleCollector_dispatchDeferredDeletion(); - } -} - -class AsyncFreeSnowWhite : public nsRunnable -{ -public: - NS_IMETHOD Run() - { - nsCycleCollector::TryToFreeSnowWhite(); - return NS_OK; - } - - static void Dispatch() - { - nsRefPtr<AsyncFreeSnowWhite> ev = new AsyncFreeSnowWhite(); - NS_DispatchToCurrentThread(ev); - } -}; - -void -nsCycleCollector::FreeSnowWhite(bool aUntilNoSWInPurpleBuffer) -{ - do { - SnowWhiteKiller visitor(mPurpleBuf.Count()); - mPurpleBuf.VisitEntries(visitor); - if (!visitor.HasSnowWhiteObjects()) { - break; - } - } while (aUntilNoSWInPurpleBuffer); -} - -/* static */ void -nsCycleCollector::TryToFreeSnowWhite() -{ - CollectorData* data = sCollectorData.get(); - if (data->mCollector) { - data->mCollector->FreeSnowWhite(false); - } } void @@ -2297,7 +2185,10 @@ nsCycleCollector::ForgetSkippable(bool removeChildlessNodes) if (mJSRuntime) { mJSRuntime->PrepareForForgetSkippable(); } - mPurpleBuf.RemoveSkippable(removeChildlessNodes, mForgetSkippableCB); + mPurpleBuf.RemoveSkippable(removeChildlessNodes); + if (mForgetSkippableCB) { + mForgetSkippableCB(); + } } MOZ_NEVER_INLINE void @@ -2532,8 +2423,6 @@ nsCycleCollector::CollectWhite(nsICycleCollectorListener *aListener) } timeLog.Checkpoint("CollectWhite::Unroot"); - nsCycleCollector_dispatchDeferredDeletion(); - return count > 0; } @@ -2708,9 +2597,8 @@ nsCycleCollector_isScanSafe(void *s, nsCycleCollectionParticipant *cp) } #endif -void -nsCycleCollector::Suspect(void *n, nsCycleCollectionParticipant *cp, - nsCycleCollectingAutoRefCnt *aRefCnt) +nsPurpleBufferEntry* +nsCycleCollector::Suspect(void *n, nsCycleCollectionParticipant *cp) { CheckThreadSafety(); @@ -2719,15 +2607,16 @@ nsCycleCollector::Suspect(void *n, nsCycleCollectionParticipant *cp, // see some spurious refcount traffic here. if (mScanInProgress) - return; + return nullptr; MOZ_ASSERT(nsCycleCollector_isScanSafe(n, cp), "suspected a non-scansafe pointer"); if (mParams.mDoNothing) - return; + return nullptr; - mPurpleBuf.Put(n, cp, aRefCnt); + // Caller is responsible for filling in result's mRefCnt. + return mPurpleBuf.Put(n, cp); } void @@ -2841,7 +2730,6 @@ nsCycleCollector::ShutdownCollect(nsICycleCollectorListener *aListener) FixGrayBits(true); if (aListener && NS_FAILED(aListener->Begin())) aListener = nullptr; - FreeSnowWhite(true); if (!(BeginCollection(ShutdownCC, aListener) && FinishCollection(aListener))) break; @@ -2984,9 +2872,6 @@ nsCycleCollector::SuspectedCount() void nsCycleCollector::Shutdown() { - // Always delete snow white objects. - FreeSnowWhite(true); - #ifndef DEBUG if (PR_GetEnv("XPCOM_CC_RUN_DURING_SHUTDOWN")) #endif @@ -3115,10 +3000,8 @@ cyclecollector::TestJSHolder(void* aHolder) } #endif -void -NS_CycleCollectorSuspect3(void *n, nsCycleCollectionParticipant *cp, - nsCycleCollectingAutoRefCnt *aRefCnt, - bool* aShouldDelete) +nsPurpleBufferEntry* +NS_CycleCollectorSuspect2(void *n, nsCycleCollectionParticipant *cp) { CollectorData *data = sCollectorData.get(); @@ -3126,22 +3009,10 @@ NS_CycleCollectorSuspect3(void *n, nsCycleCollectionParticipant *cp, MOZ_ASSERT(data); if (!data->mCollector) { - if (aRefCnt->get() == 0) { - if (!aShouldDelete) { - CanonicalizeParticipant(&n, &cp); - aRefCnt->stabilizeForDeletion(); - cp->DeleteCycleCollectable(n); - } else { - *aShouldDelete = true; - } - } else { - // Make sure we'll get called again. - aRefCnt->RemoveFromPurpleBuffer(); - } - return; + return nullptr; } - return data->mCollector->Suspect(n, cp, aRefCnt); + return data->mCollector->Suspect(n, cp); } uint32_t @@ -3231,12 +3102,6 @@ nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes) } void -nsCycleCollector_dispatchDeferredDeletion() -{ - AsyncFreeSnowWhite::Dispatch(); -} - -void nsCycleCollector_collect(bool aManuallyTriggered, nsCycleCollectorResults *aResults, nsICycleCollectorListener *aListener) diff --git a/xpcom/base/nsCycleCollector.h b/xpcom/base/nsCycleCollector.h index 05d7ea482..1a1d61d72 100644 --- a/xpcom/base/nsCycleCollector.h +++ b/xpcom/base/nsCycleCollector.h @@ -47,8 +47,6 @@ void nsCycleCollector_setForgetSkippableCallback(CC_ForgetSkippableCallback aCB) void nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes = false); -void nsCycleCollector_dispatchDeferredDeletion(); - void nsCycleCollector_collect(bool aManuallyTriggered, nsCycleCollectorResults *aResults, nsICycleCollectorListener *aListener); diff --git a/xpcom/build/FrozenFunctions.cpp b/xpcom/build/FrozenFunctions.cpp index 44771dd23..4d183147d 100644 --- a/xpcom/build/FrozenFunctions.cpp +++ b/xpcom/build/FrozenFunctions.cpp @@ -89,11 +89,9 @@ static const XPCOMFunctions kFrozenFunctions = { &NS_CStringSetIsVoid, &NS_CStringGetIsVoid, - // these functions were added post 1.9, but then made obsolete - nullptr, - nullptr, - - &NS_CycleCollectorSuspect3, + // these functions were added post 1.9 + &NS_CycleCollectorSuspect2, + nullptr }; EXPORT_XPCOM_API(nsresult) diff --git a/xpcom/build/nsXPCOM.h b/xpcom/build/nsXPCOM.h index fef5d6b1f..f4ca03fe5 100644 --- a/xpcom/build/nsXPCOM.h +++ b/xpcom/build/nsXPCOM.h @@ -326,12 +326,9 @@ NS_LogCOMPtrRelease(void *aCOMPtr, nsISupports *aObject); #ifdef __cplusplus class nsCycleCollectionParticipant; -class nsCycleCollectingAutoRefCnt; -XPCOM_API(void) -NS_CycleCollectorSuspect3(void *n, nsCycleCollectionParticipant *p, - nsCycleCollectingAutoRefCnt *aRefCnt, - bool* aShouldDelete); +XPCOM_API(nsPurpleBufferEntry*) +NS_CycleCollectorSuspect2(void *n, nsCycleCollectionParticipant *p); #endif diff --git a/xpcom/build/nsXPCOMPrivate.h b/xpcom/build/nsXPCOMPrivate.h index 4dfc6b6d2..4b265c610 100644 --- a/xpcom/build/nsXPCOMPrivate.h +++ b/xpcom/build/nsXPCOMPrivate.h @@ -89,7 +89,7 @@ typedef bool (* CycleCollectorFunc)(nsISupports*); typedef nsPurpleBufferEntry* (* CycleCollectorSuspect2Func)(void*, nsCycleCollectionParticipant*); typedef bool (* CycleCollectorForget2Func)(nsPurpleBufferEntry*); -typedef void (* CycleCollectorSuspect3Func)(void*, nsCycleCollectionParticipant*,nsCycleCollectingAutoRefCnt*,bool*); + // PRIVATE AND DEPRECATED typedef NS_CALLBACK(XPCOMExitRoutine)(void); @@ -157,7 +157,7 @@ typedef struct XPCOMFunctions{ GetXPTCallStubFunc getXPTCallStubFunc; DestroyXPTCallStubFunc destroyXPTCallStubFunc; InvokeByIndexFunc invokeByIndexFunc; - CycleCollectorFunc cycleSuspectFunc; // obsolete: use cycleSuspect3Func + CycleCollectorFunc cycleSuspectFunc; // obsolete: use cycleSuspect2Func CycleCollectorFunc cycleForgetFunc; // obsolete StringSetIsVoidFunc stringSetIsVoid; StringGetIsVoidFunc stringGetIsVoid; @@ -165,11 +165,9 @@ typedef struct XPCOMFunctions{ CStringGetIsVoidFunc cstringGetIsVoid; // Added for Mozilla 1.9.1 - CycleCollectorSuspect2Func cycleSuspect2Func; // obsolete: use cycleSuspect3Func + CycleCollectorSuspect2Func cycleSuspect2Func; CycleCollectorForget2Func cycleForget2Func; // obsolete - CycleCollectorSuspect3Func cycleSuspect3Func; - } XPCOMFunctions; typedef nsresult (*GetFrozenFunctionsFunc)(XPCOMFunctions *entryPoints, const char* libraryPath); diff --git a/xpcom/glue/nsCycleCollectionParticipant.h b/xpcom/glue/nsCycleCollectionParticipant.h index 10bb8d2db..354278327 100644 --- a/xpcom/glue/nsCycleCollectionParticipant.h +++ b/xpcom/glue/nsCycleCollectionParticipant.h @@ -123,11 +123,11 @@ struct nsCycleCollectionParticipantVTableCommon nsresult (NS_STDCALL *Unlink)(void *p); nsresult (NS_STDCALL *Unroot)(void *p); + void (NS_STDCALL *UnmarkIfPurple)(void *p); + bool (NS_STDCALL *CanSkipReal)(void *p, bool aRemovingAllowed); bool (NS_STDCALL *CanSkipInCCReal)(void *p); bool (NS_STDCALL *CanSkipThisReal)(void *p); - - void (NS_STDCALL *DeleteCycleCollectable)(void*); }; typedef nsCycleCollectionParticipantVTableCommon<nsCycleCollectionParticipant> @@ -547,9 +547,9 @@ T* DowncastCCParticipant(void *p) public: \ static NS_METHOD TraverseImpl(NS_CYCLE_COLLECTION_CLASSNAME(_class) *that, \ void *p, nsCycleCollectionTraversalCallback &cb); \ - static NS_METHOD_(void) DeleteCycleCollectableImpl(void* p) \ + static NS_METHOD_(void) UnmarkIfPurpleImpl(void *s) \ { \ - DowncastCCParticipant<_class>(p)->DeleteCycleCollectable(); \ + Downcast(static_cast<nsISupports *>(s))->UnmarkIfPurple(); \ } \ static _class* Downcast(nsISupports* s) \ { \ @@ -710,6 +710,17 @@ class NS_CYCLE_COLLECTION_INNERCLASS }; /** + * This implements a stub UnmarkIfPurple function for classes that want to be + * traversed but whose AddRef/Release functions don't add/remove them to/from + * the purple buffer. If you're just using NS_DECL_CYCLE_COLLECTING_ISUPPORTS + * then you don't need this. + */ +#define NS_DECL_CYCLE_COLLECTION_UNMARK_PURPLE_STUB(_class) \ + NS_IMETHODIMP_(void) UnmarkIfPurple() \ + { \ + } \ + +/** * Dummy class with a definition for CanSkip* function members, but no * implementation. * Implementation was added to please Win PGO. (See bug 765159) @@ -741,10 +752,10 @@ struct Skippable &_class::RootImpl, \ &_class::UnlinkImpl, \ &_class::UnrootImpl, \ + &_class::UnmarkIfPurpleImpl, \ _class::isSkippable ? &Skippable<_class>::CanSkipImpl : NULL, \ _class::isSkippable ? &Skippable<_class>::CanSkipInCCImpl : NULL, \ - _class::isSkippable ? &Skippable<_class>::CanSkipThisImpl : NULL, \ - &_class::DeleteCycleCollectableImpl \ + _class::isSkippable ? &Skippable<_class>::CanSkipThisImpl : NULL \ } #define NS_IMPL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_VTABLE(_class) \ @@ -763,21 +774,22 @@ struct Skippable static NS_METHOD UnlinkImpl(void *n); \ static NS_METHOD UnrootImpl(void *n); \ static NS_METHOD TraverseImpl(NS_CYCLE_COLLECTION_CLASSNAME(_class) *that, \ - void *n, nsCycleCollectionTraversalCallback &cb); \ - static NS_METHOD_(void) DeleteCycleCollectableImpl(void* p) \ + void *n, nsCycleCollectionTraversalCallback &cb); + +#define NS_DECL_CYCLE_COLLECTION_NATIVE_UNMARK_IF_PURPLE(_class) \ + static NS_METHOD_(void) UnmarkIfPurpleImpl(void *p) \ { \ - DowncastCCParticipant<_class>(p)->DeleteCycleCollectable(); \ + _class *tmp = static_cast<_class *>(p); \ + if (MOZ_LIKELY(tmp->mRefCnt.HasPurpleBufferEntry())) \ + tmp->mRefCnt.ReleasePurpleBufferEntry(); \ } #define NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(_class) \ - void DeleteCycleCollectable(void) \ - { \ - delete this; \ - } \ class NS_CYCLE_COLLECTION_INNERCLASS \ : public nsCycleCollectionParticipant \ { \ NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class) \ + NS_DECL_CYCLE_COLLECTION_NATIVE_UNMARK_IF_PURPLE(_class) \ static nsCycleCollectionParticipant* GetParticipant() \ { \ static const CCParticipantVTable<NS_CYCLE_COLLECTION_CLASSNAME(_class)> \ @@ -790,14 +802,11 @@ struct Skippable }; #define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(_class) \ - void DeleteCycleCollectable(void) \ - { \ - delete this; \ - } \ class NS_CYCLE_COLLECTION_INNERCLASS \ : public nsScriptObjectTracer \ { \ NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class) \ + NS_DECL_CYCLE_COLLECTION_NATIVE_UNMARK_IF_PURPLE(_class) \ static NS_METHOD_(void) TraceImpl(void *p, const TraceCallbacks &cb, \ void *closure); \ static nsScriptObjectTracer* GetParticipant() \ diff --git a/xpcom/glue/nsISupportsImpl.h b/xpcom/glue/nsISupportsImpl.h index 76806e6be..c28623a77 100644 --- a/xpcom/glue/nsISupportsImpl.h +++ b/xpcom/glue/nsISupportsImpl.h @@ -82,99 +82,162 @@ private: #endif // DEBUG +#define NS_CCAR_REFCNT_BIT 1 +#define NS_CCAR_REFCNT_TO_TAGGED(rc_) \ + NS_INT32_TO_PTR((rc_ << 1) | NS_CCAR_REFCNT_BIT) +#define NS_CCAR_PURPLE_ENTRY_TO_TAGGED(pe_) \ + static_cast<void*>(pe_) +#define NS_CCAR_TAGGED_TO_REFCNT(tagged_) \ + nsrefcnt(NS_PTR_TO_INT32(tagged_) >> 1) +#define NS_CCAR_TAGGED_TO_PURPLE_ENTRY(tagged_) \ + static_cast<nsPurpleBufferEntry*>(tagged_) +#define NS_CCAR_TAGGED_STABILIZED_REFCNT NS_CCAR_PURPLE_ENTRY_TO_TAGGED(0) + // Support for ISupports classes which interact with cycle collector. struct nsPurpleBufferEntry { + // mObject is set to null when nsCycleCollectingAutoRefCnt loses its + // reference to the PurpleBufferEntry so that the entry can be added to the + // free list. When mObject is null, mNotPurple has no meaning. union { void *mObject; // when low bit unset nsPurpleBufferEntry *mNextInFreeList; // when low bit set }; - - nsCycleCollectingAutoRefCnt *mRefCnt; + // When an object is in the purple buffer, it replaces its reference + // count with a (tagged) pointer to this entry, so we store the + // reference count for it. + nsrefcnt mRefCnt : 31; + // When this flag is true, the purple buffer entry is in + // a state where there's an object out there that holds onto it, but we aren't + // counting that object as purple. This is done to reduce the cost of removing + // objects from the purple buffer. + nsrefcnt mNotPurple : 1; // nsrefcnt to ensure right packing. nsCycleCollectionParticipant *mParticipant; // NULL for nsISupports }; -#define NS_NUMBER_OF_FLAGS_IN_REFCNT 2 -#define NS_IN_PURPLE_BUFFER (1 << 0) -#define NS_IS_PURPLE (1 << 1) -#define NS_REFCOUNT_CHANGE (1 << NS_NUMBER_OF_FLAGS_IN_REFCNT) -#define NS_REFCOUNT_VALUE(_val) (_val >> NS_NUMBER_OF_FLAGS_IN_REFCNT) - class nsCycleCollectingAutoRefCnt { public: nsCycleCollectingAutoRefCnt() - : mRefCntAndFlags(0) + : mTagged(NS_CCAR_REFCNT_TO_TAGGED(0)) {} - nsCycleCollectingAutoRefCnt(uintptr_t aValue) - : mRefCntAndFlags(aValue << NS_NUMBER_OF_FLAGS_IN_REFCNT) + nsCycleCollectingAutoRefCnt(nsrefcnt aValue) + : mTagged(NS_CCAR_REFCNT_TO_TAGGED(aValue)) { } - MOZ_ALWAYS_INLINE uintptr_t incr() + MOZ_ALWAYS_INLINE nsrefcnt incr(void *owner) { - mRefCntAndFlags += NS_REFCOUNT_CHANGE; - mRefCntAndFlags &= ~NS_IS_PURPLE; - return NS_REFCOUNT_VALUE(mRefCntAndFlags); + if (MOZ_UNLIKELY(mTagged == NS_CCAR_TAGGED_STABILIZED_REFCNT)) { + // The sentinel value "purple bit alone, refcount 0" means + // that we're stabilized, during finalization. In this + // state we lie about our actual refcount if anyone asks + // and say it's 2, which is basically true: the caller who + // is incrementing has a reference, as does the decr() frame + // that stabilized-and-is-deleting us. + return 2; + } + + nsrefcnt refcount; + if (HasPurpleBufferEntry()) { + nsPurpleBufferEntry *e = NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged); + MOZ_ASSERT(e->mObject == owner, "wrong entry"); + MOZ_ASSERT(int32_t(e->mRefCnt) > 0, "purple ISupports with bad refcnt"); + refcount = ++(e->mRefCnt); + e->mNotPurple = true; + } else { + refcount = NS_CCAR_TAGGED_TO_REFCNT(mTagged); + MOZ_ASSERT(int32_t(refcount) >= 0, "bad refcount"); + ++refcount; + mTagged = NS_CCAR_REFCNT_TO_TAGGED(refcount); + } + + return refcount; } MOZ_ALWAYS_INLINE void stabilizeForDeletion() { - // Set refcnt to 1 and mark us to be in the purple buffer. - // This way decr won't call suspect again. - mRefCntAndFlags = NS_REFCOUNT_CHANGE | NS_IN_PURPLE_BUFFER; + mTagged = NS_CCAR_TAGGED_STABILIZED_REFCNT; } - MOZ_ALWAYS_INLINE uintptr_t decr(nsISupports *owner, - bool *shouldDelete = nullptr) + MOZ_ALWAYS_INLINE nsrefcnt decr(nsISupports *owner) { - return decr(owner, nullptr, shouldDelete); + return decr(owner, nullptr); } - MOZ_ALWAYS_INLINE uintptr_t decr(void *owner, nsCycleCollectionParticipant *p, - bool *shouldDelete = nullptr) + MOZ_ALWAYS_INLINE nsrefcnt decr(void *owner, nsCycleCollectionParticipant *p) { - MOZ_ASSERT(get() > 0); - if (!IsInPurpleBuffer()) { - mRefCntAndFlags -= NS_REFCOUNT_CHANGE; - mRefCntAndFlags |= (NS_IN_PURPLE_BUFFER | NS_IS_PURPLE); - uintptr_t retval = NS_REFCOUNT_VALUE(mRefCntAndFlags); - // Suspect may delete 'owner' and 'this'! - NS_CycleCollectorSuspect3(owner, p, this, shouldDelete); - return retval; + if (MOZ_UNLIKELY(mTagged == NS_CCAR_TAGGED_STABILIZED_REFCNT)) + return 1; + + nsrefcnt refcount; + if (HasPurpleBufferEntry()) { + nsPurpleBufferEntry *e = NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged); + MOZ_ASSERT(e->mObject == owner, "wrong entry"); + MOZ_ASSERT(int32_t(e->mRefCnt) > 0, "purple ISupports with bad refcnt"); + refcount = --(e->mRefCnt); + if (MOZ_UNLIKELY(refcount == 0)) { + e->mObject = nullptr; + mTagged = NS_CCAR_REFCNT_TO_TAGGED(0); + } else { + e->mNotPurple = false; + } + } else { + refcount = NS_CCAR_TAGGED_TO_REFCNT(mTagged); + MOZ_ASSERT(int32_t(refcount) > 0, "bad refcount"); + --refcount; + + nsPurpleBufferEntry *e; + if (MOZ_LIKELY(refcount > 0) && + ((e = NS_CycleCollectorSuspect2(owner, p)))) { + e->mRefCnt = refcount; + mTagged = NS_CCAR_PURPLE_ENTRY_TO_TAGGED(e); + } else { + mTagged = NS_CCAR_REFCNT_TO_TAGGED(refcount); + } } - mRefCntAndFlags -= NS_REFCOUNT_CHANGE; - mRefCntAndFlags |= (NS_IN_PURPLE_BUFFER | NS_IS_PURPLE); - return NS_REFCOUNT_VALUE(mRefCntAndFlags); + + return refcount; } - MOZ_ALWAYS_INLINE void RemovePurple() + MOZ_ALWAYS_INLINE void ReleasePurpleBufferEntry() { - MOZ_ASSERT(IsPurple(), "must be purple"); - mRefCntAndFlags &= ~NS_IS_PURPLE; + MOZ_ASSERT(HasPurpleBufferEntry(), "must have purple buffer entry"); + nsrefcnt refcount = NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged)->mRefCnt; + mTagged = NS_CCAR_REFCNT_TO_TAGGED(refcount); } - MOZ_ALWAYS_INLINE void RemoveFromPurpleBuffer() + MOZ_ALWAYS_INLINE void RemovePurple() { - MOZ_ASSERT(IsInPurpleBuffer()); - mRefCntAndFlags &= ~(NS_IS_PURPLE | NS_IN_PURPLE_BUFFER); + MOZ_ASSERT(IsPurple(), "must be purple"); + // The entry will be added to the free list later. + NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged)->mObject = nullptr; + ReleasePurpleBufferEntry(); } - MOZ_ALWAYS_INLINE bool IsPurple() const + MOZ_ALWAYS_INLINE bool HasPurpleBufferEntry() const { - return !!(mRefCntAndFlags & NS_IS_PURPLE); + MOZ_ASSERT(mTagged != NS_CCAR_TAGGED_STABILIZED_REFCNT, + "should have checked for stabilization first"); + return !(NS_PTR_TO_INT32(mTagged) & NS_CCAR_REFCNT_BIT); } - MOZ_ALWAYS_INLINE bool IsInPurpleBuffer() const + MOZ_ALWAYS_INLINE bool IsPurple() const { - return !!(mRefCntAndFlags & NS_IN_PURPLE_BUFFER); + return HasPurpleBufferEntry() && + !(NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged)->mNotPurple); } MOZ_ALWAYS_INLINE nsrefcnt get() const { - return NS_REFCOUNT_VALUE(mRefCntAndFlags); + if (MOZ_UNLIKELY(mTagged == NS_CCAR_TAGGED_STABILIZED_REFCNT)) + return 1; + + return MOZ_UNLIKELY(HasPurpleBufferEntry()) + ? NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged)->mRefCnt + : NS_CCAR_TAGGED_TO_REFCNT(mTagged); } MOZ_ALWAYS_INLINE operator nsrefcnt() const @@ -183,7 +246,7 @@ public: } private: - uintptr_t mRefCntAndFlags; + void *mTagged; }; class nsAutoRefCnt { @@ -230,7 +293,11 @@ public: \ void** aInstancePtr); \ NS_IMETHOD_(nsrefcnt) AddRef(void); \ NS_IMETHOD_(nsrefcnt) Release(void); \ - NS_IMETHOD_(void) DeleteCycleCollectable(void); \ + void UnmarkIfPurple() \ + { \ + if (MOZ_LIKELY(mRefCnt.HasPurpleBufferEntry())) \ + mRefCnt.ReleasePurpleBufferEntry(); \ + } \ protected: \ nsCycleCollectingAutoRefCnt mRefCnt; \ NS_DECL_OWNINGTHREAD \ @@ -247,7 +314,7 @@ public: #define NS_IMPL_CC_NATIVE_ADDREF_BODY(_class) \ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \ NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class); \ - nsrefcnt count = mRefCnt.incr(); \ + nsrefcnt count = mRefCnt.incr(this); \ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \ return count; @@ -258,6 +325,12 @@ public: mRefCnt.decr(static_cast<void*>(this), \ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \ NS_LOG_RELEASE(this, count, #_class); \ + if (count == 0) { \ + NS_ASSERT_OWNINGTHREAD(_class); \ + mRefCnt.stabilizeForDeletion(); \ + delete this; \ + return 0; \ + } \ return count; #define NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(_class) \ @@ -266,30 +339,6 @@ NS_METHOD_(nsrefcnt) _class::AddRef(void) \ NS_IMPL_CC_NATIVE_ADDREF_BODY(_class) \ } -#define NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(_class, _last) \ -NS_METHOD_(nsrefcnt) _class::Release(void) \ -{ \ - MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \ - NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class); \ - bool shouldDelete = false; \ - nsrefcnt count = \ - mRefCnt.decr(static_cast<void*>(this), \ - _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant(), \ - &shouldDelete); \ - NS_LOG_RELEASE(this, count, #_class); \ - if (count == 0) { \ - mRefCnt.incr(); \ - _last; \ - mRefCnt.decr(static_cast<void*>(this), \ - _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \ - if (shouldDelete) { \ - mRefCnt.stabilizeForDeletion(); \ - DeleteCycleCollectable(); \ - } \ - } \ - return count; \ -} - #define NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(_class) \ NS_METHOD_(nsrefcnt) _class::Release(void) \ { \ @@ -481,7 +530,8 @@ NS_IMETHODIMP_(nsrefcnt) _class::AddRef(void) \ { \ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \ NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class); \ - nsrefcnt count = mRefCnt.incr(); \ + nsrefcnt count = \ + mRefCnt.incr(NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this)); \ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \ return count; \ } @@ -494,42 +544,18 @@ NS_IMETHODIMP_(nsrefcnt) _class::Release(void) \ nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \ nsrefcnt count = mRefCnt.decr(base); \ NS_LOG_RELEASE(this, count, #_class); \ + if (count == 0) { \ + NS_ASSERT_OWNINGTHREAD(_class); \ + mRefCnt.stabilizeForDeletion(); \ + _destroy; \ + return 0; \ + } \ return count; \ -} \ -NS_IMETHODIMP_(void) _class::DeleteCycleCollectable(void) \ -{ \ - _destroy; \ } #define NS_IMPL_CYCLE_COLLECTING_RELEASE(_class) \ NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(_class, delete (this)) -// _LAST_RELEASE can be useful when certain resources should be released -// as soon as we know the object will be deleted. -#define NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(_class, _last) \ -NS_IMETHODIMP_(nsrefcnt) _class::Release(void) \ -{ \ - MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \ - NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class); \ - bool shouldDelete = false; \ - nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \ - nsrefcnt count = mRefCnt.decr(base, &shouldDelete); \ - NS_LOG_RELEASE(this, count, #_class); \ - if (count == 0) { \ - mRefCnt.incr(); \ - _last; \ - mRefCnt.decr(base); \ - if (shouldDelete) { \ - mRefCnt.stabilizeForDeletion(); \ - DeleteCycleCollectable(); \ - } \ - } \ - return count; \ -} \ -NS_IMETHODIMP_(void) _class::DeleteCycleCollectable(void) \ -{ \ - delete this; \ -} /////////////////////////////////////////////////////////////////////////////// diff --git a/xpcom/glue/standalone/nsXPCOMGlue.cpp b/xpcom/glue/standalone/nsXPCOMGlue.cpp index 67b7cbc4a..16adfd102 100644 --- a/xpcom/glue/standalone/nsXPCOMGlue.cpp +++ b/xpcom/glue/standalone/nsXPCOMGlue.cpp @@ -961,15 +961,6 @@ NS_CycleCollectorSuspect2(void* obj, nsCycleCollectionParticipant *p) return xpcomFunctions.cycleSuspect2Func(obj, p); } -XPCOM_API(void) -NS_CycleCollectorSuspect3(void* obj, nsCycleCollectionParticipant *p, - nsCycleCollectingAutoRefCnt* aRefCnt, - bool* aShouldDelete) -{ - if (xpcomFunctions.cycleSuspect3Func) - xpcomFunctions.cycleSuspect3Func(obj, p, aRefCnt, aShouldDelete); -} - XPCOM_API(bool) NS_CycleCollectorForget2(nsPurpleBufferEntry* e) { |