Bug 453432 - Checking for MaybeGC conditions when allocating GC things.

r=igor,mrbkap a191=blocker
This commit is contained in:
Andrei Saprykin 2008-11-26 18:09:24 +01:00
Родитель 00579d3fe4
Коммит 00001c7857
9 изменённых файлов: 199 добавлений и 136 удалений

Просмотреть файл

@ -850,24 +850,8 @@ PrintWinCodebase(nsGlobalWindow *win)
} }
#endif #endif
// The accumulated operation weight before we call MaybeGC // The accumulated operation weight for DOM callback.
const PRUint32 MAYBE_GC_OPERATION_WEIGHT = 5000 * JS_OPERATION_WEIGHT_BASE; const PRUint32 DOM_CALLBACK_OPERATION_WEIGHT = 5000 * JS_OPERATION_WEIGHT_BASE;
static void
MaybeGC(JSContext *cx)
{
size_t bytes = cx->runtime->gcBytes;
size_t lastBytes = cx->runtime->gcLastBytes;
if ((bytes > 8192 && bytes / 16 > lastBytes)
#ifdef DEBUG
|| cx->runtime->gcZeal > 0
#endif
) {
++sGCCount;
JS_GC(cx);
}
}
static already_AddRefed<nsIPrompt> static already_AddRefed<nsIPrompt>
GetPromptFromContext(nsJSContext* ctx) GetPromptFromContext(nsJSContext* ctx)
@ -900,17 +884,8 @@ nsJSContext::DOMOperationCallback(JSContext *cx)
return JS_TRUE; return JS_TRUE;
} }
// XXX Save the operation callback time so we can restore it after the GC,
// because GCing can cause JS to run on our context, causing our
// ScriptEvaluated to be called, and clearing our operation callback time.
// See bug 302333.
PRTime callbackTime = ctx->mOperationCallbackTime; PRTime callbackTime = ctx->mOperationCallbackTime;
MaybeGC(cx);
// Now restore the callback time and count, in case they got reset.
ctx->mOperationCallbackTime = callbackTime;
// Check to see if we are running OOM // Check to see if we are running OOM
nsCOMPtr<nsIMemory> mem; nsCOMPtr<nsIMemory> mem;
NS_GetMemoryManager(getter_AddRefs(mem)); NS_GetMemoryManager(getter_AddRefs(mem));
@ -932,24 +907,24 @@ nsJSContext::DOMOperationCallback(JSContext *cx)
if (nsContentUtils::GetBoolPref("dom.prevent_oom_dialog", PR_FALSE)) if (nsContentUtils::GetBoolPref("dom.prevent_oom_dialog", PR_FALSE))
return JS_FALSE; return JS_FALSE;
nsCOMPtr<nsIPrompt> prompt = GetPromptFromContext(ctx); nsCOMPtr<nsIPrompt> prompt = GetPromptFromContext(ctx);
nsXPIDLString title, msg; nsXPIDLString title, msg;
rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
"LowMemoryTitle", "LowMemoryTitle",
title); title);
rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
"LowMemoryMessage", "LowMemoryMessage",
msg); msg);
//GetStringFromName can return NS_OK and still give NULL string //GetStringFromName can return NS_OK and still give NULL string
if (NS_FAILED(rv) || !title || !msg) { if (NS_FAILED(rv) || !title || !msg) {
NS_ERROR("Failed to get localized strings."); NS_ERROR("Failed to get localized strings.");
return JS_FALSE; return JS_FALSE;
} }
prompt->Alert(title, msg); prompt->Alert(title, msg);
return JS_FALSE; return JS_FALSE;
} }
@ -1238,7 +1213,7 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime) : mGCOnDestruction(PR_TRUE)
this); this);
::JS_SetOperationCallback(mContext, DOMOperationCallback, ::JS_SetOperationCallback(mContext, DOMOperationCallback,
MAYBE_GC_OPERATION_WEIGHT); DOM_CALLBACK_OPERATION_WEIGHT);
static JSLocaleCallbacks localeCallbacks = static JSLocaleCallbacks localeCallbacks =
{ {
@ -1251,7 +1226,6 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime) : mGCOnDestruction(PR_TRUE)
::JS_SetLocaleCallbacks(mContext, &localeCallbacks); ::JS_SetLocaleCallbacks(mContext, &localeCallbacks);
} }
mIsInitialized = PR_FALSE; mIsInitialized = PR_FALSE;
mNumEvaluations = 0;
mTerminations = nsnull; mTerminations = nsnull;
mScriptsEnabled = PR_TRUE; mScriptsEnabled = PR_TRUE;
mOperationCallbackTime = LL_ZERO; mOperationCallbackTime = LL_ZERO;
@ -3294,21 +3268,7 @@ nsJSContext::ScriptEvaluated(PRBool aTerminated)
delete start; delete start;
} }
mNumEvaluations++; mOperationCallbackTime = LL_ZERO;
#ifdef JS_GC_ZEAL
if (mContext->runtime->gcZeal >= 2) {
MaybeGC(mContext);
} else
#endif
if (mNumEvaluations > 20) {
mNumEvaluations = 0;
MaybeGC(mContext);
}
if (aTerminated) {
mOperationCallbackTime = LL_ZERO;
}
} }
nsresult nsresult
@ -3392,19 +3352,29 @@ nsJSContext::CC()
#endif #endif
sPreviousCCTime = PR_Now(); sPreviousCCTime = PR_Now();
sDelayedCCollectCount = 0; sDelayedCCollectCount = 0;
sGCCount = 0; sGCCount = JS_GetGCParameter(nsJSRuntime::sRuntime, JSGC_NUMBER);
sCCSuspectChanges = 0; sCCSuspectChanges = 0;
// nsCycleCollector_collect() will run a ::JS_GC() indirectly, so // nsCycleCollector_collect() will run a ::JS_GC() indirectly, so
// we do not explicitly call ::JS_GC() here. // we do not explicitly call ::JS_GC() here.
sCollectedObjectsCounts = nsCycleCollector_collect(); sCollectedObjectsCounts = nsCycleCollector_collect();
sCCSuspectedCount = nsCycleCollector_suspectedCount(); sCCSuspectedCount = nsCycleCollector_suspectedCount();
#ifdef DEBUG_smaug #ifdef DEBUG_smaug
printf("Collected %u objects, %u suspected objects, took %lldms\n", printf("Collected %u objects, %u suspected objects\n",
sCollectedObjectsCounts, sCCSuspectedCount, sCollectedObjectsCounts, sCCSuspectedCount);
(PR_Now() - sPreviousCCTime) / PR_USEC_PER_MSEC);
#endif #endif
} }
static inline uint32
GetGCRunsCount()
{
/*
* The result value may overflow if sGCCount is close to the uint32
* maximum. It may cause additional invocations of the CC, which may
* reduce performance but cannot breach security.
*/
return JS_GetGCParameter(nsJSRuntime::sRuntime, JSGC_NUMBER) - sGCCount;
}
//static //static
PRBool PRBool
nsJSContext::MaybeCC(PRBool aHigherProbability) nsJSContext::MaybeCC(PRBool aHigherProbability)
@ -3413,7 +3383,7 @@ nsJSContext::MaybeCC(PRBool aHigherProbability)
// Don't check suspected count if CC will be called anyway. // Don't check suspected count if CC will be called anyway.
if (sCCSuspectChanges <= NS_MIN_SUSPECT_CHANGES || if (sCCSuspectChanges <= NS_MIN_SUSPECT_CHANGES ||
sGCCount <= NS_MAX_GC_COUNT) { GetGCRunsCount() <= NS_MAX_GC_COUNT) {
#ifdef DEBUG_smaug #ifdef DEBUG_smaug
PRTime now = PR_Now(); PRTime now = PR_Now();
#endif #endif
@ -3431,7 +3401,8 @@ nsJSContext::MaybeCC(PRBool aHigherProbability)
} }
#ifdef DEBUG_smaug #ifdef DEBUG_smaug
printf("sCCSuspectChanges %u, sGCCount %u\n", printf("sCCSuspectChanges %u, sGCCount %u\n",
sCCSuspectChanges, sGCCount); sCCSuspectChanges,
GetGCRunsCount());
#endif #endif
// Increase the probability also if the previous call to cycle collector // Increase the probability also if the previous call to cycle collector
@ -3444,7 +3415,7 @@ nsJSContext::MaybeCC(PRBool aHigherProbability)
if (!sGCTimer && if (!sGCTimer &&
(sDelayedCCollectCount > NS_MAX_DELAYED_CCOLLECT) && (sDelayedCCollectCount > NS_MAX_DELAYED_CCOLLECT) &&
((sCCSuspectChanges > NS_MIN_SUSPECT_CHANGES && ((sCCSuspectChanges > NS_MIN_SUSPECT_CHANGES &&
sGCCount > NS_MAX_GC_COUNT) || GetGCRunsCount() > NS_MAX_GC_COUNT) ||
(sCCSuspectChanges > NS_MAX_SUSPECT_CHANGES))) { (sCCSuspectChanges > NS_MAX_SUSPECT_CHANGES))) {
if ((PR_Now() - sPreviousCCTime) >= if ((PR_Now() - sPreviousCCTime) >=
PRTime(NS_MIN_CC_INTERVAL * PR_USEC_PER_MSEC)) { PRTime(NS_MIN_CC_INTERVAL * PR_USEC_PER_MSEC)) {

Просмотреть файл

@ -213,19 +213,18 @@ protected:
// given an nsISupports object (presumably an event target or some other // given an nsISupports object (presumably an event target or some other
// DOM object), get (or create) the JSObject wrapping it. // DOM object), get (or create) the JSObject wrapping it.
nsresult JSObjectFromInterface(nsISupports *aSup, void *aScript, nsresult JSObjectFromInterface(nsISupports *aSup, void *aScript,
JSObject **aRet); JSObject **aRet);
private: private:
void Unlink(); void Unlink();
JSContext *mContext; JSContext *mContext;
PRUint32 mNumEvaluations;
protected: protected:
struct TerminationFuncHolder; struct TerminationFuncHolder;
friend struct TerminationFuncHolder; friend struct TerminationFuncHolder;
struct TerminationFuncClosure struct TerminationFuncClosure
{ {
TerminationFuncClosure(nsScriptTerminationFunc aFunc, TerminationFuncClosure(nsScriptTerminationFunc aFunc,
@ -240,7 +239,7 @@ protected:
{ {
delete mNext; delete mNext;
} }
nsScriptTerminationFunc mTerminationFunc; nsScriptTerminationFunc mTerminationFunc;
nsCOMPtr<nsISupports> mTerminationFuncArg; nsCOMPtr<nsISupports> mTerminationFuncArg;
TerminationFuncClosure* mNext; TerminationFuncClosure* mNext;
@ -273,7 +272,7 @@ protected:
nsJSContext* mContext; nsJSContext* mContext;
TerminationFuncClosure* mTerminations; TerminationFuncClosure* mTerminations;
}; };
TerminationFuncClosure* mTerminations; TerminationFuncClosure* mTerminations;
private: private:
@ -319,7 +318,7 @@ public:
virtual nsresult DropScriptObject(void *object); virtual nsresult DropScriptObject(void *object);
virtual nsresult HoldScriptObject(void *object); virtual nsresult HoldScriptObject(void *object);
static void Startup(); static void Startup();
static void Shutdown(); static void Shutdown();
// Setup all the statics etc - safe to call multiple times after Startup() // Setup all the statics etc - safe to call multiple times after Startup()

Просмотреть файл

@ -186,14 +186,8 @@ my_BranchCallback(JSContext *cx, JSScript *script)
return JS_FALSE; return JS_FALSE;
} }
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
if ((gBranchCount & 0xff) == 1) { if ((gBranchCount & 0xff) == 1)
#endif JS_YieldRequest(cx);
if ((gBranchCount & 0x3fff) == 1)
JS_MaybeGC(cx);
#ifdef JS_THREADSAFE
else
JS_YieldRequest(cx);
}
#endif #endif
return JS_TRUE; return JS_TRUE;
} }
@ -471,7 +465,7 @@ extern void js_InitJITStatsClass(JSContext *cx, JSObject *glob);
&jitstats_class, NULL, 0); &jitstats_class, NULL, 0);
#endif #endif
break; break;
case 'o': case 'o':
if (++i == argc) if (++i == argc)
return usage(); return usage();
@ -866,24 +860,49 @@ GCParameter(JSContext *cx, uintN argc, jsval *vp)
param = JSGC_MAX_BYTES; param = JSGC_MAX_BYTES;
} else if (strcmp(paramName, "maxMallocBytes") == 0) { } else if (strcmp(paramName, "maxMallocBytes") == 0) {
param = JSGC_MAX_MALLOC_BYTES; param = JSGC_MAX_MALLOC_BYTES;
} else if (strcmp(paramName, "gcStackpoolLifespan") == 0) {
param = JSGC_STACKPOOL_LIFESPAN;
} else if (strcmp(paramName, "gcBytes") == 0) {
param = JSGC_BYTES;
} else if (strcmp(paramName, "gcNumber") == 0) {
param = JSGC_NUMBER;
} else if (strcmp(paramName, "gcTriggerFactor") == 0) {
param = JSGC_TRIGGER_FACTOR;
} else { } else {
JS_ReportError(cx, JS_ReportError(cx,
"the first argument argument must be either maxBytes " "the first argument argument must be maxBytes, "
"or maxMallocBytes"); "maxMallocBytes, gcStackpoolLifespan, gcBytes, "
"gcNumber or gcTriggerFactor");
return JS_FALSE; return JS_FALSE;
} }
if (!JS_ValueToECMAUint32(cx, argc < 2 ? JSVAL_VOID : vp[3], &value)) if (argc == 1) {
value = JS_GetGCParameter(cx->runtime, param);
return JS_NewNumberValue(cx, value, &vp[0]);
}
if (param == JSGC_NUMBER ||
param == JSGC_BYTES) {
JS_ReportError(cx, "Attempt to change read-only parameter %s",
paramName);
return JS_FALSE; return JS_FALSE;
if (value == 0) { }
if (!JS_ValueToECMAUint32(cx, vp[3], &value)) {
JS_ReportError(cx, JS_ReportError(cx,
"the second argument must be convertable to uint32 with " "the second argument must be convertable to uint32 "
"non-zero value"); "with non-zero value");
return JS_FALSE;
}
if (param == JSGC_TRIGGER_FACTOR && value < 100) {
JS_ReportError(cx,
"the gcTriggerFactor value must be >= 100");
return JS_FALSE; return JS_FALSE;
} }
JS_SetGCParameter(cx->runtime, param, value); JS_SetGCParameter(cx->runtime, param, value);
*vp = JSVAL_VOID; *vp = JSVAL_VOID;
return JS_TRUE; return JS_TRUE;
} }
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
@ -1451,7 +1470,7 @@ DisassFile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JSScript *script; JSScript *script;
JSBool ok; JSBool ok;
uint32 oldopts; uint32 oldopts;
if (!argc) if (!argc)
return JS_TRUE; return JS_TRUE;
@ -1471,7 +1490,7 @@ DisassFile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
obj = JS_NewScriptObject(cx, script); obj = JS_NewScriptObject(cx, script);
if (!obj) if (!obj)
return JS_FALSE; return JS_FALSE;
*rval = OBJECT_TO_JSVAL(obj); /* I like to root it, root it. */ *rval = OBJECT_TO_JSVAL(obj); /* I like to root it, root it. */
ok = Disassemble(cx, obj, 1, rval, rval); /* gross, but works! */ ok = Disassemble(cx, obj, 1, rval, rval); /* gross, but works! */
*rval = JSVAL_VOID; *rval = JSVAL_VOID;

