зеркало из https://github.com/mozilla/gecko-dev.git
Bug 885866: Separate deferred finalization from XPConnect so we can use it off the main thread. r=mccr8, peterv, bsmedberg, jorendorff
This commit is contained in:
Родитель
aaba06e7d1
Коммит
ab927a2cc9
|
@ -125,6 +125,16 @@ namespace layers {
|
|||
class LayerManager;
|
||||
} // namespace layers
|
||||
|
||||
// Called back from DeferredFinalize. Should add 'thing' to the array of smart
|
||||
// pointers in 'pointers', creating the array if 'pointers' is null, and return
|
||||
// the array.
|
||||
typedef void* (*DeferredFinalizeAppendFunction)(void* pointers, void* thing);
|
||||
|
||||
// Called to finalize a number of objects. Slice is the number of objects
|
||||
// to finalize, or if it's UINT32_MAX, all objects should be finalized.
|
||||
// Return value indicates whether it finalized all objects in the buffer.
|
||||
typedef bool (*DeferredFinalizeFunction)(uint32_t slice, void* data);
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#ifdef IBMBIDI
|
||||
|
@ -1263,6 +1273,11 @@ public:
|
|||
static bool AreJSObjectsHeld(void* aScriptObjectHolder);
|
||||
#endif
|
||||
|
||||
static void DeferredFinalize(nsISupports* aSupports);
|
||||
static void DeferredFinalize(mozilla::DeferredFinalizeAppendFunction aAppendFunc,
|
||||
mozilla::DeferredFinalizeFunction aFunc,
|
||||
void* aThing);
|
||||
|
||||
static void ReleaseWrapper(void* aScriptObjectHolder,
|
||||
nsWrapperCache* aCache);
|
||||
|
||||
|
|
|
@ -5756,6 +5756,22 @@ nsContentUtils::AllocClassMatchingInfo(nsINode* aRootNode,
|
|||
return info;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsContentUtils::DeferredFinalize(nsISupports* aSupports)
|
||||
{
|
||||
cyclecollector::DeferredFinalize(aSupports);
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsContentUtils::DeferredFinalize(mozilla::DeferredFinalizeAppendFunction aAppendFunc,
|
||||
mozilla::DeferredFinalizeFunction aFunc,
|
||||
void* aThing)
|
||||
{
|
||||
cyclecollector::DeferredFinalize(aAppendFunc, aFunc, aThing);
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
nsContentUtils::IsFocusedContent(const nsIContent* aContent)
|
||||
|
|
|
@ -74,7 +74,7 @@ XBLFinalize(JSFreeOp *fop, JSObject *obj)
|
|||
{
|
||||
nsXBLDocumentInfo* docInfo =
|
||||
static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(obj));
|
||||
xpc::DeferredRelease(static_cast<nsIScriptGlobalObjectOwner*>(docInfo));
|
||||
nsContentUtils::DeferredFinalize(static_cast<nsIScriptGlobalObjectOwner*>(docInfo));
|
||||
|
||||
nsXBLJSClass* c = static_cast<nsXBLJSClass*>(::JS_GetClass(obj));
|
||||
c->Drop();
|
||||
|
|
|
@ -5339,7 +5339,7 @@ nsHTMLDocumentSH::ReleaseDocument(JSFreeOp *fop, JSObject *obj)
|
|||
{
|
||||
nsIHTMLDocument* doc = GetDocument(obj);
|
||||
if (doc) {
|
||||
xpc::DeferredRelease(doc);
|
||||
nsContentUtils::DeferredFinalize(doc);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1795,17 +1795,6 @@ ReportLenientThisUnwrappingFailure(JSContext* cx, JS::Handle<JSObject*> obj)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RegisterForDeferredFinalization(DeferredFinalizeStartFunction start,
|
||||
DeferredFinalizeFunction run)
|
||||
{
|
||||
XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
|
||||
NS_ENSURE_TRUE(rt, false);
|
||||
|
||||
rt->RegisterDeferredFinalize(start, run);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Date implementation methods
|
||||
Date::Date() :
|
||||
mMsecSinceEpoch(UnspecifiedNaN())
|
||||
|
|
|
@ -2003,10 +2003,6 @@ ConstructJSImplementation(JSContext* aCx, const char* aContractId,
|
|||
JS::MutableHandle<JSObject*> aObject,
|
||||
ErrorResult& aRv);
|
||||
|
||||
bool
|
||||
RegisterForDeferredFinalization(DeferredFinalizeStartFunction start,
|
||||
DeferredFinalizeFunction run);
|
||||
|
||||
/**
|
||||
* Convert an nsCString to jsval, returning true on success.
|
||||
* These functions are intended for ByteString implementations.
|
||||
|
|
|
@ -964,26 +964,22 @@ def DeferredFinalizeSmartPtr(descriptor):
|
|||
smartPtr = 'nsRefPtr<%s>'
|
||||
return smartPtr % descriptor.nativeType
|
||||
|
||||
class CGDeferredFinalizePointers(CGThing):
|
||||
class CGAppendDeferredFinalizePointer(CGAbstractStaticMethod):
|
||||
def __init__(self, descriptor):
|
||||
CGThing.__init__(self)
|
||||
self.descriptor = descriptor
|
||||
|
||||
def declare(self):
|
||||
return ""
|
||||
|
||||
def define(self):
|
||||
return """nsTArray<%s >* sDeferredFinalizePointers;
|
||||
""" % DeferredFinalizeSmartPtr(self.descriptor)
|
||||
|
||||
class CGGetDeferredFinalizePointers(CGAbstractStaticMethod):
|
||||
def __init__(self, descriptor):
|
||||
CGAbstractStaticMethod.__init__(self, descriptor, "GetDeferredFinalizePointers", "void*", [])
|
||||
CGAbstractStaticMethod.__init__(self, descriptor, "AppendDeferredFinalizePointer", "void*", [Argument('void*', 'data'), Argument('void*', 'thing')])
|
||||
|
||||
def definition_body(self):
|
||||
return """ nsTArray<%s >* pointers = sDeferredFinalizePointers;
|
||||
sDeferredFinalizePointers = nullptr;
|
||||
return pointers;""" % DeferredFinalizeSmartPtr(self.descriptor)
|
||||
smartPtr = DeferredFinalizeSmartPtr(self.descriptor)
|
||||
return """ nsTArray<%(smartPtr)s >* pointers = static_cast<nsTArray<%(smartPtr)s >*>(data);
|
||||
if (!pointers) {
|
||||
pointers = new nsTArray<%(smartPtr)s >();
|
||||
}
|
||||
|
||||
%(nativeType)s* self = static_cast<%(nativeType)s*>(thing);
|
||||
|
||||
%(smartPtr)s* defer = pointers->AppendElement();
|
||||
Take(*defer, self);
|
||||
return pointers;""" % { 'smartPtr': smartPtr, 'nativeType': self.descriptor.nativeType }
|
||||
|
||||
class CGDeferredFinalize(CGAbstractStaticMethod):
|
||||
def __init__(self, descriptor):
|
||||
|
@ -1015,29 +1011,12 @@ def finalizeHook(descriptor, hookName, context):
|
|||
if descriptor.workers:
|
||||
finalize += "self->Release();"
|
||||
elif descriptor.nativeOwnership == 'nsisupports':
|
||||
finalize += "xpc::DeferredRelease(reinterpret_cast<nsISupports*>(self));"
|
||||
finalize += "nsContentUtils::DeferredFinalize(reinterpret_cast<nsISupports*>(self));"
|
||||
else:
|
||||
smartPtr = DeferredFinalizeSmartPtr(descriptor)
|
||||
finalize += """static bool registered = false;
|
||||
if (!registered) {
|
||||
if (!RegisterForDeferredFinalization(GetDeferredFinalizePointers,
|
||||
DeferredFinalize)) {
|
||||
%(smartPtr)s dying;
|
||||
Take(dying, self);
|
||||
return;
|
||||
}
|
||||
registered = true;
|
||||
}
|
||||
if (!sDeferredFinalizePointers) {
|
||||
sDeferredFinalizePointers = new nsAutoTArray<%(smartPtr)s, 16>();
|
||||
}
|
||||
%(smartPtr)s* defer = sDeferredFinalizePointers->AppendElement();
|
||||
if (!defer) {
|
||||
%(smartPtr)s dying;
|
||||
Take(dying, self);
|
||||
return;
|
||||
}
|
||||
Take(*defer, self);""" % { 'smartPtr': smartPtr }
|
||||
finalize += """nsContentUtils::DeferredFinalize(AppendDeferredFinalizePointer,
|
||||
DeferredFinalize,
|
||||
self);
|
||||
"""
|
||||
return CGIfWrapper(CGGeneric(finalize), "self")
|
||||
|
||||
class CGClassFinalizeHook(CGAbstractClassHook):
|
||||
|
@ -7583,8 +7562,7 @@ class CGDescriptor(CGThing):
|
|||
|
||||
if descriptor.concrete:
|
||||
if descriptor.nativeOwnership == 'owned' or descriptor.nativeOwnership == 'refcounted':
|
||||
cgThings.append(CGDeferredFinalizePointers(descriptor))
|
||||
cgThings.append(CGGetDeferredFinalizePointers(descriptor))
|
||||
cgThings.append(CGAppendDeferredFinalizePointer(descriptor))
|
||||
cgThings.append(CGDeferredFinalize(descriptor))
|
||||
|
||||
if not descriptor.proxy:
|
||||
|
|
|
@ -196,7 +196,7 @@ static void
|
|||
OnWrapperDestroyed();
|
||||
|
||||
static void
|
||||
DelayedReleaseGCCallback(JSRuntime* rt, JSGCStatus status)
|
||||
DelayedReleaseGCCallback(JSGCStatus status)
|
||||
{
|
||||
if (JSGC_END == status) {
|
||||
// Take ownership of sDelayedReleases and null it out now. The
|
||||
|
|
|
@ -2784,10 +2784,11 @@ JS_MaybeGC(JSContext *cx)
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_SetGCCallback(JSRuntime *rt, JSGCCallback cb)
|
||||
JS_SetGCCallback(JSRuntime *rt, JSGCCallback cb, void *data)
|
||||
{
|
||||
AssertHeapIsIdle(rt);
|
||||
rt->gcCallback = cb;
|
||||
rt->gcCallbackData = data;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
|
|
|
@ -956,7 +956,7 @@ typedef enum JSGCStatus {
|
|||
} JSGCStatus;
|
||||
|
||||
typedef void
|
||||
(* JSGCCallback)(JSRuntime *rt, JSGCStatus status);
|
||||
(* JSGCCallback)(JSRuntime *rt, JSGCStatus status, void *data);
|
||||
|
||||
typedef enum JSFinalizeStatus {
|
||||
/*
|
||||
|
@ -2629,7 +2629,7 @@ extern JS_PUBLIC_API(void)
|
|||
JS_MaybeGC(JSContext *cx);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetGCCallback(JSRuntime *rt, JSGCCallback cb);
|
||||
JS_SetGCCallback(JSRuntime *rt, JSGCCallback cb, void *data);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb);
|
||||
|
|
|
@ -4553,7 +4553,7 @@ Collect(JSRuntime *rt, bool incremental, int64_t budget,
|
|||
if (rt->gcIncrementalState == NO_INCREMENTAL) {
|
||||
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_GC_BEGIN);
|
||||
if (JSGCCallback callback = rt->gcCallback)
|
||||
callback(rt, JSGC_BEGIN);
|
||||
callback(rt, JSGC_BEGIN, rt->gcCallbackData);
|
||||
}
|
||||
|
||||
rt->gcPoke = false;
|
||||
|
@ -4562,7 +4562,7 @@ Collect(JSRuntime *rt, bool incremental, int64_t budget,
|
|||
if (rt->gcIncrementalState == NO_INCREMENTAL) {
|
||||
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_GC_END);
|
||||
if (JSGCCallback callback = rt->gcCallback)
|
||||
callback(rt, JSGC_END);
|
||||
callback(rt, JSGC_END, rt->gcCallbackData);
|
||||
}
|
||||
|
||||
/* Need to re-schedule all zones for GC. */
|
||||
|
|
|
@ -1120,6 +1120,8 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
JS::GCSliceCallback gcSliceCallback;
|
||||
JSFinalizeCallback gcFinalizeCallback;
|
||||
|
||||
void *gcCallbackData;
|
||||
|
||||
js::AnalysisPurgeCallback analysisPurgeCallback;
|
||||
uint64_t analysisPurgeTriggerBytes;
|
||||
|
||||
|
|
|
@ -7,11 +7,18 @@
|
|||
#include "nsISupports.idl"
|
||||
|
||||
[ptr] native JSRuntime(JSRuntime);
|
||||
native JSGCCallback(JSGCCallback);
|
||||
native xpcGCCallback(xpcGCCallback);
|
||||
|
||||
%{C++
|
||||
|
||||
typedef void
|
||||
(* xpcGCCallback)(JSGCStatus status);
|
||||
|
||||
%}
|
||||
|
||||
interface nsIBackstagePass;
|
||||
|
||||
[uuid(991c0749-a22e-476b-9428-a373df037455)]
|
||||
[uuid(996ef894-88cd-42c8-ac2d-28c60970daaf)]
|
||||
interface nsIJSRuntimeService : nsISupports
|
||||
{
|
||||
readonly attribute JSRuntime runtime;
|
||||
|
@ -20,6 +27,6 @@ interface nsIJSRuntimeService : nsISupports
|
|||
* Register additional GC callback which will run after the
|
||||
* standard XPConnect callback.
|
||||
*/
|
||||
[noscript, notxpcom] void registerGCCallback(in JSGCCallback func);
|
||||
[noscript, notxpcom] void unregisterGCCallback(in JSGCCallback func);
|
||||
[noscript, notxpcom] void registerGCCallback(in xpcGCCallback func);
|
||||
[noscript, notxpcom] void unregisterGCCallback(in xpcGCCallback func);
|
||||
};
|
||||
|
|
|
@ -572,149 +572,6 @@ DoDeferredRelease(nsTArray<T> &array)
|
|||
}
|
||||
}
|
||||
|
||||
struct DeferredFinalizeFunctionHolder
|
||||
{
|
||||
DeferredFinalizeFunction run;
|
||||
void *data;
|
||||
};
|
||||
|
||||
class XPCIncrementalReleaseRunnable : public nsRunnable
|
||||
{
|
||||
XPCJSRuntime *runtime;
|
||||
nsTArray<nsISupports *> items;
|
||||
nsAutoTArray<DeferredFinalizeFunctionHolder, 16> deferredFinalizeFunctions;
|
||||
uint32_t finalizeFunctionToRun;
|
||||
|
||||
static const PRTime SliceMillis = 10; /* ms */
|
||||
|
||||
public:
|
||||
XPCIncrementalReleaseRunnable(XPCJSRuntime *rt, nsTArray<nsISupports *> &items);
|
||||
virtual ~XPCIncrementalReleaseRunnable();
|
||||
|
||||
void ReleaseNow(bool limited);
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
bool
|
||||
ReleaseSliceNow(uint32_t slice, void *data)
|
||||
{
|
||||
MOZ_ASSERT(slice > 0, "nonsensical/useless call with slice == 0");
|
||||
nsTArray<nsISupports *> *items = static_cast<nsTArray<nsISupports *>*>(data);
|
||||
|
||||
slice = std::min(slice, items->Length());
|
||||
for (uint32_t i = 0; i < slice; ++i) {
|
||||
// Remove (and NS_RELEASE) the last entry in "items":
|
||||
uint32_t lastItemIdx = items->Length() - 1;
|
||||
|
||||
nsISupports *wrapper = items->ElementAt(lastItemIdx);
|
||||
items->RemoveElementAt(lastItemIdx);
|
||||
NS_RELEASE(wrapper);
|
||||
}
|
||||
|
||||
return items->IsEmpty();
|
||||
}
|
||||
|
||||
|
||||
XPCIncrementalReleaseRunnable::XPCIncrementalReleaseRunnable(XPCJSRuntime *rt,
|
||||
nsTArray<nsISupports *> &items)
|
||||
: runtime(rt),
|
||||
finalizeFunctionToRun(0)
|
||||
{
|
||||
nsLayoutStatics::AddRef();
|
||||
this->items.SwapElements(items);
|
||||
DeferredFinalizeFunctionHolder *function = deferredFinalizeFunctions.AppendElement();
|
||||
function->run = ReleaseSliceNow;
|
||||
function->data = &this->items;
|
||||
for (uint32_t i = 0; i < rt->mDeferredFinalizeFunctions.Length(); ++i) {
|
||||
void *data = (rt->mDeferredFinalizeFunctions[i].start)();
|
||||
if (data) {
|
||||
function = deferredFinalizeFunctions.AppendElement();
|
||||
function->run = rt->mDeferredFinalizeFunctions[i].run;
|
||||
function->data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XPCIncrementalReleaseRunnable::~XPCIncrementalReleaseRunnable()
|
||||
{
|
||||
MOZ_ASSERT(this != runtime->mReleaseRunnable);
|
||||
nsLayoutStatics::Release();
|
||||
}
|
||||
|
||||
void
|
||||
XPCIncrementalReleaseRunnable::ReleaseNow(bool limited)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(deferredFinalizeFunctions.Length() != 0,
|
||||
"We should have at least ReleaseSliceNow to run");
|
||||
MOZ_ASSERT(finalizeFunctionToRun < deferredFinalizeFunctions.Length(),
|
||||
"No more finalizers to run?");
|
||||
|
||||
TimeDuration sliceTime = TimeDuration::FromMilliseconds(SliceMillis);
|
||||
TimeStamp started = TimeStamp::Now();
|
||||
bool timeout = false;
|
||||
do {
|
||||
const DeferredFinalizeFunctionHolder &function =
|
||||
deferredFinalizeFunctions[finalizeFunctionToRun];
|
||||
if (limited) {
|
||||
bool done = false;
|
||||
while (!timeout && !done) {
|
||||
/*
|
||||
* We don't want to read the clock too often, so we try to
|
||||
* release slices of 100 items.
|
||||
*/
|
||||
done = function.run(100, function.data);
|
||||
timeout = TimeStamp::Now() - started >= sliceTime;
|
||||
}
|
||||
if (done)
|
||||
++finalizeFunctionToRun;
|
||||
if (timeout)
|
||||
break;
|
||||
} else {
|
||||
function.run(UINT32_MAX, function.data);
|
||||
MOZ_ASSERT(!items.Length());
|
||||
++finalizeFunctionToRun;
|
||||
}
|
||||
} while (finalizeFunctionToRun < deferredFinalizeFunctions.Length());
|
||||
|
||||
if (finalizeFunctionToRun == deferredFinalizeFunctions.Length()) {
|
||||
MOZ_ASSERT(runtime->mReleaseRunnable == this);
|
||||
runtime->mReleaseRunnable = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
XPCIncrementalReleaseRunnable::Run()
|
||||
{
|
||||
if (runtime->mReleaseRunnable != this) {
|
||||
/* These items were already processed synchronously in JSGC_BEGIN. */
|
||||
MOZ_ASSERT(!items.Length());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ReleaseNow(true);
|
||||
|
||||
if (items.Length()) {
|
||||
nsresult rv = NS_DispatchToMainThread(this);
|
||||
if (NS_FAILED(rv))
|
||||
ReleaseNow(false);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
XPCJSRuntime::ReleaseIncrementally(nsTArray<nsISupports *> &array)
|
||||
{
|
||||
MOZ_ASSERT(!mReleaseRunnable);
|
||||
mReleaseRunnable = new XPCIncrementalReleaseRunnable(this, array);
|
||||
|
||||
nsresult rv = NS_DispatchToMainThread(mReleaseRunnable);
|
||||
if (NS_FAILED(rv))
|
||||
mReleaseRunnable->ReleaseNow(false);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
XPCJSRuntime::GCSliceCallback(JSRuntime *rt,
|
||||
JS::GCProgress progress,
|
||||
|
@ -733,20 +590,16 @@ XPCJSRuntime::GCSliceCallback(JSRuntime *rt,
|
|||
(*self->mPrevGCSliceCallback)(rt, progress, desc);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
XPCJSRuntime::GCCallback(JSRuntime *rt, JSGCStatus status)
|
||||
void
|
||||
XPCJSRuntime::CustomGCCallback(JSGCStatus status)
|
||||
{
|
||||
XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
|
||||
if (!self)
|
||||
return;
|
||||
|
||||
switch (status) {
|
||||
case JSGC_BEGIN:
|
||||
{
|
||||
// We seem to sometime lose the unrooted global flag. Restore it
|
||||
// here. FIXME: bug 584495.
|
||||
JSContext *iter = nullptr;
|
||||
while (JSContext *acx = JS_ContextIterator(rt, &iter)) {
|
||||
while (JSContext *acx = JS_ContextIterator(Runtime(), &iter)) {
|
||||
if (!js::HasUnrootedGlobal(acx))
|
||||
JS_ToggleOptions(acx, JSOPTION_UNROOTED_GLOBAL);
|
||||
}
|
||||
|
@ -754,33 +607,13 @@ XPCJSRuntime::GCCallback(JSRuntime *rt, JSGCStatus status)
|
|||
}
|
||||
case JSGC_END:
|
||||
{
|
||||
/*
|
||||
* If the previous GC created a runnable to release objects
|
||||
* incrementally, and if it hasn't finished yet, finish it now. We
|
||||
* don't want these to build up. We also don't want to allow any
|
||||
* existing incremental release runnables to run after a
|
||||
* non-incremental GC, since they are often used to detect leaks.
|
||||
*/
|
||||
if (self->mReleaseRunnable)
|
||||
self->mReleaseRunnable->ReleaseNow(false);
|
||||
|
||||
// Do any deferred releases of native objects.
|
||||
if (JS::WasIncrementalGC(rt)) {
|
||||
self->ReleaseIncrementally(self->mNativesToReleaseArray);
|
||||
} else {
|
||||
DoDeferredRelease(self->mNativesToReleaseArray);
|
||||
for (uint32_t i = 0; i < self->mDeferredFinalizeFunctions.Length(); ++i) {
|
||||
if (void *data = self->mDeferredFinalizeFunctions[i].start())
|
||||
self->mDeferredFinalizeFunctions[i].run(UINT32_MAX, data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<JSGCCallback> callbacks(self->extraGCCallbacks);
|
||||
nsTArray<xpcGCCallback> callbacks(extraGCCallbacks);
|
||||
for (uint32_t i = 0; i < callbacks.Length(); ++i)
|
||||
callbacks[i](rt, status);
|
||||
callbacks[i](status);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
|
@ -1215,8 +1048,6 @@ void XPCJSRuntime::SystemIsBeingShutDown()
|
|||
|
||||
XPCJSRuntime::~XPCJSRuntime()
|
||||
{
|
||||
MOZ_ASSERT(!mReleaseRunnable);
|
||||
|
||||
JS::SetGCSliceCallback(Runtime(), mPrevGCSliceCallback);
|
||||
|
||||
xpc_DelocalizeRuntime(Runtime());
|
||||
|
@ -2689,7 +2520,6 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
|||
JS_SetContextCallback(runtime, ContextCallback);
|
||||
JS_SetDestroyCompartmentCallback(runtime, CompartmentDestroyedCallback);
|
||||
JS_SetCompartmentNameCallback(runtime, CompartmentNameCallback);
|
||||
JS_SetGCCallback(runtime, GCCallback);
|
||||
mPrevGCSliceCallback = JS::SetGCSliceCallback(runtime, GCSliceCallback);
|
||||
JS_SetFinalizeCallback(runtime, FinalizeCallback);
|
||||
JS_SetWrapObjectCallbacks(runtime,
|
||||
|
@ -2871,20 +2701,6 @@ XPCJSRuntime::NoteCustomGCThingXPCOMChildren(js::Class* clasp, JSObject* obj,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
XPCJSRuntime::DeferredRelease(nsISupports *obj)
|
||||
{
|
||||
MOZ_ASSERT(obj);
|
||||
|
||||
if (mNativesToReleaseArray.IsEmpty()) {
|
||||
// This array sometimes has 1000's
|
||||
// of entries, and usually has 50-200 entries. Avoid lots
|
||||
// of incremental grows. We compact it down when we're done.
|
||||
mNativesToReleaseArray.SetCapacity(256);
|
||||
}
|
||||
return mNativesToReleaseArray.AppendElement(obj) != nullptr;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -3017,14 +2833,14 @@ XPCRootSetElem::RemoveFromRootSet(XPCLock *lock)
|
|||
}
|
||||
|
||||
void
|
||||
XPCJSRuntime::AddGCCallback(JSGCCallback cb)
|
||||
XPCJSRuntime::AddGCCallback(xpcGCCallback cb)
|
||||
{
|
||||
NS_ASSERTION(cb, "null callback");
|
||||
extraGCCallbacks.AppendElement(cb);
|
||||
}
|
||||
|
||||
void
|
||||
XPCJSRuntime::RemoveGCCallback(JSGCCallback cb)
|
||||
XPCJSRuntime::RemoveGCCallback(xpcGCCallback cb)
|
||||
{
|
||||
NS_ASSERTION(cb, "null callback");
|
||||
bool found = extraGCCallbacks.RemoveElement(cb);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "xpcprivate.h"
|
||||
#include "nsCxPusher.h"
|
||||
#include "nsAtomicRefcnt.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsTextFormatter.h"
|
||||
|
@ -508,7 +509,7 @@ nsXPCWrappedJS::Unlink()
|
|||
if (mOuter) {
|
||||
XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
|
||||
if (rt->GetThreadRunningGC()) {
|
||||
rt->DeferredRelease(mOuter);
|
||||
nsContentUtils::DeferredFinalize(mOuter);
|
||||
mOuter = nullptr;
|
||||
} else {
|
||||
NS_RELEASE(mOuter);
|
||||
|
|
|
@ -824,13 +824,8 @@ XPCWrappedNative::Destroy()
|
|||
if (mIdentity) {
|
||||
XPCJSRuntime* rt = GetRuntime();
|
||||
if (rt && rt->GetDoingFinalization()) {
|
||||
if (rt->DeferredRelease(mIdentity)) {
|
||||
mIdentity = nullptr;
|
||||
} else {
|
||||
NS_WARNING("Failed to append object for deferred release.");
|
||||
// XXX do we really want to do this???
|
||||
NS_RELEASE(mIdentity);
|
||||
}
|
||||
nsContentUtils::DeferredFinalize(mIdentity);
|
||||
mIdentity = nullptr;
|
||||
} else {
|
||||
NS_RELEASE(mIdentity);
|
||||
}
|
||||
|
@ -1168,11 +1163,7 @@ XPCWrappedNative::FlatJSObjectFinalized()
|
|||
#endif
|
||||
XPCJSRuntime* rt = GetRuntime();
|
||||
if (rt) {
|
||||
if (!rt->DeferredRelease(obj)) {
|
||||
NS_WARNING("Failed to append object for deferred release.");
|
||||
// XXX do we really want to do this???
|
||||
obj->Release();
|
||||
}
|
||||
nsContentUtils::DeferredFinalize(obj);
|
||||
} else {
|
||||
obj->Release();
|
||||
}
|
||||
|
|
|
@ -1156,16 +1156,16 @@ nsXPConnect::GetRuntime(JSRuntime **runtime)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/* [noscript, notxpcom] void registerGCCallback(in JSGCCallback func); */
|
||||
/* [noscript, notxpcom] void registerGCCallback(in xpcGCCallback func); */
|
||||
NS_IMETHODIMP_(void)
|
||||
nsXPConnect::RegisterGCCallback(JSGCCallback func)
|
||||
nsXPConnect::RegisterGCCallback(xpcGCCallback func)
|
||||
{
|
||||
mRuntime->AddGCCallback(func);
|
||||
}
|
||||
|
||||
/* [noscript, notxpcom] void unregisterGCCallback(in JSGCCallback func); */
|
||||
/* [noscript, notxpcom] void unregisterGCCallback(in xpcGCCallback func); */
|
||||
NS_IMETHODIMP_(void)
|
||||
nsXPConnect::UnregisterGCCallback(JSGCCallback func)
|
||||
nsXPConnect::UnregisterGCCallback(xpcGCCallback func)
|
||||
{
|
||||
mRuntime->RemoveGCCallback(func);
|
||||
}
|
||||
|
@ -1317,7 +1317,8 @@ namespace xpc {
|
|||
bool
|
||||
DeferredRelease(nsISupports *obj)
|
||||
{
|
||||
return nsXPConnect::GetRuntimeInstance()->DeferredRelease(obj);
|
||||
nsContentUtils::DeferredFinalize(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_EXPORT_(bool)
|
||||
|
|
|
@ -596,7 +596,6 @@ public:
|
|||
// So, xpconnect can only be used on one JSRuntime within the process.
|
||||
|
||||
class XPCJSContextStack;
|
||||
class XPCIncrementalReleaseRunnable;
|
||||
class XPCJSRuntime : public mozilla::CycleCollectedJSRuntime
|
||||
{
|
||||
public:
|
||||
|
@ -663,37 +662,13 @@ public:
|
|||
NoteCustomGCThingXPCOMChildren(js::Class* aClasp, JSObject* aObj,
|
||||
nsCycleCollectionTraversalCallback& aCb) const;
|
||||
|
||||
bool DeferredRelease(nsISupports* obj);
|
||||
|
||||
|
||||
/**
|
||||
* Infrastructure for classes that need to defer part of the finalization
|
||||
* until after the GC has run, for example for objects that we don't want to
|
||||
* destroy during the GC.
|
||||
*/
|
||||
|
||||
private:
|
||||
struct DeferredFinalizeFunctions
|
||||
{
|
||||
DeferredFinalizeStartFunction start;
|
||||
DeferredFinalizeFunction run;
|
||||
};
|
||||
nsAutoTArray<DeferredFinalizeFunctions, 16> mDeferredFinalizeFunctions;
|
||||
|
||||
public:
|
||||
// Register deferred finalization functions. Should only be called once per
|
||||
// pair of start and run.
|
||||
bool RegisterDeferredFinalize(DeferredFinalizeStartFunction start,
|
||||
DeferredFinalizeFunction run)
|
||||
{
|
||||
DeferredFinalizeFunctions* item =
|
||||
mDeferredFinalizeFunctions.AppendElement();
|
||||
item->start = start;
|
||||
item->run = run;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
JSBool GetDoingFinalization() const {return mDoingFinalization;}
|
||||
|
||||
// Mapping of often used strings to jsid atoms that live 'forever'.
|
||||
|
@ -744,7 +719,7 @@ public:
|
|||
void PrepareForForgetSkippable() MOZ_OVERRIDE;
|
||||
void PrepareForCollection() MOZ_OVERRIDE;
|
||||
|
||||
static void GCCallback(JSRuntime *rt, JSGCStatus status);
|
||||
void CustomGCCallback(JSGCStatus status) MOZ_OVERRIDE;
|
||||
static void GCSliceCallback(JSRuntime *rt,
|
||||
JS::GCProgress progress,
|
||||
const JS::GCDescription &desc);
|
||||
|
@ -829,8 +804,8 @@ private:
|
|||
public:
|
||||
#endif
|
||||
|
||||
void AddGCCallback(JSGCCallback cb);
|
||||
void RemoveGCCallback(JSGCCallback cb);
|
||||
void AddGCCallback(xpcGCCallback cb);
|
||||
void RemoveGCCallback(xpcGCCallback cb);
|
||||
|
||||
static void ActivityCallback(void *arg, JSBool active);
|
||||
static void CTypesActivityCallback(JSContext *cx,
|
||||
|
@ -884,11 +859,10 @@ private:
|
|||
PRLock *mWatchdogLock;
|
||||
PRCondVar *mWatchdogWakeup;
|
||||
PRThread *mWatchdogThread;
|
||||
nsTArray<JSGCCallback> extraGCCallbacks;
|
||||
nsTArray<xpcGCCallback> extraGCCallbacks;
|
||||
bool mWatchdogHibernating;
|
||||
enum { RUNTIME_ACTIVE, RUNTIME_INACTIVE } mRuntimeState;
|
||||
PRTime mTimeAtLastRuntimeStateChange;
|
||||
nsRefPtr<XPCIncrementalReleaseRunnable> mReleaseRunnable;
|
||||
JS::GCSliceCallback mPrevGCSliceCallback;
|
||||
JSObject* mJunkScope;
|
||||
|
||||
|
|
|
@ -240,8 +240,6 @@ private:
|
|||
|
||||
namespace xpc {
|
||||
|
||||
bool DeferredRelease(nsISupports *obj);
|
||||
|
||||
// If these functions return false, then an exception will be set on cx.
|
||||
NS_EXPORT_(bool) Base64Encode(JSContext *cx, JS::Value val, JS::Value *out);
|
||||
NS_EXPORT_(bool) Base64Decode(JSContext *cx, JS::Value val, JS::Value *out);
|
||||
|
@ -459,14 +457,4 @@ Register(nsScriptNameSpaceManager* aNameSpaceManager);
|
|||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
// Called once before the deferred finalization starts. Should hand off the
|
||||
// buffer with things to finalize in the return value.
|
||||
typedef void* (*DeferredFinalizeStartFunction)();
|
||||
|
||||
// Called to finalize a number of objects. Slice is the number of objects
|
||||
// to finalize, or if it's UINT32_MAX, all objects should be finalized.
|
||||
// data is the pointer returned by DeferredFinalizeStartFunction.
|
||||
// Return value indicates whether it finalized all objects in the buffer.
|
||||
typedef bool (*DeferredFinalizeFunction)(uint32_t slice, void* data);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
// traversed.
|
||||
|
||||
#include "mozilla/CycleCollectedJSRuntime.h"
|
||||
#include <algorithm>
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/DOMJSClass.h"
|
||||
|
@ -70,6 +71,44 @@
|
|||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct DeferredFinalizeFunctionHolder
|
||||
{
|
||||
DeferredFinalizeFunction run;
|
||||
void *data;
|
||||
};
|
||||
|
||||
class IncrementalFinalizeRunnable : public nsRunnable
|
||||
{
|
||||
typedef nsAutoTArray<DeferredFinalizeFunctionHolder, 16> DeferredFinalizeArray;
|
||||
typedef CycleCollectedJSRuntime::DeferredFinalizerTable DeferredFinalizerTable;
|
||||
|
||||
CycleCollectedJSRuntime* mRuntime;
|
||||
nsTArray<nsISupports*> mSupports;
|
||||
DeferredFinalizeArray mDeferredFinalizeFunctions;
|
||||
uint32_t mFinalizeFunctionToRun;
|
||||
|
||||
static const PRTime SliceMillis = 10; /* ms */
|
||||
|
||||
static PLDHashOperator
|
||||
DeferredFinalizerEnumerator(DeferredFinalizeFunction& aFunction,
|
||||
void*& aData,
|
||||
void* aClosure);
|
||||
|
||||
public:
|
||||
IncrementalFinalizeRunnable(CycleCollectedJSRuntime* aRt,
|
||||
nsTArray<nsISupports*>& mSupports,
|
||||
DeferredFinalizerTable& aFinalizerTable);
|
||||
virtual ~IncrementalFinalizeRunnable();
|
||||
|
||||
void ReleaseNow(bool aLimited);
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
inline bool
|
||||
AddToCCKind(JSGCTraceKind kind)
|
||||
{
|
||||
|
@ -468,14 +507,20 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(uint32_t aMaxbytes,
|
|||
MOZ_CRASH();
|
||||
}
|
||||
JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
|
||||
JS_SetGCCallback(mJSRuntime, GCCallback, this);
|
||||
|
||||
mJSHolders.Init(512);
|
||||
|
||||
nsCycleCollector_registerJSRuntime(this);
|
||||
|
||||
mDeferredFinalizerTable.Init();
|
||||
}
|
||||
|
||||
CycleCollectedJSRuntime::~CycleCollectedJSRuntime()
|
||||
{
|
||||
MOZ_ASSERT(!mDeferredFinalizerTable.Count());
|
||||
MOZ_ASSERT(!mDeferredSupports.Length());
|
||||
|
||||
nsCycleCollector_forgetJSRuntime();
|
||||
|
||||
JS_DestroyRuntime(mJSRuntime);
|
||||
|
@ -751,6 +796,18 @@ CycleCollectedJSRuntime::TraceGrayJS(JSTracer* aTracer, void* aData)
|
|||
self->TraceNativeGrayRoots(aTracer);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
CycleCollectedJSRuntime::GCCallback(JSRuntime* aRuntime,
|
||||
JSGCStatus aStatus,
|
||||
void* aData)
|
||||
{
|
||||
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
|
||||
|
||||
MOZ_ASSERT(aRuntime == self->Runtime());
|
||||
|
||||
self->OnGC(aStatus);
|
||||
}
|
||||
|
||||
struct JsGcTracer : public TraceCallbacks
|
||||
{
|
||||
virtual void Trace(JS::Heap<JS::Value> *p, const char *name, void *closure) const MOZ_OVERRIDE {
|
||||
|
@ -968,3 +1025,197 @@ CycleCollectedJSRuntime::Collect(uint32_t aReason) const
|
|||
JS::PrepareForFullGC(mJSRuntime);
|
||||
JS::GCForReason(mJSRuntime, gcreason);
|
||||
}
|
||||
|
||||
void
|
||||
CycleCollectedJSRuntime::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
|
||||
DeferredFinalizeFunction aFunc,
|
||||
void* aThing)
|
||||
{
|
||||
void* thingArray = nullptr;
|
||||
bool hadThingArray = mDeferredFinalizerTable.Get(aFunc, &thingArray);
|
||||
|
||||
thingArray = aAppendFunc(thingArray, aThing);
|
||||
if (!hadThingArray) {
|
||||
mDeferredFinalizerTable.Put(aFunc, thingArray);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CycleCollectedJSRuntime::DeferredFinalize(nsISupports* aSupports)
|
||||
{
|
||||
mDeferredSupports.AppendElement(aSupports);
|
||||
}
|
||||
|
||||
bool
|
||||
ReleaseSliceNow(uint32_t aSlice, void* aData)
|
||||
{
|
||||
MOZ_ASSERT(aSlice > 0, "nonsensical/useless call with slice == 0");
|
||||
nsTArray<nsISupports*>* items = static_cast<nsTArray<nsISupports*>*>(aData);
|
||||
|
||||
uint32_t length = items->Length();
|
||||
aSlice = std::min(aSlice, length);
|
||||
for (uint32_t i = length; i > length - aSlice; --i) {
|
||||
// Remove (and NS_RELEASE) the last entry in "items":
|
||||
uint32_t lastItemIdx = i - 1;
|
||||
|
||||
nsISupports* wrapper = items->ElementAt(lastItemIdx);
|
||||
items->RemoveElementAt(lastItemIdx);
|
||||
NS_RELEASE(wrapper);
|
||||
}
|
||||
|
||||
return items->IsEmpty();
|
||||
}
|
||||
|
||||
/* static */ PLDHashOperator
|
||||
IncrementalFinalizeRunnable::DeferredFinalizerEnumerator(DeferredFinalizeFunction& aFunction,
|
||||
void*& aData,
|
||||
void* aClosure)
|
||||
{
|
||||
DeferredFinalizeArray* array = static_cast<DeferredFinalizeArray*>(aClosure);
|
||||
|
||||
DeferredFinalizeFunctionHolder* function = array->AppendElement();
|
||||
function->run = aFunction;
|
||||
function->data = aData;
|
||||
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSRuntime* aRt,
|
||||
nsTArray<nsISupports*>& aSupports,
|
||||
DeferredFinalizerTable& aFinalizers)
|
||||
: mRuntime(aRt),
|
||||
mFinalizeFunctionToRun(0)
|
||||
{
|
||||
this->mSupports.SwapElements(aSupports);
|
||||
DeferredFinalizeFunctionHolder* function = mDeferredFinalizeFunctions.AppendElement();
|
||||
function->run = ReleaseSliceNow;
|
||||
function->data = &this->mSupports;
|
||||
|
||||
// Enumerate the hashtable into our array.
|
||||
aFinalizers.Enumerate(DeferredFinalizerEnumerator, &mDeferredFinalizeFunctions);
|
||||
}
|
||||
|
||||
IncrementalFinalizeRunnable::~IncrementalFinalizeRunnable()
|
||||
{
|
||||
MOZ_ASSERT(this != mRuntime->mFinalizeRunnable);
|
||||
}
|
||||
|
||||
void
|
||||
IncrementalFinalizeRunnable::ReleaseNow(bool aLimited)
|
||||
{
|
||||
//MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mDeferredFinalizeFunctions.Length() != 0,
|
||||
"We should have at least ReleaseSliceNow to run");
|
||||
MOZ_ASSERT(mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length(),
|
||||
"No more finalizers to run?");
|
||||
|
||||
TimeDuration sliceTime = TimeDuration::FromMilliseconds(SliceMillis);
|
||||
TimeStamp started = TimeStamp::Now();
|
||||
bool timeout = false;
|
||||
do {
|
||||
const DeferredFinalizeFunctionHolder &function =
|
||||
mDeferredFinalizeFunctions[mFinalizeFunctionToRun];
|
||||
if (aLimited) {
|
||||
bool done = false;
|
||||
while (!timeout && !done) {
|
||||
/*
|
||||
* We don't want to read the clock too often, so we try to
|
||||
* release slices of 100 items.
|
||||
*/
|
||||
done = function.run(100, function.data);
|
||||
timeout = TimeStamp::Now() - started >= sliceTime;
|
||||
}
|
||||
if (done) {
|
||||
++mFinalizeFunctionToRun;
|
||||
}
|
||||
if (timeout) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
function.run(UINT32_MAX, function.data);
|
||||
++mFinalizeFunctionToRun;
|
||||
}
|
||||
} while (mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length());
|
||||
|
||||
if (mFinalizeFunctionToRun == mDeferredFinalizeFunctions.Length()) {
|
||||
MOZ_ASSERT(mRuntime->mFinalizeRunnable == this);
|
||||
mDeferredFinalizeFunctions.Clear();
|
||||
// NB: This may delete this!
|
||||
mRuntime->mFinalizeRunnable = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IncrementalFinalizeRunnable::Run()
|
||||
{
|
||||
if (mRuntime->mFinalizeRunnable != this) {
|
||||
/* These items were already processed synchronously in JSGC_END. */
|
||||
MOZ_ASSERT(!mSupports.Length());
|
||||
MOZ_ASSERT(!mDeferredFinalizeFunctions.Length());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ReleaseNow(true);
|
||||
|
||||
if (mDeferredFinalizeFunctions.Length()) {
|
||||
nsresult rv = NS_DispatchToCurrentThread(this);
|
||||
if (NS_FAILED(rv)) {
|
||||
ReleaseNow(false);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
CycleCollectedJSRuntime::FinalizeDeferredThings(DeferredFinalizeType aType)
|
||||
{
|
||||
MOZ_ASSERT(!mFinalizeRunnable);
|
||||
mFinalizeRunnable = new IncrementalFinalizeRunnable(this,
|
||||
mDeferredSupports,
|
||||
mDeferredFinalizerTable);
|
||||
|
||||
// Everything should be gone now.
|
||||
MOZ_ASSERT(!mDeferredSupports.Length());
|
||||
MOZ_ASSERT(!mDeferredFinalizerTable.Count());
|
||||
|
||||
if (aType == FinalizeIncrementally) {
|
||||
NS_DispatchToCurrentThread(mFinalizeRunnable);
|
||||
} else {
|
||||
mFinalizeRunnable->ReleaseNow(false);
|
||||
MOZ_ASSERT(!mFinalizeRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus)
|
||||
{
|
||||
switch (aStatus) {
|
||||
case JSGC_BEGIN:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case JSGC_END:
|
||||
{
|
||||
/*
|
||||
* If the previous GC created a runnable to finalize objects
|
||||
* incrementally, and if it hasn't finished yet, finish it now. We
|
||||
* don't want these to build up. We also don't want to allow any
|
||||
* existing incremental finalize runnables to run after a
|
||||
* non-incremental GC, since they are often used to detect leaks.
|
||||
*/
|
||||
if (mFinalizeRunnable) {
|
||||
mFinalizeRunnable->ReleaseNow(false);
|
||||
}
|
||||
|
||||
// Do any deferred finalization of native objects.
|
||||
FinalizeDeferredThings(JS::WasIncrementalGC(mJSRuntime) ? FinalizeIncrementally :
|
||||
FinalizeNow);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
CustomGCCallback(aStatus);
|
||||
}
|
||||
|
|
|
@ -11,9 +11,11 @@
|
|||
#include "jsprvtd.h"
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "nsCycleCollector.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsCycleCollectionNoteRootCallback;
|
||||
class nsScriptObjectTracer;
|
||||
|
@ -73,10 +75,13 @@ public:
|
|||
nsCycleCollectionTraversalCallback &cb);
|
||||
};
|
||||
|
||||
class IncrementalFinalizeRunnable;
|
||||
|
||||
class CycleCollectedJSRuntime
|
||||
{
|
||||
friend class JSGCThingParticipant;
|
||||
friend class JSZoneParticipant;
|
||||
friend class IncrementalFinalizeRunnable;
|
||||
protected:
|
||||
CycleCollectedJSRuntime(uint32_t aMaxbytes,
|
||||
JSUseHelperThreads aUseHelperThreads,
|
||||
|
@ -95,6 +100,8 @@ protected:
|
|||
virtual void TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& aCb) = 0;
|
||||
virtual void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) = 0;
|
||||
|
||||
virtual void CustomGCCallback(JSGCStatus aStatus) {}
|
||||
|
||||
private:
|
||||
|
||||
void
|
||||
|
@ -143,10 +150,20 @@ private:
|
|||
|
||||
static void TraceBlackJS(JSTracer* aTracer, void* aData);
|
||||
static void TraceGrayJS(JSTracer* aTracer, void* aData);
|
||||
static void GCCallback(JSRuntime* aRuntime, JSGCStatus aStatus, void* aData);
|
||||
|
||||
virtual void TraceNativeBlackRoots(JSTracer* aTracer) { };
|
||||
void TraceNativeGrayRoots(JSTracer* aTracer);
|
||||
|
||||
enum DeferredFinalizeType {
|
||||
FinalizeIncrementally,
|
||||
FinalizeNow,
|
||||
};
|
||||
|
||||
void FinalizeDeferredThings(DeferredFinalizeType aType);
|
||||
|
||||
void OnGC(JSGCStatus aStatus);
|
||||
|
||||
public:
|
||||
void AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer);
|
||||
void RemoveJSHolder(void* aHolder);
|
||||
|
@ -175,6 +192,11 @@ public:
|
|||
virtual void PrepareForForgetSkippable() {}
|
||||
virtual void PrepareForCollection() {}
|
||||
|
||||
void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
|
||||
DeferredFinalizeFunction aFunc,
|
||||
void* aThing);
|
||||
void DeferredFinalize(nsISupports* aSupports);
|
||||
|
||||
private:
|
||||
typedef const CCParticipantVTable<JSGCThingParticipant>::Type GCThingParticipantVTable;
|
||||
const GCThingParticipantVTable mGCThingCycleCollectorGlobal;
|
||||
|
@ -186,6 +208,13 @@ private:
|
|||
|
||||
nsDataHashtable<nsPtrHashKey<void>, nsScriptObjectTracer*> mJSHolders;
|
||||
|
||||
nsTArray<nsISupports*> mDeferredSupports;
|
||||
typedef nsDataHashtable<nsFuncPtrHashKey<DeferredFinalizeFunction>, void*>
|
||||
DeferredFinalizerTable;
|
||||
DeferredFinalizerTable mDeferredFinalizerTable;
|
||||
|
||||
nsRefPtr<IncrementalFinalizeRunnable> mFinalizeRunnable;
|
||||
|
||||
#ifdef DEBUG
|
||||
void* mObjectToUnlink;
|
||||
bool mExpectUnrootedGlobals;
|
||||
|
|
|
@ -106,7 +106,6 @@
|
|||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsCycleCollectionNoteRootCallback.h"
|
||||
#include "nsCycleCollectorUtils.h"
|
||||
#include "nsIProgrammingLanguage.h"
|
||||
#include "nsBaseHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsDeque.h"
|
||||
|
@ -118,13 +117,10 @@
|
|||
#include "nsPrintfCString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsICycleCollectorListener.h"
|
||||
#include "nsIXPConnect.h"
|
||||
#include "nsIJSRuntimeService.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
|
@ -3030,6 +3026,36 @@ cyclecollector::TestJSHolder(void* aHolder)
|
|||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
cyclecollector::DeferredFinalize(nsISupports* aSupports)
|
||||
{
|
||||
CollectorData *data = sCollectorData.get();
|
||||
|
||||
// We should have started the cycle collector by now, and not completely
|
||||
// shut down.
|
||||
MOZ_ASSERT(data);
|
||||
// And we should have a runtime.
|
||||
MOZ_ASSERT(data->mRuntime);
|
||||
|
||||
data->mRuntime->DeferredFinalize(aSupports);
|
||||
}
|
||||
|
||||
void
|
||||
cyclecollector::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
|
||||
DeferredFinalizeFunction aFunc,
|
||||
void* aThing)
|
||||
{
|
||||
CollectorData *data = sCollectorData.get();
|
||||
|
||||
// We should have started the cycle collector by now, and not completely
|
||||
// shut down.
|
||||
MOZ_ASSERT(data);
|
||||
// And we should have a runtime.
|
||||
MOZ_ASSERT(data->mRuntime);
|
||||
|
||||
data->mRuntime->DeferredFinalize(aAppendFunc, aFunc, aThing);
|
||||
}
|
||||
|
||||
nsPurpleBufferEntry*
|
||||
NS_CycleCollectorSuspect2(void *n, nsCycleCollectionParticipant *cp)
|
||||
{
|
||||
|
|
|
@ -10,8 +10,17 @@ class nsICycleCollectorListener;
|
|||
class nsISupports;
|
||||
class nsScriptObjectTracer;
|
||||
|
||||
#include "nsError.h"
|
||||
#include "nsID.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class CycleCollectedJSRuntime;
|
||||
|
||||
// See the comments in nsContentUtils.h for explanations of these functions.
|
||||
typedef void* (*DeferredFinalizeAppendFunction)(void* pointers, void* thing);
|
||||
typedef bool (*DeferredFinalizeFunction)(uint32_t slice, void* data);
|
||||
|
||||
}
|
||||
|
||||
// Contains various stats about the cycle collection.
|
||||
|
@ -76,6 +85,12 @@ void RemoveJSHolder(void* aHolder);
|
|||
bool TestJSHolder(void* aHolder);
|
||||
#endif
|
||||
|
||||
void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
|
||||
DeferredFinalizeFunction aFunc,
|
||||
void* aThing);
|
||||
void DeferredFinalize(nsISupports* aSupports);
|
||||
|
||||
|
||||
} // namespace cyclecollector
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -361,6 +361,37 @@ class nsClearingPtrHashKey : public nsPtrHashKey<T>
|
|||
typedef nsPtrHashKey<const void> nsVoidPtrHashKey;
|
||||
typedef nsClearingPtrHashKey<const void> nsClearingVoidPtrHashKey;
|
||||
|
||||
/**
|
||||
* hashkey wrapper using a function pointer KeyType
|
||||
*
|
||||
* @see nsTHashtable::EntryType for specification
|
||||
*/
|
||||
template<class T>
|
||||
class nsFuncPtrHashKey : public PLDHashEntryHdr
|
||||
{
|
||||
public:
|
||||
typedef T &KeyType;
|
||||
typedef const T *KeyTypePointer;
|
||||
|
||||
nsFuncPtrHashKey(const T *key) : mKey(*const_cast<T*>(key)) {}
|
||||
nsFuncPtrHashKey(const nsFuncPtrHashKey<T> &toCopy) : mKey(toCopy.mKey) {}
|
||||
~nsFuncPtrHashKey() {}
|
||||
|
||||
KeyType GetKey() const { return const_cast<T&>(mKey); }
|
||||
|
||||
bool KeyEquals(KeyTypePointer key) const { return *key == mKey; }
|
||||
|
||||
static KeyTypePointer KeyToPointer(KeyType key) { return &key; }
|
||||
static PLDHashNumber HashKey(KeyTypePointer key)
|
||||
{
|
||||
return NS_PTR_TO_INT32(*key) >> 2;
|
||||
}
|
||||
enum { ALLOW_MEMMOVE = true };
|
||||
|
||||
protected:
|
||||
T mKey;
|
||||
};
|
||||
|
||||
/**
|
||||
* hashkey wrapper using nsID KeyType
|
||||
*
|
||||
|
|
Загрузка…
Ссылка в новой задаче