From c5c7623497af6791bb3934640e5e68be47af39b7 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Fri, 24 Feb 2012 12:03:28 +0100 Subject: [PATCH] bug 730234 - remove GC locking from activities, operation callbacks-related code and for code that accesses the JSContext list. r=luke --- dom/workers/WorkerPrivate.cpp | 2 +- js/src/jsapi.cpp | 23 +------ js/src/jsapi.h | 19 ++---- js/src/jscntxt.cpp | 69 ++++++++++--------- js/src/jscntxt.h | 36 +++++++++- js/src/jsfriendapi.cpp | 35 ---------- js/src/jsfriendapi.h | 43 +----------- js/src/jsgc.cpp | 40 ++--------- js/src/jsinfer.cpp | 11 --- js/src/shell/js.cpp | 2 +- js/src/shell/jsworkers.cpp | 2 +- js/src/vm/Debugger.cpp | 1 - js/xpconnect/src/XPCJSRuntime.cpp | 109 +++++++++++++++++------------- js/xpconnect/src/xpcprivate.h | 3 + 14 files changed, 152 insertions(+), 243 deletions(-) diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index cf9aba1a4a023..e827aab9886b8 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -2925,7 +2925,7 @@ WorkerPrivate::Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue) } if (aQueue == &mControlQueue && mJSContext) { - JS_TriggerOperationCallback(mJSContext); + JS_TriggerOperationCallback(JS_GetRuntime(mJSContext)); } mCondVar.Notify(); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 5fe134b9f68d4..eb780ca612a1b 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1014,8 +1014,6 @@ StartRequest(JSContext *cx) if (rt->requestDepth) { rt->requestDepth++; } else { - AutoLockGC lock(rt); - /* Indicate that a request is running. */ rt->requestDepth = 1; @@ -1034,10 +1032,6 @@ StopRequest(JSContext *cx) rt->requestDepth--; } else { rt->conservativeGC.updateForRequestEnd(rt->suspendCount); - - /* Lock before clearing to interlock with ClaimScope, in jslock.c. */ - AutoLockGC lock(rt); - rt->requestDepth = 0; if (rt->activityCallback) @@ -1307,14 +1301,12 @@ SetOptionsCommon(JSContext *cx, unsigned options) JS_PUBLIC_API(uint32_t) JS_SetOptions(JSContext *cx, uint32_t options) { - AutoLockGC lock(cx->runtime); return SetOptionsCommon(cx, options); } JS_PUBLIC_API(uint32_t) JS_ToggleOptions(JSContext *cx, uint32_t options) { - AutoLockGC lock(cx->runtime); unsigned oldopts = cx->allOptions(); unsigned newopts = oldopts ^ options; return SetOptionsCommon(cx, newopts); @@ -2896,7 +2888,6 @@ JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value) { switch (key) { case JSGC_MAX_BYTES: { - AutoLockGC lock(rt); JS_ASSERT(value >= rt->gcBytes); rt->gcMaxBytes = value; break; @@ -5523,20 +5514,8 @@ JS_GetOperationCallback(JSContext *cx) } JS_PUBLIC_API(void) -JS_TriggerOperationCallback(JSContext *cx) +JS_TriggerOperationCallback(JSRuntime *rt) { -#ifdef JS_THREADSAFE - AutoLockGC lock(cx->runtime); -#endif - cx->runtime->triggerOperationCallback(); -} - -JS_PUBLIC_API(void) -JS_TriggerRuntimeOperationCallback(JSRuntime *rt) -{ -#ifdef JS_THREADSAFE - AutoLockGC lock(rt); -#endif rt->triggerOperationCallback(); } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index cca903fde5e1d..536e6aebd7d22 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -4504,21 +4504,17 @@ JS_BEGIN_EXTERN_C /* * These functions allow setting an operation callback that will be called - * from the thread the context is associated with some time after any thread - * triggered the callback using JS_TriggerOperationCallback(cx). + * from the JS thread some time after any thread triggered the callback using + * JS_TriggerOperationCallback(rt). * - * In a threadsafe build the engine internally triggers operation callbacks - * under certain circumstances (i.e. GC and title transfer) to force the - * context to yield its current request, which the engine always - * automatically does immediately prior to calling the callback function. - * The embedding should thus not rely on callbacks being triggered through - * the external API only. + * To schedule the GC and for other activities the engine internally triggers + * operation callbacks. The embedding should thus not rely on callbacks being + * triggered through the external API only. * * Important note: Additional callbacks can occur inside the callback handler * if it re-enters the JS engine. The embedding must ensure that the callback * is disconnected before attempting such re-entry. */ - extern JS_PUBLIC_API(JSOperationCallback) JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback); @@ -4526,10 +4522,7 @@ extern JS_PUBLIC_API(JSOperationCallback) JS_GetOperationCallback(JSContext *cx); extern JS_PUBLIC_API(void) -JS_TriggerOperationCallback(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_TriggerRuntimeOperationCallback(JSRuntime *rt); +JS_TriggerOperationCallback(JSRuntime *rt); extern JS_PUBLIC_API(JSBool) JS_IsRunning(JSContext *cx); diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 414ed5b67d0d7..6e900691ab82e 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -118,7 +118,7 @@ JSRuntime::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, s *gcMarkerSize = gcMarker.sizeOfExcludingThis(mallocSizeOf); } -JS_FRIEND_API(void) +void JSRuntime::triggerOperationCallback() { /* @@ -250,49 +250,48 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) } } - JS_LOCK_GC(rt); JS_REMOVE_LINK(&cx->link); bool last = !rt->hasContexts(); - if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC) { + if (last) { JS_ASSERT(!rt->gcRunning); #ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(); -#endif - JS_UNLOCK_GC(rt); - - if (last) { - /* - * Dump remaining type inference results first. This printing - * depends on atoms still existing. - */ - { - AutoLockGC lock(rt); - for (CompartmentsIter c(rt); !c.done(); c.next()) - c->types.print(cx, false); - } - - /* Unpin all common atoms before final GC. */ - js_FinishCommonAtoms(cx); - - /* Clear debugging state to remove GC roots. */ - for (CompartmentsIter c(rt); !c.done(); c.next()) - c->clearTraps(cx); - JS_ClearAllWatchPoints(cx); - - GC(cx, NULL, GC_NORMAL, gcreason::LAST_CONTEXT); - - } else if (mode == JSDCM_FORCE_GC) { - GC(cx, NULL, GC_NORMAL, gcreason::DESTROY_CONTEXT); - } else if (mode == JSDCM_MAYBE_GC) { - JS_MaybeGC(cx); + { + AutoLockGC lock(rt); + rt->gcHelperThread.waitBackgroundSweepEnd(); } - JS_LOCK_GC(rt); +#endif + + /* + * Dump remaining type inference results first. This printing + * depends on atoms still existing. + */ + for (CompartmentsIter c(rt); !c.done(); c.next()) + c->types.print(cx, false); + + /* Unpin all common atoms before final GC. */ + js_FinishCommonAtoms(cx); + + /* Clear debugging state to remove GC roots. */ + for (CompartmentsIter c(rt); !c.done(); c.next()) + c->clearTraps(cx); + JS_ClearAllWatchPoints(cx); + + GC(cx, NULL, GC_NORMAL, gcreason::LAST_CONTEXT); + } else if (mode == JSDCM_FORCE_GC) { + JS_ASSERT(!rt->gcRunning); + GC(cx, NULL, GC_NORMAL, gcreason::DESTROY_CONTEXT); + } else if (mode == JSDCM_MAYBE_GC) { + JS_ASSERT(!rt->gcRunning); + JS_MaybeGC(cx); } + #ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(); + { + AutoLockGC lock(rt); + rt->gcHelperThread.waitBackgroundSweepEnd(); + } #endif - JS_UNLOCK_GC(rt); Foreground::delete_(cx); } diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 4d1afcb45f058..44ad726618b9f 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -686,7 +686,7 @@ struct JSRuntime : js::RuntimeFriendFields */ JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx); - JS_FRIEND_API(void) triggerOperationCallback(); + void triggerOperationCallback(); void setJitHardening(bool enabled); bool getJitHardening() const { @@ -1247,6 +1247,40 @@ class AutoXMLRooter : private AutoGCRooter { # define JS_UNLOCK_GC(rt) #endif +class AutoLockGC +{ + public: + explicit AutoLockGC(JSRuntime *rt = NULL + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : runtime(rt) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + if (rt) + JS_LOCK_GC(rt); + } + + ~AutoLockGC() + { + if (runtime) + JS_UNLOCK_GC(runtime); + } + + bool locked() const { + return !!runtime; + } + + void lock(JSRuntime *rt) { + JS_ASSERT(rt); + JS_ASSERT(!runtime); + runtime = rt; + JS_LOCK_GC(rt); + } + + private: + JSRuntime *runtime; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + class AutoUnlockGC { private: JSRuntime *rt; diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index e9f8d0590fa1b..f4bd7aa161166 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -609,29 +609,6 @@ js::DumpHeapComplete(JSRuntime *rt, FILE *fp) namespace js { -/* static */ void -AutoLockGC::LockGC(JSRuntime *rt) -{ - JS_ASSERT(rt); - JS_LOCK_GC(rt); -} - -/* static */ void -AutoLockGC::UnlockGC(JSRuntime *rt) -{ - JS_ASSERT(rt); - JS_UNLOCK_GC(rt); -} - -void -AutoLockGC::lock(JSRuntime *rt) -{ - JS_ASSERT(rt); - JS_ASSERT(!runtime); - runtime = rt; - JS_LOCK_GC(rt); -} - JS_FRIEND_API(const JSStructuredCloneCallbacks *) GetContextStructuredCloneCallbacks(JSContext *cx) { @@ -674,12 +651,6 @@ GetContextOutstandingRequests(const JSContext *cx) return cx->outstandingRequests; } -JS_FRIEND_API(PRLock *) -GetRuntimeGCLock(const JSRuntime *rt) -{ - return rt->gcLock; -} - AutoSkipConservativeScan::AutoSkipConservativeScan(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) : context(cx) @@ -726,12 +697,6 @@ IsContextRunningJS(JSContext *cx) return !cx->stack.empty(); } -JS_FRIEND_API(void) -TriggerOperationCallback(JSRuntime *rt) -{ - rt->triggerOperationCallback(); -} - JS_FRIEND_API(const CompartmentVector&) GetRuntimeCompartments(JSRuntime *rt) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index a2273d29929fd..b4d2f04182649 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -187,8 +187,6 @@ JS_END_EXTERN_C #ifdef __cplusplus -struct PRLock; - namespace js { struct ContextFriendFields { @@ -573,9 +571,6 @@ GetOwnerThread(const JSContext *cx); JS_FRIEND_API(unsigned) GetContextOutstandingRequests(const JSContext *cx); -JS_FRIEND_API(PRLock *) -GetRuntimeGCLock(const JSRuntime *rt); - class JS_FRIEND_API(AutoSkipConservativeScan) { public: @@ -600,43 +595,11 @@ typedef void /* * Sets a callback that is run whenever the runtime goes idle - the * last active request ceases - and begins activity - when it was - * idle and a request begins. Note: The callback is called under the - * GC lock. + * idle and a request begins. */ JS_FRIEND_API(void) SetActivityCallback(JSRuntime *rt, ActivityCallback cb, void *arg); -class JS_FRIEND_API(AutoLockGC) -{ - public: - explicit AutoLockGC(JSRuntime *rt = NULL - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : runtime(rt) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - if (rt) - LockGC(rt); - } - - ~AutoLockGC() - { - if (runtime) - UnlockGC(runtime); - } - - bool locked() const { - return !!runtime; - } - void lock(JSRuntime *rt); - - private: - static void LockGC(JSRuntime *rt); - static void UnlockGC(JSRuntime *rt); - - JSRuntime *runtime; - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - extern JS_FRIEND_API(const JSStructuredCloneCallbacks *) GetContextStructuredCloneCallbacks(JSContext *cx); @@ -652,10 +615,6 @@ CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value * extern JS_FRIEND_API(bool) IsContextRunningJS(JSContext *cx); -/* Must be called with GC lock taken. */ -extern JS_FRIEND_API(void) -TriggerOperationCallback(JSRuntime *rt); - class SystemAllocPolicy; typedef Vector CompartmentVector; extern JS_FRIEND_API(const CompartmentVector&) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index fefa6680d9dd9..8ad28fd7b6f50 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1308,15 +1308,6 @@ js_AddGCThingRoot(JSContext *cx, void **rp, const char *name) JS_FRIEND_API(JSBool) js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name) { - /* - * Due to the long-standing, but now removed, use of rt->gcLock across the - * bulk of js::GC, API users have come to depend on JS_AddRoot etc. locking - * properly with a racing GC, without calling JS_AddRoot from a request. - * We have to preserve API compatibility here, now that we avoid holding - * rt->gcLock across the mark phase (including the root hashtable mark). - */ - AutoLockGC lock(rt); - return !!rt->gcRootsHash.put((void *)vp, RootInfo(name, JS_GC_ROOT_VALUE_PTR)); } @@ -1324,15 +1315,6 @@ js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name) JS_FRIEND_API(JSBool) js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name) { - /* - * Due to the long-standing, but now removed, use of rt->gcLock across the - * bulk of js::GC, API users have come to depend on JS_AddRoot etc. locking - * properly with a racing GC, without calling JS_AddRoot from a request. - * We have to preserve API compatibility here, now that we avoid holding - * rt->gcLock across the mark phase (including the root hashtable mark). - */ - AutoLockGC lock(rt); - return !!rt->gcRootsHash.put((void *)rp, RootInfo(name, JS_GC_ROOT_GCTHING_PTR)); } @@ -1340,11 +1322,6 @@ js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name) JS_FRIEND_API(JSBool) js_RemoveRoot(JSRuntime *rt, void *rp) { - /* - * Due to the JS_RemoveRootRT API, we may be called outside of a request. - * Same synchronization drill as above in js_AddRoot. - */ - AutoLockGC lock(rt); rt->gcRootsHash.remove(rp); rt->gcPoke = JS_TRUE; return JS_TRUE; @@ -1404,7 +1381,6 @@ js_DumpNamedRoots(JSRuntime *rt, uint32_t js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) { - AutoLockGC lock(rt); int ct = 0; for (RootEnum e(rt->gcRootsHash); !e.empty(); e.popFront()) { RootEntry &entry = e.front(); @@ -1795,7 +1771,6 @@ js_LockGCThingRT(JSRuntime *rt, void *thing) if (!thing) return true; - AutoLockGC lock(rt); if (GCLocks::Ptr p = rt->gcLocksHash.lookupWithDefault(thing, 0)) { p->value++; return true; @@ -1810,10 +1785,7 @@ js_UnlockGCThingRT(JSRuntime *rt, void *thing) if (!thing) return; - AutoLockGC lock(rt); - GCLocks::Ptr p = rt->gcLocksHash.lookup(thing); - - if (p) { + if (GCLocks::Ptr p = rt->gcLocksHash.lookup(thing)) { rt->gcPoke = true; if (--p->value == 0) rt->gcLocksHash.remove(p); @@ -2417,6 +2389,7 @@ MarkRuntime(JSTracer *trc, bool useSavedRoots = false) } } } + void TriggerGC(JSRuntime *rt, gcreason::Reason reason) { @@ -2463,7 +2436,7 @@ TriggerCompartmentGC(JSCompartment *comp, gcreason::Reason reason) rt->gcIsNeeded = true; rt->gcTriggerCompartment = comp; rt->gcTriggerReason = reason; - comp->rt->triggerOperationCallback(); + rt->triggerOperationCallback(); } void @@ -4516,7 +4489,6 @@ JS_FRIEND_API(void) StartPCCountProfiling(JSContext *cx) { JSRuntime *rt = cx->runtime; - AutoLockGC lock(rt); if (rt->profilingScripts) return; @@ -4533,7 +4505,6 @@ JS_FRIEND_API(void) StopPCCountProfiling(JSContext *cx) { JSRuntime *rt = cx->runtime; - AutoLockGC lock(rt); if (!rt->profilingScripts) return; @@ -4566,7 +4537,6 @@ JS_FRIEND_API(void) PurgePCCounts(JSContext *cx) { JSRuntime *rt = cx->runtime; - AutoLockGC lock(rt); if (!rt->scriptPCCounters) return; @@ -4585,6 +4555,10 @@ JS_IterateCompartments(JSRuntime *rt, void *data, AutoLockGC lock(rt); AutoHeapSession session(rt); +#ifdef JS_THREADSAFE + rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); +#endif + AutoUnlockGC unlock(rt); for (CompartmentsIter c(rt); !c.done(); c.next()) (*compartmentCallback)(rt, data, c); diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index d344b888d3559..a36d154edea08 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2179,17 +2179,6 @@ TypeCompartment::nukeTypes(JSContext *cx) pendingRecompiles = NULL; } - /* - * We may or may not be under the GC. In either case don't allocate, and - * acquire the GC lock so we can update inferenceEnabled for all contexts. - */ - -#ifdef JS_THREADSAFE - AutoLockGC maybeLock; - if (!cx->runtime->gcRunning) - maybeLock.lock(cx->runtime); -#endif - inferenceEnabled = false; /* Update the cached inferenceEnabled bit in all contexts. */ diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index d2d06854a79ee..77e6732a4f2a5 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3134,7 +3134,7 @@ CancelExecution(JSRuntime *rt) if (gWorkerThreadPool) js::workers::terminateAll(gWorkerThreadPool); #endif - JS_TriggerRuntimeOperationCallback(rt); + JS_TriggerOperationCallback(rt); static const char msg[] = "Script runs for too long, terminating.\n"; #if defined(XP_UNIX) && !defined(JS_THREADSAFE) diff --git a/js/src/shell/jsworkers.cpp b/js/src/shell/jsworkers.cpp index b319254b2ff6c..963000fbbb809 100644 --- a/js/src/shell/jsworkers.cpp +++ b/js/src/shell/jsworkers.cpp @@ -827,7 +827,7 @@ class Worker MOZ_FINAL : public WorkerParent AutoLock hold(lock); terminateFlag = true; if (current) - JS_TriggerOperationCallback(context); + JS_TriggerOperationCallback(runtime); } void notifyTerminating() { diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index a4e45665ccccf..f373a67711cfd 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -429,7 +429,6 @@ Debugger::Debugger(JSContext *cx, JSObject *dbg) assertSameCompartment(cx, dbg); JSRuntime *rt = cx->runtime; - AutoLockGC lock(rt); JS_APPEND_LINK(&link, &rt->debuggerList); JS_INIT_CLIST(&breakpoints); } diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 4bb7f9dece0b7..15cdcc970ecd7 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -926,6 +926,20 @@ XPCJSRuntime::FinalizeCallback(JSContext *cx, JSFinalizeStatus status) } } +class AutoLockWatchdog { + XPCJSRuntime* const mRuntime; + + public: + AutoLockWatchdog(XPCJSRuntime* aRuntime) + : mRuntime(aRuntime) { + PR_Lock(mRuntime->mWatchdogLock); + } + + ~AutoLockWatchdog() { + PR_Unlock(mRuntime->mWatchdogLock); + } +}; + //static void XPCJSRuntime::WatchdogMain(void *arg) @@ -933,7 +947,7 @@ XPCJSRuntime::WatchdogMain(void *arg) XPCJSRuntime* self = static_cast(arg); // Lock lasts until we return - js::AutoLockGC lock(self->mJSRuntime); + AutoLockWatchdog lock(self); PRIntervalTime sleepInterval; while (self->mWatchdogThread) { @@ -944,12 +958,8 @@ XPCJSRuntime::WatchdogMain(void *arg) sleepInterval = PR_INTERVAL_NO_TIMEOUT; self->mWatchdogHibernating = true; } -#ifdef DEBUG - PRStatus status = -#endif - PR_WaitCondVar(self->mWatchdogWakeup, sleepInterval); - JS_ASSERT(status == PR_SUCCESS); - js::TriggerOperationCallback(self->mJSRuntime); + MOZ_ALWAYS_TRUE(PR_WaitCondVar(self->mWatchdogWakeup, sleepInterval) == PR_SUCCESS); + JS_TriggerOperationCallback(self->mJSRuntime); } /* Wake up the main thread waiting for the watchdog to terminate. */ @@ -961,6 +971,9 @@ void XPCJSRuntime::ActivityCallback(void *arg, JSBool active) { XPCJSRuntime* self = static_cast(arg); + + AutoLockWatchdog lock(self); + if (active) { self->mLastActiveTime = -1; if (self->mWatchdogHibernating) { @@ -1058,7 +1071,7 @@ XPCJSRuntime::~XPCJSRuntime() // must release the lock before calling PR_DestroyCondVar, we use an // extra block here. { - js::AutoLockGC lock(mJSRuntime); + AutoLockWatchdog lock(this); if (mWatchdogThread) { mWatchdogThread = nsnull; PR_NotifyCondVar(mWatchdogWakeup); @@ -1066,6 +1079,7 @@ XPCJSRuntime::~XPCJSRuntime() } } PR_DestroyCondVar(mWatchdogWakeup); + PR_DestroyLock(mWatchdogLock); mWatchdogWakeup = nsnull; } @@ -2029,6 +2043,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) mVariantRoots(nsnull), mWrappedJSRoots(nsnull), mObjectHolderRoots(nsnull), + mWatchdogLock(nsnull), mWatchdogWakeup(nsnull), mWatchdogThread(nsnull), mWatchdogHibernating(false), @@ -2053,47 +2068,40 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) if (!mJSRuntime) NS_RUNTIMEABORT("JS_NewRuntime failed."); - { - // Unconstrain the runtime's threshold on nominal heap size, to avoid - // triggering GC too often if operating continuously near an arbitrary - // finite threshold (0xffffffff is infinity for uint32_t parameters). - // This leaves the maximum-JS_malloc-bytes threshold still in effect - // to cause period, and we hope hygienic, last-ditch GCs from within - // the GC's allocator. - JS_SetGCParameter(mJSRuntime, JSGC_MAX_BYTES, 0xffffffff); + // Unconstrain the runtime's threshold on nominal heap size, to avoid + // triggering GC too often if operating continuously near an arbitrary + // finite threshold (0xffffffff is infinity for uint32_t parameters). + // This leaves the maximum-JS_malloc-bytes threshold still in effect + // to cause period, and we hope hygienic, last-ditch GCs from within + // the GC's allocator. + JS_SetGCParameter(mJSRuntime, JSGC_MAX_BYTES, 0xffffffff); #ifdef MOZ_ASAN - // ASan requires more stack space due to redzones - JS_SetNativeStackQuota(mJSRuntime, 2 * 128 * sizeof(size_t) * 1024); + // ASan requires more stack space due to redzones + JS_SetNativeStackQuota(mJSRuntime, 2 * 128 * sizeof(size_t) * 1024); #else - JS_SetNativeStackQuota(mJSRuntime, 128 * sizeof(size_t) * 1024); + JS_SetNativeStackQuota(mJSRuntime, 128 * sizeof(size_t) * 1024); #endif - JS_SetContextCallback(mJSRuntime, ContextCallback); - JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback); - JS_SetGCCallback(mJSRuntime, GCCallback); - JS_SetFinalizeCallback(mJSRuntime, FinalizeCallback); - JS_SetExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this); - JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this); - JS_SetWrapObjectCallbacks(mJSRuntime, - xpc::WrapperFactory::Rewrap, - xpc::WrapperFactory::PrepareForWrapping); - js::SetPreserveWrapperCallback(mJSRuntime, PreserveWrapper); - + JS_SetContextCallback(mJSRuntime, ContextCallback); + JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback); + JS_SetGCCallback(mJSRuntime, GCCallback); + JS_SetFinalizeCallback(mJSRuntime, FinalizeCallback); + JS_SetExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this); + JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this); + JS_SetWrapObjectCallbacks(mJSRuntime, + xpc::WrapperFactory::Rewrap, + xpc::WrapperFactory::PrepareForWrapping); + js::SetPreserveWrapperCallback(mJSRuntime, PreserveWrapper); #ifdef MOZ_CRASHREPORTER - JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback); + JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback); #endif - JS_SetAccumulateTelemetryCallback(mJSRuntime, AccumulateTelemetryCallback); - mWatchdogWakeup = PR_NewCondVar(js::GetRuntimeGCLock(mJSRuntime)); - if (!mWatchdogWakeup) - NS_RUNTIMEABORT("PR_NewCondVar failed."); - - js::SetActivityCallback(mJSRuntime, ActivityCallback, this); - - NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap)); - NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSSystemCompartmentCount)); - NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSUserCompartmentCount)); - NS_RegisterMemoryMultiReporter(new JSMemoryMultiReporter); - NS_RegisterMemoryMultiReporter(new JSCompartmentsMultiReporter); - } + JS_SetAccumulateTelemetryCallback(mJSRuntime, AccumulateTelemetryCallback); + js::SetActivityCallback(mJSRuntime, ActivityCallback, this); + + NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap)); + NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSSystemCompartmentCount)); + NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSUserCompartmentCount)); + NS_RegisterMemoryMultiReporter(new JSMemoryMultiReporter); + NS_RegisterMemoryMultiReporter(new JSCompartmentsMultiReporter); if (!JS_DHashTableInit(&mJSHolders, JS_DHashGetStubOps(), nsnull, sizeof(ObjectHolder), 512)) @@ -2103,12 +2111,19 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) // Install a JavaScript 'debugger' keyword handler in debug builds only #ifdef DEBUG - if (mJSRuntime && !JS_GetGlobalDebugHooks(mJSRuntime)->debuggerHandler) + if (!JS_GetGlobalDebugHooks(mJSRuntime)->debuggerHandler) xpc_InstallJSDebuggerKeywordHandler(mJSRuntime); #endif - if (mWatchdogWakeup) { - js::AutoLockGC lock(mJSRuntime); + mWatchdogLock = PR_NewLock(); + if (!mWatchdogLock) + NS_RUNTIMEABORT("PR_NewLock failed."); + mWatchdogWakeup = PR_NewCondVar(mWatchdogLock); + if (!mWatchdogWakeup) + NS_RUNTIMEABORT("PR_NewCondVar failed."); + + { + AutoLockWatchdog lock(this); mWatchdogThread = PR_CreateThread(PR_USER_THREAD, WatchdogMain, this, PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index d76013bb3710e..222f8677e32a4 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -837,11 +837,14 @@ class XPCJSRuntime XPCRootSetElem *mWrappedJSRoots; XPCRootSetElem *mObjectHolderRoots; JSDHashTable mJSHolders; + PRLock *mWatchdogLock; PRCondVar *mWatchdogWakeup; PRThread *mWatchdogThread; nsTArray extraGCCallbacks; bool mWatchdogHibernating; PRTime mLastActiveTime; // -1 if active NOW + + friend class AutoLockWatchdog; }; /***************************************************************************/