Просмотреть файл

@ -2593,6 +2593,31 @@ JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value)
case JSGC_STACKPOOL_LIFESPAN: case JSGC_STACKPOOL_LIFESPAN:
rt->gcEmptyArenaPoolLifespan = value; rt->gcEmptyArenaPoolLifespan = value;
break; break;
default:
JS_ASSERT(key == JSGC_TRIGGER_FACTOR);
JS_ASSERT(value >= 100);
rt->gcTriggerFactor = value;
return;
}
}
JS_PUBLIC_API(uint32)
JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key)
{
switch (key) {
case JSGC_MAX_BYTES:
return rt->gcMaxBytes;
case JSGC_MAX_MALLOC_BYTES:
return rt->gcMaxMallocBytes;
case JSGC_STACKPOOL_LIFESPAN:
return rt->gcEmptyArenaPoolLifespan;
case JSGC_TRIGGER_FACTOR:
return rt->gcTriggerFactor;
case JSGC_BYTES:
return rt->gcBytes;
default:
JS_ASSERT(key == JSGC_NUMBER);
return rt->gcNumber;
} }
} }
@ -3807,7 +3832,7 @@ JS_HasUCProperty(JSContext *cx, JSObject *obj,
JSProperty *prop; JSProperty *prop;
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
ok = LookupUCProperty(cx, obj, name, namelen, ok = LookupUCProperty(cx, obj, name, namelen,
JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING,
&obj2, &prop); &obj2, &prop);
if (ok) { if (ok) {

Просмотреть файл

@ -1134,12 +1134,31 @@ typedef enum JSGCParamKey {
JSGC_MAX_MALLOC_BYTES = 1, JSGC_MAX_MALLOC_BYTES = 1,
/* Hoard stackPools for this long, in ms, default is 30 seconds. */ /* Hoard stackPools for this long, in ms, default is 30 seconds. */
JSGC_STACKPOOL_LIFESPAN = 2 JSGC_STACKPOOL_LIFESPAN = 2,
/*
* The factor that defines when the GC is invoked. The factor is a
* percent of the memory allocated by the GC after the last run of
* the GC. When the current memory allocated by the GC is more than
* this percent then the GC is invoked. The factor cannot be less
* than 100 since the current memory allocated by the GC cannot be less
* than the memory allocated after the last run of the GC.
*/
JSGC_TRIGGER_FACTOR = 3,
/* Amount of bytes allocated by the GC. */
JSGC_BYTES = 4,
/* Number of times when GC was invoked. */
JSGC_NUMBER = 5
} JSGCParamKey; } JSGCParamKey;
extern JS_PUBLIC_API(void) extern JS_PUBLIC_API(void)
JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value); JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value);
extern JS_PUBLIC_API(uint32)
JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key);
/* /*
* Add a finalizer for external strings created by JS_NewExternalString (see * Add a finalizer for external strings created by JS_NewExternalString (see
* below) using a type-code returned from this function, and that understands * below) using a type-code returned from this function, and that understands

Просмотреть файл

@ -253,6 +253,7 @@ struct JSRuntime {
uint32 gcLevel; uint32 gcLevel;
uint32 gcNumber; uint32 gcNumber;
JSTracer *gcMarkingTracer; JSTracer *gcMarkingTracer;
uint32 gcTriggerFactor;
/* /*
* NB: do not pack another flag here by claiming gcPadding unless the new * NB: do not pack another flag here by claiming gcPadding unless the new

Просмотреть файл

@ -1253,6 +1253,18 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes)
rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes; rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes;
rt->gcEmptyArenaPoolLifespan = 30000; rt->gcEmptyArenaPoolLifespan = 30000;
/*
* By default the trigger factor gets maximum possible value. This
* means that GC will not be triggered by growth of GC memory (gcBytes).
*/
rt->gcTriggerFactor = (uint32) -1;
/*
* The assigned value prevents GC from running when GC memory is too low
* (during JS engine start).
*/
rt->gcLastBytes = 8192;
METER(memset(&rt->gcStats, 0, sizeof rt->gcStats)); METER(memset(&rt->gcStats, 0, sizeof rt->gcStats));
return JS_TRUE; return JS_TRUE;
} }
@ -1757,6 +1769,17 @@ EnsureLocalFreeList(JSContext *cx)
#endif #endif
static JS_INLINE JSBool
IsGCThresholdReached(JSRuntime *rt)
{
/*
* Since the initial value of the gcLastBytes parameter is not equal to
* zero (see the js_InitGC function) the return value is false when
* the gcBytes value is close to zero at the JS engine start.
*/
return rt->gcBytes / rt->gcTriggerFactor >= rt->gcLastBytes / 100;
}
void * void *
js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes) js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
{ {
@ -1823,7 +1846,8 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
return NULL; return NULL;
} }
doGC = (rt->gcMallocBytes >= rt->gcMaxMallocBytes && rt->gcPoke); doGC = (rt->gcMallocBytes >= rt->gcMaxMallocBytes && rt->gcPoke) ||
IsGCThresholdReached(rt);
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
doGC = doGC || rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke); doGC = doGC || rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke);
#endif #endif
@ -2033,9 +2057,10 @@ RefillDoubleFreeList(JSContext *cx)
return NULL; return NULL;
} }
if (rt->gcMallocBytes >= rt->gcMaxMallocBytes && rt->gcPoke if ((rt->gcMallocBytes >= rt->gcMaxMallocBytes && rt->gcPoke) ||
IsGCThresholdReached(rt)
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
&& (rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke)) || (rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke))
#endif #endif
) { ) {
goto do_gc; goto do_gc;
@ -2207,7 +2232,8 @@ js_AddAsGCBytes(JSContext *cx, size_t sz)
rt = cx->runtime; rt = cx->runtime;
if (rt->gcBytes >= rt->gcMaxBytes || if (rt->gcBytes >= rt->gcMaxBytes ||
sz > (size_t) (rt->gcMaxBytes - rt->gcBytes) sz > (size_t) (rt->gcMaxBytes - rt->gcBytes) ||
IsGCThresholdReached(rt)
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
|| rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke) || rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke)
#endif #endif

Просмотреть файл

@ -216,7 +216,7 @@ static JSDHashOperator
DetachedWrappedNativeProtoMarker(JSDHashTable *table, JSDHashEntryHdr *hdr, DetachedWrappedNativeProtoMarker(JSDHashTable *table, JSDHashEntryHdr *hdr,
uint32 number, void *arg) uint32 number, void *arg)
{ {
XPCWrappedNativeProto* proto = XPCWrappedNativeProto* proto =
(XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key; (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key;
proto->Mark(); proto->Mark();
@ -309,7 +309,7 @@ void XPCJSRuntime::TraceJS(JSTracer* trc, void* data)
// them here. // them here.
for(XPCRootSetElem *e = self->mObjectHolderRoots; e ; e = e->GetNextRoot()) for(XPCRootSetElem *e = self->mObjectHolderRoots; e ; e = e->GetNextRoot())
static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc); static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc);
if(self->GetXPConnect()->ShouldTraceRoots()) if(self->GetXPConnect()->ShouldTraceRoots())
{ {
// Only trace these if we're not cycle-collecting, the cycle collector // Only trace these if we're not cycle-collecting, the cycle collector
@ -527,7 +527,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
case JSGC_MARK_END: case JSGC_MARK_END:
{ {
NS_ASSERTION(!self->mDoingFinalization, "bad state"); NS_ASSERTION(!self->mDoingFinalization, "bad state");
// mThreadRunningGC indicates that GC is running // mThreadRunningGC indicates that GC is running
{ // scoped lock { // scoped lock
XPCAutoLock lock(self->GetMapLock()); XPCAutoLock lock(self->GetMapLock());
@ -550,8 +550,8 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
Enumerate(WrappedJSDyingJSObjectFinder, &data); Enumerate(WrappedJSDyingJSObjectFinder, &data);
} }
// Do cleanup in NativeInterfaces. This part just finds // Do cleanup in NativeInterfaces. This part just finds
// member cloned function objects that are about to be // member cloned function objects that are about to be
// collected. It does not deal with collection of interfaces or // collected. It does not deal with collection of interfaces or
// sets at this point. // sets at this point.
CX_AND_XPCRT_Data data = {cx, self}; CX_AND_XPCRT_Data data = {cx, self};
@ -716,7 +716,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
if(threadLock) if(threadLock)
{ {
// Do the marking... // Do the marking...
{ // scoped lock { // scoped lock
nsAutoLock lock(threadLock); nsAutoLock lock(threadLock);
@ -735,7 +735,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
// possibly be valid. // possibly be valid.
if(ccxp->CanGetTearOff()) if(ccxp->CanGetTearOff())
{ {
XPCWrappedNativeTearOff* to = XPCWrappedNativeTearOff* to =
ccxp->GetTearOff(); ccxp->GetTearOff();
if(to) if(to)
to->Mark(); to->Mark();
@ -744,7 +744,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
} }
} }
} }
// Do the sweeping... // Do the sweeping...
XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs(); XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs();
} }
@ -850,7 +850,7 @@ static JSDHashOperator
DetachedWrappedNativeProtoShutdownMarker(JSDHashTable *table, JSDHashEntryHdr *hdr, DetachedWrappedNativeProtoShutdownMarker(JSDHashTable *table, JSDHashEntryHdr *hdr,
uint32 number, void *arg) uint32 number, void *arg)
{ {
XPCWrappedNativeProto* proto = XPCWrappedNativeProto* proto =
(XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key; (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key;
proto->SystemIsBeingShutDown((JSContext*)arg); proto->SystemIsBeingShutDown((JSContext*)arg);
@ -1084,6 +1084,9 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
JS_SetContextCallback(mJSRuntime, ContextCallback); JS_SetContextCallback(mJSRuntime, ContextCallback);
JS_SetGCCallbackRT(mJSRuntime, GCCallback); JS_SetGCCallbackRT(mJSRuntime, GCCallback);
JS_SetExtraGCRoots(mJSRuntime, TraceJS, this); JS_SetExtraGCRoots(mJSRuntime, TraceJS, this);
// GC will be called when gcBytes is 1600% of gcLastBytes.
JS_SetGCParameter(mJSRuntime, JSGC_TRIGGER_FACTOR, 1600);
} }
if(!JS_DHashTableInit(&mJSHolders, JS_DHashGetStubOps(), nsnull, if(!JS_DHashTableInit(&mJSHolders, JS_DHashGetStubOps(), nsnull,

Просмотреть файл

@ -275,22 +275,22 @@ extern const char XPC_XPCONNECT_CONTRACTID[];
typedef PRMonitor XPCLock; typedef PRMonitor XPCLock;
static inline void xpc_Wait(XPCLock* lock) static inline void xpc_Wait(XPCLock* lock)
{ {
NS_ASSERTION(lock, "xpc_Wait called with null lock!"); NS_ASSERTION(lock, "xpc_Wait called with null lock!");
#ifdef DEBUG #ifdef DEBUG
PRStatus result = PRStatus result =
#endif #endif
PR_Wait(lock, PR_INTERVAL_NO_TIMEOUT); PR_Wait(lock, PR_INTERVAL_NO_TIMEOUT);
NS_ASSERTION(PR_SUCCESS == result, "bad result from PR_Wait!"); NS_ASSERTION(PR_SUCCESS == result, "bad result from PR_Wait!");
} }
static inline void xpc_NotifyAll(XPCLock* lock) static inline void xpc_NotifyAll(XPCLock* lock)
{ {
NS_ASSERTION(lock, "xpc_NotifyAll called with null lock!"); NS_ASSERTION(lock, "xpc_NotifyAll called with null lock!");
#ifdef DEBUG #ifdef DEBUG
PRStatus result = PRStatus result =
#endif #endif
PR_NotifyAll(lock); PR_NotifyAll(lock);
NS_ASSERTION(PR_SUCCESS == result, "bad result from PR_NotifyAll!"); NS_ASSERTION(PR_SUCCESS == result, "bad result from PR_NotifyAll!");
} }
@ -510,7 +510,7 @@ public:
NS_IMETHOD Unroot(void *p); NS_IMETHOD Unroot(void *p);
NS_IMETHOD Traverse(void *p, NS_IMETHOD Traverse(void *p,
nsCycleCollectionTraversalCallback &cb); nsCycleCollectionTraversalCallback &cb);
// nsCycleCollectionLanguageRuntime // nsCycleCollectionLanguageRuntime
virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb); virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb);
virtual nsresult FinishCycleCollection(); virtual nsresult FinishCycleCollection();
@ -808,26 +808,26 @@ public:
JSContext* GetJSContext() const {return mJSContext;} JSContext* GetJSContext() const {return mJSContext;}
enum LangType {LANG_UNKNOWN, LANG_JS, LANG_NATIVE}; enum LangType {LANG_UNKNOWN, LANG_JS, LANG_NATIVE};
LangType GetCallingLangType() const LangType GetCallingLangType() const
{ {
return mCallingLangType; return mCallingLangType;
} }
LangType SetCallingLangType(LangType lt) LangType SetCallingLangType(LangType lt)
{ {
LangType tmp = mCallingLangType; LangType tmp = mCallingLangType;
mCallingLangType = lt; mCallingLangType = lt;
return tmp; return tmp;
} }
JSBool CallerTypeIsJavaScript() const JSBool CallerTypeIsJavaScript() const
{ {
return LANG_JS == mCallingLangType; return LANG_JS == mCallingLangType;
} }
JSBool CallerTypeIsNative() const JSBool CallerTypeIsNative() const
{ {
return LANG_NATIVE == mCallingLangType; return LANG_NATIVE == mCallingLangType;
} }
JSBool CallerTypeIsKnown() const JSBool CallerTypeIsKnown() const
{ {
return LANG_UNKNOWN != mCallingLangType; return LANG_UNKNOWN != mCallingLangType;
} }
@ -1244,7 +1244,7 @@ public:
{return mScriptObjectPrincipal ? {return mScriptObjectPrincipal ?
mScriptObjectPrincipal->GetPrincipal() : nsnull;} mScriptObjectPrincipal->GetPrincipal() : nsnull;}
#endif #endif
JSObject* JSObject*
GetPrototypeJSFunction() const {return mPrototypeJSFunction;} GetPrototypeJSFunction() const {return mPrototypeJSFunction;}
@ -1977,7 +1977,7 @@ public:
// Yes, we *do* need to mark the mScriptableInfo in both cases. // Yes, we *do* need to mark the mScriptableInfo in both cases.
void Mark() const void Mark() const
{mSet->Mark(); {mSet->Mark();
if(mScriptableInfo) mScriptableInfo->Mark();} if(mScriptableInfo) mScriptableInfo->Mark();}
#ifdef DEBUG #ifdef DEBUG
@ -2499,9 +2499,9 @@ public:
JSObject* aJSObj, JSObject* aJSObj,
nsISimpleEnumerator** aEnumerate); nsISimpleEnumerator** aEnumerate);
static nsresult GetNamedPropertyAsVariant(XPCCallContext& ccx, static nsresult GetNamedPropertyAsVariant(XPCCallContext& ccx,
JSObject* aJSObj, JSObject* aJSObj,
jsval aName, jsval aName,
nsIVariant** aResult); nsIVariant** aResult);
virtual ~nsXPCWrappedJSClass(); virtual ~nsXPCWrappedJSClass();
@ -2737,7 +2737,7 @@ public:
* @param scope the default scope to put on the new JSObject's __parent__ * @param scope the default scope to put on the new JSObject's __parent__
* chain * chain
* @param pErr [out] relevant error code, if any. * @param pErr [out] relevant error code, if any.
*/ */
static JSBool NativeData2JS(XPCCallContext& ccx, jsval* d, const void* s, static JSBool NativeData2JS(XPCCallContext& ccx, jsval* d, const void* s,
const nsXPTType& type, const nsID* iid, const nsXPTType& type, const nsID* iid,
JSObject* scope, nsresult* pErr); JSObject* scope, nsresult* pErr);
@ -2771,7 +2771,7 @@ public:
static JSBool GetNativeInterfaceFromJSObject(XPCCallContext& ccx, static JSBool GetNativeInterfaceFromJSObject(XPCCallContext& ccx,
void** dest, JSObject* src, void** dest, JSObject* src,
const nsID* iid, const nsID* iid,
nsresult* pErr); nsresult* pErr);
static JSBool JSObject2NativeInterface(XPCCallContext& ccx, static JSBool JSObject2NativeInterface(XPCCallContext& ccx,
void** dest, JSObject* src, void** dest, JSObject* src,
@ -2792,7 +2792,7 @@ public:
* @param scope the default scope to put on the new JSObjects' __parent__ * @param scope the default scope to put on the new JSObjects' __parent__
* chain * chain
* @param pErr [out] relevant error code, if any. * @param pErr [out] relevant error code, if any.
*/ */
static JSBool NativeArray2JS(XPCCallContext& ccx, static JSBool NativeArray2JS(XPCCallContext& ccx,
jsval* d, const void** s, jsval* d, const void** s,
const nsXPTType& type, const nsID* iid, const nsXPTType& type, const nsID* iid,
@ -2880,7 +2880,7 @@ public:
static void ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx); static void ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx);
static void ThrowBadParam(nsresult rv, uintN paramNum, XPCCallContext& ccx); static void ThrowBadParam(nsresult rv, uintN paramNum, XPCCallContext& ccx);
#ifdef XPC_IDISPATCH_SUPPORT #ifdef XPC_IDISPATCH_SUPPORT
static void ThrowCOMError(JSContext* cx, unsigned long COMErrorCode, static void ThrowCOMError(JSContext* cx, unsigned long COMErrorCode,
nsresult rv = NS_ERROR_XPC_COM_ERROR, nsresult rv = NS_ERROR_XPC_COM_ERROR,
const EXCEPINFO * exception = nsnull); const EXCEPINFO * exception = nsnull);
#endif #endif
@ -3287,7 +3287,7 @@ private:
static XPCPerThreadData* gThreads; static XPCPerThreadData* gThreads;
static PRUintn gTLSIndex; static PRUintn gTLSIndex;
// Cached value of cx->thread on the main thread. // Cached value of cx->thread on the main thread.
static void *sMainJSThread; static void *sMainJSThread;
// Cached per thread data for the main thread. Only safe to access // Cached per thread data for the main thread. Only safe to access
@ -3595,7 +3595,7 @@ private:
JSContext *mCX; JSContext *mCX;
jsrefcount mDepth; jsrefcount mDepth;
}; };
/*****************************************/ /*****************************************/
@ -3691,7 +3691,7 @@ public:
~AutoResolveName() ~AutoResolveName()
{ {
#ifdef DEBUG #ifdef DEBUG
jsval old = jsval old =
#endif #endif
mTLS->SetResolveName(mOld); mTLS->SetResolveName(mOld);
NS_ASSERTION(old == mCheck, "Bad Nesting!"); NS_ASSERTION(old == mCheck, "Bad Nesting!");
@ -3717,17 +3717,17 @@ public:
} }
void AutoTrace(JSTracer* trc) {} void AutoTrace(JSTracer* trc) {}
private: private:
XPCMarkableJSVal(); // not implemented XPCMarkableJSVal(); // not implemented
jsval mVal; jsval mVal;
jsval* mValPtr; jsval* mValPtr;
}; };
/***************************************************************************/ /***************************************************************************/
// AutoMarkingPtr is the base class for the various AutoMarking pointer types // AutoMarkingPtr is the base class for the various AutoMarking pointer types
// below. This system allows us to temporarily protect instances of our garbage // below. This system allows us to temporarily protect instances of our garbage
// collected types after they are constructed but before they are safely // collected types after they are constructed but before they are safely
// attached to other rooted objects. // attached to other rooted objects.
// This base class has pure virtual support for marking. // This base class has pure virtual support for marking.
class AutoMarkingPtr class AutoMarkingPtr
{ {
@ -3736,15 +3736,15 @@ public:
: mNext(nsnull), mTLS(ccx.GetThreadData()) {Link();} : mNext(nsnull), mTLS(ccx.GetThreadData()) {Link();}
virtual ~AutoMarkingPtr() {Unlink();} virtual ~AutoMarkingPtr() {Unlink();}
void Link() void Link()
{if(!mTLS) return; {if(!mTLS) return;
AutoMarkingPtr** list = mTLS->GetAutoRootsAdr(); AutoMarkingPtr** list = mTLS->GetAutoRootsAdr();
mNext = *list; *list = this;} mNext = *list; *list = this;}
void Unlink() void Unlink()
{if(!mTLS) return; {if(!mTLS) return;
AutoMarkingPtr** cur = mTLS->GetAutoRootsAdr(); AutoMarkingPtr** cur = mTLS->GetAutoRootsAdr();
while(*cur != this) { while(*cur != this) {
NS_ASSERTION(*cur, "This object not in list!"); NS_ASSERTION(*cur, "This object not in list!");
cur = &(*cur)->mNext; cur = &(*cur)->mNext;
@ -3754,7 +3754,7 @@ public:
} }
AutoMarkingPtr* GetNext() {return mNext;} AutoMarkingPtr* GetNext() {return mNext;}
virtual void TraceJS(JSTracer* trc) = 0; virtual void TraceJS(JSTracer* trc) = 0;
virtual void MarkAfterJSFinalize() = 0; virtual void MarkAfterJSFinalize() = 0;
@ -3803,7 +3803,7 @@ DEFINE_AUTO_MARKING_PTR_TYPE(AutoMarkingWrappedNativePtr, XPCWrappedNative)
DEFINE_AUTO_MARKING_PTR_TYPE(AutoMarkingWrappedNativeTearOffPtr, XPCWrappedNativeTearOff) DEFINE_AUTO_MARKING_PTR_TYPE(AutoMarkingWrappedNativeTearOffPtr, XPCWrappedNativeTearOff)
DEFINE_AUTO_MARKING_PTR_TYPE(AutoMarkingWrappedNativeProtoPtr, XPCWrappedNativeProto) DEFINE_AUTO_MARKING_PTR_TYPE(AutoMarkingWrappedNativeProtoPtr, XPCWrappedNativeProto)
DEFINE_AUTO_MARKING_PTR_TYPE(AutoMarkingJSVal, XPCMarkableJSVal) DEFINE_AUTO_MARKING_PTR_TYPE(AutoMarkingJSVal, XPCMarkableJSVal)
#define DEFINE_AUTO_MARKING_ARRAY_PTR_TYPE(class_, type_) \ #define DEFINE_AUTO_MARKING_ARRAY_PTR_TYPE(class_, type_) \
class class_ : public AutoMarkingPtr \ class class_ : public AutoMarkingPtr \
{ \ { \
@ -3858,9 +3858,9 @@ protected: \
DEFINE_AUTO_MARKING_ARRAY_PTR_TYPE(AutoMarkingNativeInterfacePtrArrayPtr, DEFINE_AUTO_MARKING_ARRAY_PTR_TYPE(AutoMarkingNativeInterfacePtrArrayPtr,
XPCNativeInterface) XPCNativeInterface)
// Note: It looked like I would need one of these AutoMarkingPtr types for // Note: It looked like I would need one of these AutoMarkingPtr types for
// XPCNativeScriptableInfo in order to manage marking its // XPCNativeScriptableInfo in order to manage marking its
// XPCNativeScriptableShared member during construction. But AFAICT we build // XPCNativeScriptableShared member during construction. But AFAICT we build
// these and bind them to rooted things so immediately that this just is not // these and bind them to rooted things so immediately that this just is not
// needed. // needed.
@ -3903,8 +3903,8 @@ public:
// the case when mJSVal is JSVAL_STRING, since we don't own the data in // the case when mJSVal is JSVAL_STRING, since we don't own the data in
// that case. // that case.
// We #define and iid so that out module local code can use QI to detect // We #define and iid so that out module local code can use QI to detect
// if a given nsIVariant is in fact an XPCVariant. // if a given nsIVariant is in fact an XPCVariant.
NS_DECLARE_STATIC_IID_ACCESSOR(XPCVARIANT_IID) NS_DECLARE_STATIC_IID_ACCESSOR(XPCVARIANT_IID)
static XPCVariant* newVariant(XPCCallContext& ccx, jsval aJSVal); static XPCVariant* newVariant(XPCCallContext& ccx, jsval aJSVal);
@ -3922,8 +3922,8 @@ public:
* chain * chain
* @param pErr [out] relevant error code, if any. * @param pErr [out] relevant error code, if any.
* @param pJSVal [out] the resulting jsval. * @param pJSVal [out] the resulting jsval.
*/ */
static JSBool VariantDataToJS(XPCCallContext& ccx, static JSBool VariantDataToJS(XPCCallContext& ccx,
nsIVariant* variant, nsIVariant* variant,
JSObject* scope, nsresult* pErr, JSObject* scope, nsresult* pErr,
jsval* pJSVal); jsval* pJSVal);