summaryrefslogtreecommitdiff
path: root/xpcom
diff options
context:
space:
mode:
authorPale Moon <git-repo@palemoon.org>2016-03-31 02:30:31 +0200
committerPale Moon <git-repo@palemoon.org>2016-03-31 02:30:31 +0200
commit0d7c146acd32e6f4b313e45e599ccbb602b5b012 (patch)
tree06462c9c077822b3d48d8373cde79bc9fe1ed5ca /xpcom
parent5c90e4393ec4238a1519f92c36c74120727c0ccf (diff)
downloadpalemoon-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.cpp2
-rw-r--r--xpcom/base/CycleCollectedJSRuntime.h4
-rw-r--r--xpcom/base/nsAgg.h18
-rw-r--r--xpcom/base/nsCycleCollector.cpp233
-rw-r--r--xpcom/base/nsCycleCollector.h2
-rw-r--r--xpcom/build/FrozenFunctions.cpp8
-rw-r--r--xpcom/build/nsXPCOM.h7
-rw-r--r--xpcom/build/nsXPCOMPrivate.h8
-rw-r--r--xpcom/glue/nsCycleCollectionParticipant.h43
-rw-r--r--xpcom/glue/nsISupportsImpl.h232
-rw-r--r--xpcom/glue/standalone/nsXPCOMGlue.cpp9
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)
{