From 74aaef5de9472fcf8b30ca5a0aae9dd4fb603fbb Mon Sep 17 00:00:00 2001 From: Andrei Saprykin Date: Wed, 21 Jan 2009 16:47:17 +0100 Subject: [PATCH] bug 453432 - Checking for MaybeGC conditions when allocating GC things --- dom/src/base/nsJSEnvironment.cpp | 85 ++++++++++----------------- dom/src/base/nsJSEnvironment.h | 5 +- js/src/jsapi.cpp | 27 ++++++++- js/src/jsapi.h | 21 ++++++- js/src/jscntxt.h | 1 + js/src/jsgc.cpp | 34 +++++++++-- js/src/shell/js.cpp | 73 ++++++++++------------- js/src/xpconnect/shell/xpcshell.cpp | 4 ++ js/src/xpconnect/src/xpcjsruntime.cpp | 22 ++++--- 9 files changed, 159 insertions(+), 113 deletions(-) diff --git a/dom/src/base/nsJSEnvironment.cpp b/dom/src/base/nsJSEnvironment.cpp index a990e10042e9..639515c2ab8f 100644 --- a/dom/src/base/nsJSEnvironment.cpp +++ b/dom/src/base/nsJSEnvironment.cpp @@ -854,24 +854,8 @@ PrintWinCodebase(nsGlobalWindow *win) } #endif -// The accumulated operation weight before we call MaybeGC -const PRUint32 MAYBE_GC_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); - } -} +// The accumulated operation weight for DOM callback. +const PRUint32 DOM_CALLBACK_OPERATION_WEIGHT = 5000 * JS_OPERATION_WEIGHT_BASE; static already_AddRefed GetPromptFromContext(nsJSContext* ctx) @@ -904,17 +888,8 @@ nsJSContext::DOMOperationCallback(JSContext *cx) 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; - 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 nsCOMPtr mem; NS_GetMemoryManager(getter_AddRefs(mem)); @@ -936,24 +911,24 @@ nsJSContext::DOMOperationCallback(JSContext *cx) if (nsContentUtils::GetBoolPref("dom.prevent_oom_dialog", PR_FALSE)) return JS_FALSE; - + nsCOMPtr prompt = GetPromptFromContext(ctx); - + nsXPIDLString title, msg; rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, "LowMemoryTitle", title); - + rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, "LowMemoryMessage", msg); - + //GetStringFromName can return NS_OK and still give NULL string if (NS_FAILED(rv) || !title || !msg) { NS_ERROR("Failed to get localized strings."); return JS_FALSE; } - + prompt->Alert(title, msg); return JS_FALSE; } @@ -1251,7 +1226,7 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime) : mGCOnDestruction(PR_TRUE) this); ::JS_SetOperationCallback(mContext, DOMOperationCallback, - MAYBE_GC_OPERATION_WEIGHT); + DOM_CALLBACK_OPERATION_WEIGHT); static JSLocaleCallbacks localeCallbacks = { @@ -1264,7 +1239,6 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime) : mGCOnDestruction(PR_TRUE) ::JS_SetLocaleCallbacks(mContext, &localeCallbacks); } mIsInitialized = PR_FALSE; - mNumEvaluations = 0; mTerminations = nsnull; mScriptsEnabled = PR_TRUE; mOperationCallbackTime = LL_ZERO; @@ -3312,18 +3286,6 @@ nsJSContext::ScriptEvaluated(PRBool aTerminated) delete start; } - mNumEvaluations++; - -#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; } @@ -3410,19 +3372,35 @@ nsJSContext::CC() #endif sPreviousCCTime = PR_Now(); sDelayedCCollectCount = 0; - sGCCount = 0; + sGCCount = JS_GetGCParameter(nsJSRuntime::sRuntime, JSGC_NUMBER); sCCSuspectChanges = 0; // nsCycleCollector_collect() will run a ::JS_GC() indirectly, so // we do not explicitly call ::JS_GC() here. sCollectedObjectsCounts = nsCycleCollector_collect(); sCCSuspectedCount = nsCycleCollector_suspectedCount(); #ifdef DEBUG_smaug - printf("Collected %u objects, %u suspected objects, took %lldms\n", - sCollectedObjectsCounts, sCCSuspectedCount, - (PR_Now() - sPreviousCCTime) / PR_USEC_PER_MSEC); + printf("Collected %u objects, %u suspected objects\n", + sCollectedObjectsCounts, sCCSuspectedCount); #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. + */ + + // To avoid crash if nsJSRuntime is not properly initialized. + // See the bug 474586 + if (!nsJSRuntime::sRuntime) + return 0; + + return JS_GetGCParameter(nsJSRuntime::sRuntime, JSGC_NUMBER) - sGCCount; +} + //static PRBool nsJSContext::MaybeCC(PRBool aHigherProbability) @@ -3431,7 +3409,7 @@ nsJSContext::MaybeCC(PRBool aHigherProbability) // Don't check suspected count if CC will be called anyway. if (sCCSuspectChanges <= NS_MIN_SUSPECT_CHANGES || - sGCCount <= NS_MAX_GC_COUNT) { + GetGCRunsCount() <= NS_MAX_GC_COUNT) { #ifdef DEBUG_smaug PRTime now = PR_Now(); #endif @@ -3449,7 +3427,8 @@ nsJSContext::MaybeCC(PRBool aHigherProbability) } #ifdef DEBUG_smaug printf("sCCSuspectChanges %u, sGCCount %u\n", - sCCSuspectChanges, sGCCount); + sCCSuspectChanges, + GetGCRunsCount()); #endif // Increase the probability also if the previous call to cycle collector @@ -3462,7 +3441,7 @@ nsJSContext::MaybeCC(PRBool aHigherProbability) if (!sGCTimer && (sDelayedCCollectCount > NS_MAX_DELAYED_CCOLLECT) && ((sCCSuspectChanges > NS_MIN_SUSPECT_CHANGES && - sGCCount > NS_MAX_GC_COUNT) || + GetGCRunsCount() > NS_MAX_GC_COUNT) || (sCCSuspectChanges > NS_MAX_SUSPECT_CHANGES))) { if ((PR_Now() - sPreviousCCTime) >= PRTime(NS_MIN_CC_INTERVAL * PR_USEC_PER_MSEC)) { diff --git a/dom/src/base/nsJSEnvironment.h b/dom/src/base/nsJSEnvironment.h index 408ff700f41e..7d9dc68b8565 100644 --- a/dom/src/base/nsJSEnvironment.h +++ b/dom/src/base/nsJSEnvironment.h @@ -223,7 +223,6 @@ private: void Unlink(); JSContext *mContext; - PRUint32 mNumEvaluations; protected: struct TerminationFuncHolder; @@ -276,7 +275,7 @@ protected: nsJSContext* mContext; TerminationFuncClosure* mTerminations; }; - + TerminationFuncClosure* mTerminations; private: @@ -322,7 +321,7 @@ public: virtual nsresult DropScriptObject(void *object); virtual nsresult HoldScriptObject(void *object); - + static void Startup(); static void Shutdown(); // Setup all the statics etc - safe to call multiple times after Startup() diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 192a07f740b3..3239e40fad44 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2595,6 +2595,31 @@ JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value) case JSGC_STACKPOOL_LIFESPAN: rt->gcEmptyArenaPoolLifespan = value; 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; } } @@ -3812,7 +3837,7 @@ JS_HasUCProperty(JSContext *cx, JSObject *obj, JSProperty *prop; CHECK_REQUEST(cx); - ok = LookupUCProperty(cx, obj, name, namelen, + ok = LookupUCProperty(cx, obj, name, namelen, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING, &obj2, &prop); if (ok) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 1348568ddebb..e1056b627ab0 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1137,12 +1137,31 @@ typedef enum JSGCParamKey { JSGC_MAX_MALLOC_BYTES = 1, /* 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; extern JS_PUBLIC_API(void) 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 * below) using a type-code returned from this function, and that understands diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 9fa4b0141f2a..9c4f8038d887 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -267,6 +267,7 @@ struct JSRuntime { uint32 gcLevel; uint32 gcNumber; JSTracer *gcMarkingTracer; + uint32 gcTriggerFactor; /* * NB: do not pack another flag here by claiming gcPadding unless the new diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 10911f14cc78..3e3d339b62ee 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1253,6 +1253,18 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes) rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes; 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)); return JS_TRUE; } @@ -1757,6 +1769,17 @@ EnsureLocalFreeList(JSContext *cx) #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 * js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes) { @@ -1823,7 +1846,8 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes) return NULL; } - doGC = (rt->gcMallocBytes >= rt->gcMaxMallocBytes && rt->gcPoke); + doGC = (rt->gcMallocBytes >= rt->gcMaxMallocBytes && rt->gcPoke) || + IsGCThresholdReached(rt); #ifdef JS_GC_ZEAL doGC = doGC || rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke); # ifdef JS_TRACER @@ -2056,9 +2080,10 @@ RefillDoubleFreeList(JSContext *cx) return NULL; } - if (rt->gcMallocBytes >= rt->gcMaxMallocBytes && rt->gcPoke + if ((rt->gcMallocBytes >= rt->gcMaxMallocBytes && rt->gcPoke) || + IsGCThresholdReached(rt) #ifdef JS_GC_ZEAL - && (rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke)) + || (rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke)) #endif ) { goto do_gc; @@ -2257,7 +2282,8 @@ js_AddAsGCBytes(JSContext *cx, size_t sz) rt = cx->runtime; 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 || rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke) #endif diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index db8fb4c0581f..967d948a7840 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -229,9 +229,6 @@ struct JSShellContextData { PRIntervalTime timeout; volatile PRIntervalTime startTime; /* startTime + timeout is time when script must be stopped */ - PRIntervalTime maybeGCPeriod; - volatile PRIntervalTime lastMaybeGCTime;/* lastMaybeGCTime + maybeGCPeriod - is the time to call MaybeGC */ PRIntervalTime yieldPeriod; volatile PRIntervalTime lastYieldTime; /* lastYieldTime + yieldPeriod is the time to call @@ -239,7 +236,6 @@ struct JSShellContextData { #else int64 stopTime; /* time when script must be stopped */ - int64 nextMaybeGCTime;/* time to call JS_MaybeGC */ #endif }; @@ -249,7 +245,6 @@ SetTimeoutValue(JSContext *cx, jsdouble t); #ifdef JS_THREADSAFE # define DEFAULT_YIELD_PERIOD() (PR_TicksPerSecond() / 50) -# define DEFAULT_MAYBEGC_PERIOD() (PR_TicksPerSecond() / 10) /* * The function assumes that the GC lock is already held on entry. On a @@ -261,8 +256,6 @@ RescheduleWatchdog(JSContext *cx, JSShellContextData *data, PRIntervalTime now); #else -# define DEFAULT_MAYBEGC_PERIOD() (MICROSECONDS_PER_SECOND / 10) - const int64 MICROSECONDS_PER_SECOND = 1000000LL; const int64 MAX_TIME_VALUE = 0x7FFFFFFFFFFFFFFFLL; @@ -277,16 +270,13 @@ NewContextData() return NULL; #ifdef JS_THREADSAFE data->timeout = PR_INTERVAL_NO_TIMEOUT; - data->maybeGCPeriod = PR_INTERVAL_NO_TIMEOUT; data->yieldPeriod = PR_INTERVAL_NO_TIMEOUT; # ifdef DEBUG data->startTime = 0; - data->lastMaybeGCTime = 0; data->lastYieldTime = 0; # endif #else /* !JS_THREADSAFE */ data->stopTime = MAX_TIME_VALUE; - data->nextMaybeGCTime = MAX_TIME_VALUE; #endif return data; @@ -306,7 +296,6 @@ ShellOperationCallback(JSContext *cx) { JSShellContextData *data = GetContextData(cx); JSBool doStop; - JSBool doMaybeGC; #ifdef JS_THREADSAFE JSBool doYield; PRIntervalTime now = PR_IntervalNow(); @@ -314,11 +303,6 @@ ShellOperationCallback(JSContext *cx) doStop = (data->timeout != PR_INTERVAL_NO_TIMEOUT && now - data->startTime >= data->timeout); - doMaybeGC = (data->maybeGCPeriod != PR_INTERVAL_NO_TIMEOUT && - now - data->lastMaybeGCTime >= data->maybeGCPeriod); - if (doMaybeGC) - data->lastMaybeGCTime = now; - doYield = (data->yieldPeriod != PR_INTERVAL_NO_TIMEOUT && now - data->lastYieldTime >= data->yieldPeriod); if (doYield) @@ -328,9 +312,6 @@ ShellOperationCallback(JSContext *cx) int64 now = JS_Now(); doStop = (now >= data->stopTime); - doMaybeGC = (now >= data->nextMaybeGCTime); - if (doMaybeGC) - data->nextMaybeGCTime = now + DEFAULT_MAYBEGC_PERIOD(); #endif if (doStop) { @@ -338,9 +319,6 @@ ShellOperationCallback(JSContext *cx) return JS_FALSE; } - if (doMaybeGC) - JS_MaybeGC(cx); - #ifdef JS_THREADSAFE if (doYield) JS_YieldRequest(cx); @@ -1090,24 +1068,49 @@ GCParameter(JSContext *cx, uintN argc, jsval *vp) param = JSGC_MAX_BYTES; } else if (strcmp(paramName, "maxMallocBytes") == 0) { 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 { JS_ReportError(cx, - "the first argument argument must be either maxBytes " - "or maxMallocBytes"); + "the first argument argument must be maxBytes, " + "maxMallocBytes, gcStackpoolLifespan, gcBytes, " + "gcNumber or gcTriggerFactor"); 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; - if (value == 0) { + } + + if (!JS_ValueToECMAUint32(cx, vp[3], &value)) { JS_ReportError(cx, - "the second argument must be convertable to uint32 with " - "non-zero value"); + "the second argument must be convertable to uint32 " + "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; } JS_SetGCParameter(cx->runtime, param, value); *vp = JSVAL_VOID; return JS_TRUE; + } #ifdef JS_GC_ZEAL @@ -3142,8 +3145,6 @@ CheckCallbackTime(JSContext *cx, JSShellContextData *data, PRIntervalTime now, UpdateSleepDuration(now, data->startTime, data->timeout, sleepDuration, expired); - UpdateSleepDuration(now, data->lastMaybeGCTime, data->maybeGCPeriod, - sleepDuration, expired); UpdateSleepDuration(now, data->lastYieldTime, data->yieldPeriod, sleepDuration, expired); if (expired) { @@ -3247,24 +3248,15 @@ SetTimeoutValue(JSContext *cx, jsdouble t) return JS_FALSE; } - /* - * For compatibility periodic MaybeGC calls are enabled only when the - * execution time is bounded. - */ JSShellContextData *data = GetContextData(cx); #ifdef JS_THREADSAFE JS_LOCK_GC(cx->runtime); if (t < 0) { data->timeout = PR_INTERVAL_NO_TIMEOUT; - data->maybeGCPeriod = PR_INTERVAL_NO_TIMEOUT; } else { PRIntervalTime now = PR_IntervalNow(); data->timeout = PRIntervalTime(t * PR_TicksPerSecond()); data->startTime = now; - if (data->maybeGCPeriod == PR_INTERVAL_NO_TIMEOUT) { - data->maybeGCPeriod = DEFAULT_MAYBEGC_PERIOD(); - data->lastMaybeGCTime = now; - } if (!RescheduleWatchdog(cx, data, now)) { /* The GC lock is already released here. */ return JS_FALSE; @@ -3275,13 +3267,10 @@ SetTimeoutValue(JSContext *cx, jsdouble t) #else /* !JS_THREADSAFE */ if (t < 0) { data->stopTime = MAX_TIME_VALUE; - data->nextMaybeGCTime = MAX_TIME_VALUE; JS_SetOperationLimit(cx, JS_MAX_OPERATION_LIMIT); } else { int64 now = JS_Now(); data->stopTime = now + int64(t * MICROSECONDS_PER_SECOND); - if (data->nextMaybeGCTime == MAX_TIME_VALUE) - data->nextMaybeGCTime = now + DEFAULT_MAYBEGC_PERIOD(); /* * Call the callback infrequently enough to avoid the overhead of diff --git a/js/src/xpconnect/shell/xpcshell.cpp b/js/src/xpconnect/shell/xpcshell.cpp index 637354c97309..c77caebd0127 100644 --- a/js/src/xpconnect/shell/xpcshell.cpp +++ b/js/src/xpconnect/shell/xpcshell.cpp @@ -1572,6 +1572,10 @@ main(int argc, char **argv, char **envp) gOldJSContextCallback = JS_SetContextCallback(rt, ContextCallback); + //Set the GC trigger factor back to the initial value. + //See the bug 474312. + JS_SetGCParameter(rt, JSGC_TRIGGER_FACTOR, (uint32) -1); + cx = JS_NewContext(rt, 8192); if (!cx) { printf("JS_NewContext failed!\n"); diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index ee5db9c88215..09002f07dbb0 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -217,7 +217,7 @@ static JSDHashOperator DetachedWrappedNativeProtoMarker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { - XPCWrappedNativeProto* proto = + XPCWrappedNativeProto* proto = (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key; proto->Mark(); @@ -310,7 +310,7 @@ void XPCJSRuntime::TraceJS(JSTracer* trc, void* data) // them here. for(XPCRootSetElem *e = self->mObjectHolderRoots; e ; e = e->GetNextRoot()) static_cast(e)->TraceJS(trc); - + if(self->GetXPConnect()->ShouldTraceRoots()) { // Only trace these if we're not cycle-collecting, the cycle collector @@ -498,7 +498,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) case JSGC_MARK_END: { NS_ASSERTION(!self->mDoingFinalization, "bad state"); - + // mThreadRunningGC indicates that GC is running { // scoped lock XPCAutoLock lock(self->GetMapLock()); @@ -521,8 +521,8 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) Enumerate(WrappedJSDyingJSObjectFinder, &data); } - // Do cleanup in NativeInterfaces. This part just finds - // member cloned function objects that are about to be + // Do cleanup in NativeInterfaces. This part just finds + // member cloned function objects that are about to be // collected. It does not deal with collection of interfaces or // sets at this point. CX_AND_XPCRT_Data data = {cx, self}; @@ -689,7 +689,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) if(threadLock) { // Do the marking... - + { // scoped lock nsAutoLock lock(threadLock); @@ -708,7 +708,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) // possibly be valid. if(ccxp->CanGetTearOff()) { - XPCWrappedNativeTearOff* to = + XPCWrappedNativeTearOff* to = ccxp->GetTearOff(); if(to) to->Mark(); @@ -717,7 +717,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) } } } - + // Do the sweeping... XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs(); } @@ -823,7 +823,7 @@ static JSDHashOperator DetachedWrappedNativeProtoShutdownMarker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { - XPCWrappedNativeProto* proto = + XPCWrappedNativeProto* proto = (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key; proto->SystemIsBeingShutDown((JSContext*)arg); @@ -1057,6 +1057,10 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) JS_SetContextCallback(mJSRuntime, ContextCallback); JS_SetGCCallbackRT(mJSRuntime, GCCallback); 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,