From 01a649c1977e3389d1195c82dfd323a798c35dd3 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Fri, 10 Feb 2017 16:47:50 -0700 Subject: [PATCH] Bug 1337112 - Remove links from JSRuntime to its single context and zone group, r=jandem,jonco. --HG-- extra : rebase_source : ebf319af724d5f829cdf5c2386dc82d49be89989 --- dom/base/nsFrameMessageManager.cpp | 2 +- dom/base/nsGlobalWindow.cpp | 2 +- dom/bindings/SimpleGlobalObject.cpp | 2 +- dom/workers/RuntimeService.cpp | 14 +-- ipc/testshell/XPCShellEnvironment.cpp | 2 +- js/src/builtin/TestingFunctions.cpp | 14 ++- js/src/gc/GCRuntime.h | 3 +- js/src/gc/Nursery.cpp | 1 - js/src/gc/Zone.h | 4 - js/src/gc/ZoneGroup.cpp | 5 ++ js/src/gc/ZoneGroup.h | 36 -------- js/src/jsapi.cpp | 50 ++++++++--- js/src/jsapi.h | 55 ++++++++---- js/src/jscntxt.cpp | 26 +++++- js/src/jscompartment.h | 4 - js/src/jsgc.cpp | 92 +++++++++++++++++--- js/src/jsgc.h | 2 +- js/src/shell/js.cpp | 12 +-- js/src/vm/GlobalObject.cpp | 19 +--- js/src/vm/HelperThreads.cpp | 23 +++-- js/src/vm/Runtime.cpp | 15 ---- js/src/vm/Runtime.h | 18 ---- js/src/vm/SelfHosting.cpp | 4 +- js/xpconnect/loader/mozJSComponentLoader.cpp | 2 +- js/xpconnect/src/Sandbox.cpp | 6 +- js/xpconnect/src/XPCShellImpl.cpp | 2 +- netwerk/base/ProxyAutoConfig.cpp | 2 +- xpcom/base/CycleCollectedJSContext.cpp | 4 +- xpcom/base/CycleCollectedJSContext.h | 2 +- 29 files changed, 239 insertions(+), 184 deletions(-) diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index 837f2ddbb56d..ebccf50c8598 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -1655,7 +1655,7 @@ nsMessageManagerScriptExecutor::InitChildGlobalInternal( const uint32_t flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES; JS::CompartmentOptions options; - options.creationOptions().setZone(JS::SystemZone); + options.creationOptions().setSystemZone(); options.behaviors().setVersion(JSVERSION_LATEST); if (xpc::SharedMemoryEnabled()) { diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index d8ab0709fbd9..a0fca5dfaa65 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -2736,7 +2736,7 @@ CreateNativeGlobalForInner(JSContext* aCx, } if (top && top->GetGlobalJSObject()) { - options.creationOptions().setSameZoneAs(top->GetGlobalJSObject()); + options.creationOptions().setExistingZone(top->GetGlobalJSObject()); } options.creationOptions().setSecureContext(aIsSecureContext); diff --git a/dom/bindings/SimpleGlobalObject.cpp b/dom/bindings/SimpleGlobalObject.cpp index d4d1787f10a3..5659138c852f 100644 --- a/dom/bindings/SimpleGlobalObject.cpp +++ b/dom/bindings/SimpleGlobalObject.cpp @@ -110,7 +110,7 @@ SimpleGlobalObject::Create(GlobalType globalType, JS::Handle proto) // lots of zones for what are probably very short-lived // compartments. This should help them be GCed quicker and take up // less memory before they're GCed. - .setZone(JS::SystemZone); + .setSystemZone(); if (NS_IsMainThread()) { nsCOMPtr principal = nsNullPrincipal::Create(); diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 27c0897060f5..327a851215d3 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -1075,10 +1075,10 @@ public: mWorkerPrivate = nullptr; } - nsresult Initialize(JSContext* aParentContext) + nsresult Initialize(JSRuntime* aParentRuntime) { nsresult rv = - CycleCollectedJSContext::Initialize(aParentContext, + CycleCollectedJSContext::Initialize(aParentRuntime, WORKER_DEFAULT_RUNTIME_HEAPSIZE, WORKER_DEFAULT_NURSERY_SIZE); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -1187,7 +1187,7 @@ class WorkerThreadPrimaryRunnable final : public Runnable { WorkerPrivate* mWorkerPrivate; RefPtr mThread; - JSContext* mParentContext; + JSRuntime* mParentRuntime; class FinishedRunnable final : public Runnable { @@ -1213,8 +1213,8 @@ class WorkerThreadPrimaryRunnable final : public Runnable public: WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate, WorkerThread* aThread, - JSContext* aParentContext) - : mWorkerPrivate(aWorkerPrivate), mThread(aThread), mParentContext(aParentContext) + JSRuntime* aParentRuntime) + : mWorkerPrivate(aWorkerPrivate), mThread(aThread), mParentRuntime(aParentRuntime) { MOZ_ASSERT(aWorkerPrivate); MOZ_ASSERT(aThread); @@ -1893,7 +1893,7 @@ RuntimeService::ScheduleWorker(WorkerPrivate* aWorkerPrivate) JSContext* cx = CycleCollectedJSContext::Get()->Context(); nsCOMPtr runnable = new WorkerThreadPrimaryRunnable(aWorkerPrivate, thread, - JS_GetParentContext(cx)); + JS_GetParentRuntime(cx)); if (NS_FAILED(thread->DispatchPrimaryRunnable(friendKey, runnable.forget()))) { UnregisterWorker(aWorkerPrivate); return false; @@ -2861,7 +2861,7 @@ WorkerThreadPrimaryRunnable::Run() nsCycleCollector_startup(); WorkerJSContext context(mWorkerPrivate); - nsresult rv = context.Initialize(mParentContext); + nsresult rv = context.Initialize(mParentRuntime); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } diff --git a/ipc/testshell/XPCShellEnvironment.cpp b/ipc/testshell/XPCShellEnvironment.cpp index bf8d72e05743..fbc9238fcdaf 100644 --- a/ipc/testshell/XPCShellEnvironment.cpp +++ b/ipc/testshell/XPCShellEnvironment.cpp @@ -451,7 +451,7 @@ XPCShellEnvironment::Init() } JS::CompartmentOptions options; - options.creationOptions().setZone(JS::SystemZone); + options.creationOptions().setSystemZone(); options.behaviors().setVersion(JSVERSION_LATEST); if (xpc::SharedMemoryEnabled()) options.creationOptions().setSharedMemoryAndAtomicsEnabled(true); diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 83337272b0a7..9a8d11c86051 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -347,6 +347,7 @@ MinorGC(JSContext* cx, unsigned argc, Value* vp) #define FOR_EACH_GC_PARAM(_) \ _("maxBytes", JSGC_MAX_BYTES, true) \ _("maxMallocBytes", JSGC_MAX_MALLOC_BYTES, true) \ + _("maxNurseryBytes", JSGC_MAX_NURSERY_BYTES, true) \ _("gcBytes", JSGC_BYTES, false) \ _("gcNumber", JSGC_NUMBER, false) \ _("mode", JSGC_MODE, true) \ @@ -421,9 +422,16 @@ GCParameter(JSContext* cx, unsigned argc, Value* vp) return false; } - if (disableOOMFunctions && (param == JSGC_MAX_BYTES || param == JSGC_MAX_MALLOC_BYTES)) { - args.rval().setUndefined(); - return true; + if (disableOOMFunctions) { + switch (param) { + case JSGC_MAX_BYTES: + case JSGC_MAX_MALLOC_BYTES: + case JSGC_MAX_NURSERY_BYTES: + args.rval().setUndefined(); + return true; + default: + break; + } } double d; diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index b2acca773ab0..2b4fe92af6a2 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -980,8 +980,9 @@ class GCRuntime public: JSRuntime* const rt; - /* Embedders can use this zone however they wish. */ + /* Embedders can use this zone and group however they wish. */ UnprotectedData systemZone; + UnprotectedData systemZoneGroup; // List of all zone groups (protected by the GC lock). ActiveThreadOrGCTaskData groups; diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 1af9968d744a..72db3954120c 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -553,7 +553,6 @@ void js::Nursery::collect(JS::gcreason::Reason reason) { MOZ_ASSERT(!TlsContext.get()->suppressGC); - MOZ_RELEASE_ASSERT(zoneGroup()->ownedByCurrentThread()); if (!isEnabled() || isEmpty()) { // Our barriers are not always exact, and there may be entries in the diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index e73c78c4de21..6a81b9edb3bb 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -562,10 +562,6 @@ struct Zone : public JS::shadow::Zone, source->uniqueIds().clear(); } - JSContext* contextFromMainThread() { - return runtime_->contextFromMainThread(); - } - #ifdef JSGC_HASH_TABLE_CHECKS // Assert that the UniqueId table has been redirected successfully. void checkUniqueIdTableAfterMovingGC(); diff --git a/js/src/gc/ZoneGroup.cpp b/js/src/gc/ZoneGroup.cpp index 882ad6a47ed2..ada3440b5784 100644 --- a/js/src/gc/ZoneGroup.cpp +++ b/js/src/gc/ZoneGroup.cpp @@ -48,6 +48,9 @@ ZoneGroup::init(size_t maxNurseryBytes) ZoneGroup::~ZoneGroup() { js_delete(jitZoneGroup.ref()); + + if (this == runtime->gc.systemZoneGroup) + runtime->gc.systemZoneGroup = nullptr; } void @@ -60,6 +63,8 @@ ZoneGroup::enter() MOZ_ASSERT(ownerContext().context() == nullptr); MOZ_ASSERT(enterCount == 0); ownerContext_ = CooperatingContext(cx); + if (cx->generationalDisabled) + nursery().disable(); } enterCount++; } diff --git a/js/src/gc/ZoneGroup.h b/js/src/gc/ZoneGroup.h index f563c3bfa9ed..f0b3e10e9b3f 100644 --- a/js/src/gc/ZoneGroup.h +++ b/js/src/gc/ZoneGroup.h @@ -130,42 +130,6 @@ class ZoneGroup mozilla::LinkedList& debuggerList() { return debuggerList_.ref(); } }; -class MOZ_RAII AutoAccessZoneGroup -{ - ZoneGroup* group; - - public: - explicit AutoAccessZoneGroup(ZoneGroup* group) - : group(group) - { - group->enter(); - } - - ~AutoAccessZoneGroup() { - group->leave(); - } -}; - -class MOZ_RAII AutoAccessZoneGroups -{ - Vector acquiredGroups; - - public: - AutoAccessZoneGroups() {} - - ~AutoAccessZoneGroups() { - for (size_t i = 0; i < acquiredGroups.length(); i++) - acquiredGroups[i]->leave(); - } - - void access(ZoneGroup* group) { - group->enter(); - AutoEnterOOMUnsafeRegion oomUnsafe; - if (!acquiredGroups.append(group)) - oomUnsafe.crash("acquiredGroups.append failed"); - } -}; - } // namespace js #endif // gc_Zone_h diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 7ea81c652dfd..d8b6bc9dc92b 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -463,18 +463,14 @@ JS_FRIEND_API(bool) JS::isGCEnabled() { return true; } #endif JS_PUBLIC_API(JSContext*) -JS_NewContext(uint32_t maxbytes, uint32_t maxNurseryBytes, JSContext* parentContext) +JS_NewContext(uint32_t maxbytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime) { MOZ_ASSERT(JS::detail::libraryInitState == JS::detail::InitState::Running, "must call JS_Init prior to creating any JSContexts"); // Make sure that all parent runtimes are the topmost parent. - JSRuntime* parentRuntime = nullptr; - if (parentContext) { - parentRuntime = parentContext->runtime(); - while (parentRuntime && parentRuntime->parentRuntime) - parentRuntime = parentRuntime->parentRuntime; - } + while (parentRuntime && parentRuntime->parentRuntime) + parentRuntime = parentRuntime->parentRuntime; return NewContext(maxbytes, maxNurseryBytes, parentRuntime); } @@ -546,10 +542,10 @@ JS_EndRequest(JSContext* cx) StopRequest(cx); } -JS_PUBLIC_API(JSContext*) -JS_GetParentContext(JSContext* cx) +JS_PUBLIC_API(JSRuntime*) +JS_GetParentRuntime(JSContext* cx) { - return cx->runtime()->parentRuntime ? cx->runtime()->parentRuntime->unsafeContextFromAnyThread() : cx; + return cx->runtime()->parentRuntime ? cx->runtime()->parentRuntime : cx->runtime(); } JS_PUBLIC_API(JSVersion) @@ -1743,16 +1739,42 @@ JS::CompartmentBehaviors::extraWarnings(JSContext* cx) const } JS::CompartmentCreationOptions& -JS::CompartmentCreationOptions::setZone(ZoneSpecifier spec) +JS::CompartmentCreationOptions::setSystemZone() { - zone_.spec = spec; + zoneSpec_ = JS::SystemZone; + zonePointer_ = nullptr; return *this; } JS::CompartmentCreationOptions& -JS::CompartmentCreationOptions::setSameZoneAs(JSObject* obj) +JS::CompartmentCreationOptions::setExistingZone(JSObject* obj) { - zone_.pointer = static_cast(obj->zone()); + zoneSpec_ = JS::ExistingZone; + zonePointer_ = obj->zone(); + return *this; +} + +JS::CompartmentCreationOptions& +JS::CompartmentCreationOptions::setNewZoneInNewZoneGroup() +{ + zoneSpec_ = JS::NewZoneInNewZoneGroup; + zonePointer_ = nullptr; + return *this; +} + +JS::CompartmentCreationOptions& +JS::CompartmentCreationOptions::setNewZoneInSystemZoneGroup() +{ + zoneSpec_ = JS::NewZoneInSystemZoneGroup; + zonePointer_ = nullptr; + return *this; +} + +JS::CompartmentCreationOptions& +JS::CompartmentCreationOptions::setNewZoneInExistingZoneGroup(JSObject* obj) +{ + zoneSpec_ = JS::NewZoneInExistingZoneGroup; + zonePointer_ = obj->zone()->group(); return *this; } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 6a209fa5819f..cfda8c7c460a 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -981,7 +981,7 @@ JS_IsBuiltinFunctionConstructor(JSFunction* fun); extern JS_PUBLIC_API(JSContext*) JS_NewContext(uint32_t maxbytes, uint32_t maxNurseryBytes = JS::DefaultNurseryBytes, - JSContext* parentContext = nullptr); + JSRuntime* parentRuntime = nullptr); extern JS_PUBLIC_API(void) JS_DestroyContext(JSContext* cx); @@ -992,8 +992,8 @@ JS_GetContextPrivate(JSContext* cx); JS_PUBLIC_API(void) JS_SetContextPrivate(JSContext* cx, void* data); -extern JS_PUBLIC_API(JSContext*) -JS_GetParentContext(JSContext* cx); +extern JS_PUBLIC_API(JSRuntime*) +JS_GetParentRuntime(JSContext* cx); extern JS_PUBLIC_API(void) JS_BeginRequest(JSContext* cx); @@ -1710,6 +1710,9 @@ typedef enum JSGCParamKey { /** Number of JS_malloc bytes before last ditch GC. */ JSGC_MAX_MALLOC_BYTES = 1, + /** Maximum size of the generational GC nurseries. */ + JSGC_MAX_NURSERY_BYTES = 2, + /** Amount of bytes allocated by the GC. */ JSGC_BYTES = 3, @@ -2178,9 +2181,23 @@ JS_GetConstructor(JSContext* cx, JS::Handle proto); namespace JS { +// Specification for which zone a newly created compartment should use. enum ZoneSpecifier { - FreshZone = 0, - SystemZone = 1 + // Use the single runtime wide system zone. The meaning of this zone is + // left to the embedder. + SystemZone, + + // Use a particular existing zone. + ExistingZone, + + // Create a new zone with its own new zone group. + NewZoneInNewZoneGroup, + + // Create a new zone in the same zone group as the system zone. + NewZoneInSystemZoneGroup, + + // Create a new zone in the same zone group as another existing zone. + NewZoneInExistingZoneGroup }; /** @@ -2197,6 +2214,8 @@ class JS_PUBLIC_API(CompartmentCreationOptions) CompartmentCreationOptions() : addonId_(nullptr), traceGlobal_(nullptr), + zoneSpec_(NewZoneInSystemZoneGroup), + zonePointer_(nullptr), invisibleToDebugger_(false), mergeable_(false), preserveJitCode_(false), @@ -2204,9 +2223,7 @@ class JS_PUBLIC_API(CompartmentCreationOptions) experimentalNumberFormatFormatToPartsEnabled_(false), sharedMemoryAndAtomics_(false), secureContext_(false) - { - zone_.spec = JS::FreshZone; - } + {} // A null add-on ID means that the compartment is not associated with an // add-on. @@ -2224,13 +2241,15 @@ class JS_PUBLIC_API(CompartmentCreationOptions) return *this; } - void* zonePointer() const { - MOZ_ASSERT(uintptr_t(zone_.pointer) > uintptr_t(JS::SystemZone)); - return zone_.pointer; - } - ZoneSpecifier zoneSpecifier() const { return zone_.spec; } - CompartmentCreationOptions& setZone(ZoneSpecifier spec); - CompartmentCreationOptions& setSameZoneAs(JSObject* obj); + void* zonePointer() const { return zonePointer_; } + ZoneSpecifier zoneSpecifier() const { return zoneSpec_; } + + // Set the zone to use for the compartment. See ZoneSpecifier above. + CompartmentCreationOptions& setSystemZone(); + CompartmentCreationOptions& setExistingZone(JSObject* obj); + CompartmentCreationOptions& setNewZoneInNewZoneGroup(); + CompartmentCreationOptions& setNewZoneInSystemZoneGroup(); + CompartmentCreationOptions& setNewZoneInExistingZoneGroup(JSObject* obj); // Certain scopes (i.e. XBL compilation scopes) are implementation details // of the embedding, and references to them should never leak out to script. @@ -2299,10 +2318,8 @@ class JS_PUBLIC_API(CompartmentCreationOptions) private: JSAddonId* addonId_; JSTraceOp traceGlobal_; - union { - ZoneSpecifier spec; - void* pointer; // js::Zone* is not exposed in the API. - } zone_; + ZoneSpecifier zoneSpec_; + void* zonePointer_; // Per zoneSpec_, either a Zone, ZoneGroup, or null. bool invisibleToDebugger_; bool mergeable_; bool preserveJitCode_; diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 05bed58213ba..d36810ec963e 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -176,10 +176,28 @@ js::DestroyContext(JSContext* cx) cx->checkNoGCRooters(); - js_delete(cx->ionPcScriptCache.ref()); + // Cancel all off thread Ion compiles before destroying a cooperative + // context. Completed Ion compiles may try to interrupt arbitrary + // cooperative contexts which they have read off the owner context of a + // zone group. See HelperThread::handleIonWorkload. + CancelOffThreadIonCompile(cx->runtime()); - cx->runtime()->destroyRuntime(); - js_delete(cx->runtime()); + if (cx->runtime()->cooperatingContexts().length() == 1) { + // Destroy the runtime along with its last context. + cx->runtime()->destroyRuntime(); + js_delete(cx->runtime()); + } else { + DebugOnly found = false; + for (size_t i = 0; i < cx->runtime()->cooperatingContexts().length(); i++) { + CooperatingContext& target = cx->runtime()->cooperatingContexts()[i]; + if (cx == target.context()) { + cx->runtime()->cooperatingContexts().erase(&target); + found = true; + break; + } + } + MOZ_ASSERT(found); + } js_delete_poison(cx); } @@ -1034,6 +1052,8 @@ JSContext::~JSContext() /* Free the stuff hanging off of cx. */ MOZ_ASSERT(!resolvingList); + js_delete(ionPcScriptCache.ref()); + if (dtoaState) DestroyDtoaState(dtoaState); diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 9359785fd4b3..b2815cbd4f70 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -406,10 +406,6 @@ struct JSCompartment return runtime_; } - JSContext* contextFromMainThread() const { - return runtime_->contextFromMainThread(); - } - /* * Nb: global_ might be nullptr, if (a) it's the atoms compartment, or * (b) the compartment's global has been collected. The latter can happen diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 9cdad9f56a96..1f6277ef99ba 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -810,6 +810,7 @@ GCRuntime::releaseArena(Arena* arena, const AutoLockGC& lock) GCRuntime::GCRuntime(JSRuntime* rt) : rt(rt), systemZone(nullptr), + systemZoneGroup(nullptr), atomsZone(nullptr), stats_(rt), marker(rt), @@ -1044,6 +1045,7 @@ GCRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) * for default backward API compatibility. */ MOZ_ALWAYS_TRUE(tunables.setParameter(JSGC_MAX_BYTES, maxbytes, lock)); + MOZ_ALWAYS_TRUE(tunables.setParameter(JSGC_MAX_NURSERY_BYTES, maxNurseryBytes, lock)); setMaxMallocBytes(maxbytes); const char* size = getenv("JSGC_MARK_STACK_LIMIT"); @@ -1165,6 +1167,9 @@ GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, const AutoL case JSGC_MAX_BYTES: gcMaxBytes_ = value; break; + case JSGC_MAX_NURSERY_BYTES: + gcMaxNurseryBytes_ = value; + break; case JSGC_HIGH_FREQUENCY_TIME_LIMIT: highFrequencyThresholdUsec_ = value * PRMJ_USEC_PER_MSEC; break; @@ -3550,9 +3555,7 @@ GCRuntime::sweepZoneGroups(FreeOp* fop, bool destroyingRuntime) ZoneGroup* group = *read++; sweepZones(fop, group, destroyingRuntime); - // For now, the singleton zone group is not destroyed until the runtime - // itself is, bug 1323066. - if (group->zones().empty() && group != rt->zoneGroupFromMainThread()) { + if (group->zones().empty()) { MOZ_ASSERT(numActiveZoneIters == 0); fop->delete_(group); } else { @@ -6169,7 +6172,6 @@ namespace { class AutoScheduleZonesForGC { JSRuntime* rt_; - AutoAccessZoneGroups aazg; public: explicit AutoScheduleZonesForGC(JSRuntime* rt) : rt_(rt) { @@ -6187,9 +6189,6 @@ class AutoScheduleZonesForGC { zone->scheduleGC(); } - - if (zone->isGCScheduled() && !zone->isAtomsZone()) - aazg.access(zone->group()); } } @@ -6753,15 +6752,61 @@ AutoPrepareForTracing::AutoPrepareForTracing(JSContext* cx, ZoneSelector selecto } JSCompartment* -js::NewCompartment(JSContext* cx, Zone* zone, JSPrincipals* principals, +js::NewCompartment(JSContext* cx, JSPrincipals* principals, const JS::CompartmentOptions& options) { JSRuntime* rt = cx->runtime(); JS_AbortIfWrongThread(cx); + ScopedJSDeletePtr groupHolder; ScopedJSDeletePtr zoneHolder; + + Zone* zone = nullptr; + ZoneGroup* group = nullptr; + JS::ZoneSpecifier zoneSpec = options.creationOptions().zoneSpecifier(); + switch (zoneSpec) { + case JS::SystemZone: + // systemZone and possibly systemZoneGroup might be null here, in which + // case we'll make a zone/group and set these fields below. + zone = rt->gc.systemZone; + group = rt->gc.systemZoneGroup; + break; + case JS::ExistingZone: + zone = static_cast(options.creationOptions().zonePointer()); + MOZ_ASSERT(zone); + group = zone->group(); + break; + case JS::NewZoneInNewZoneGroup: + break; + case JS::NewZoneInSystemZoneGroup: + // As above, systemZoneGroup might be null here. + group = rt->gc.systemZoneGroup; + break; + case JS::NewZoneInExistingZoneGroup: + group = static_cast(options.creationOptions().zonePointer()); + MOZ_ASSERT(group); + break; + } + + if (!group) { + MOZ_ASSERT(!zone); + group = cx->new_(rt); + if (!group) + return nullptr; + + groupHolder.reset(group); + + if (!group->init(rt->gc.tunables.gcMaxNurseryBytes())) { + ReportOutOfMemory(cx); + return nullptr; + } + + if (cx->generationalDisabled) + group->nursery().disable(); + } + if (!zone) { - zone = cx->new_(cx->runtime(), rt->zoneGroupFromMainThread()); + zone = cx->new_(cx->runtime(), group); if (!zone) return nullptr; @@ -6789,12 +6834,35 @@ js::NewCompartment(JSContext* cx, Zone* zone, JSPrincipals* principals, return nullptr; } - if (zoneHolder && zoneHolder->group() && !zoneHolder->group()->zones().append(zone)) { - ReportOutOfMemory(cx); - return nullptr; + if (zoneHolder) { + if (!group->zones().append(zone)) { + ReportOutOfMemory(cx); + return nullptr; + } + + // Lazily set the runtime's sytem zone. + if (zoneSpec == JS::SystemZone) { + MOZ_RELEASE_ASSERT(!rt->gc.systemZone); + rt->gc.systemZone = zone; + zone->isSystem = true; + } + } + + if (groupHolder) { + if (!rt->gc.groups.ref().append(group)) { + ReportOutOfMemory(cx); + return nullptr; + } + + // Lazily set the runtime's system zone group. + if (zoneSpec == JS::SystemZone || zoneSpec == JS::NewZoneInSystemZoneGroup) { + MOZ_RELEASE_ASSERT(!rt->gc.systemZoneGroup); + rt->gc.systemZoneGroup = group; + } } zoneHolder.forget(); + groupHolder.forget(); return compartment.forget(); } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 258a2b3e6122..4a395e40c072 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1057,7 +1057,7 @@ extern void FinalizeStringRT(JSRuntime* rt, JSString* str); JSCompartment* -NewCompartment(JSContext* cx, JS::Zone* zone, JSPrincipals* principals, +NewCompartment(JSContext* cx, JSPrincipals* principals, const JS::CompartmentOptions& options); namespace gc { diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 3fe5518c8bd7..ebb731c4e18d 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3364,12 +3364,12 @@ EvalInContext(JSContext* cx, unsigned argc, Value* vp) struct WorkerInput { - JSContext* context; + JSRuntime* parentRuntime; char16_t* chars; size_t length; - WorkerInput(JSContext* context, char16_t* chars, size_t length) - : context(context), chars(chars), length(length) + WorkerInput(JSRuntime* parentRuntime, char16_t* chars, size_t length) + : parentRuntime(parentRuntime), chars(chars), length(length) {} ~WorkerInput() { @@ -3384,7 +3384,7 @@ WorkerMain(void* arg) { WorkerInput* input = (WorkerInput*) arg; - JSContext* cx = JS_NewContext(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->context); + JSContext* cx = JS_NewContext(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->parentRuntime); if (!cx) { js_delete(input); return; @@ -3513,7 +3513,7 @@ EvalInWorker(JSContext* cx, unsigned argc, Value* vp) CopyChars(chars, *str); - WorkerInput* input = js_new(JS_GetParentContext(cx), chars, str->length()); + WorkerInput* input = js_new(JS_GetParentRuntime(cx), chars, str->length()); if (!input) { ReportOutOfMemory(cx); return false; @@ -4846,7 +4846,7 @@ NewGlobal(JSContext* cx, unsigned argc, Value* vp) if (!JS_GetProperty(cx, opts, "sameZoneAs", &v)) return false; if (v.isObject()) - creationOptions.setSameZoneAs(UncheckedUnwrap(&v.toObject())); + creationOptions.setExistingZone(UncheckedUnwrap(&v.toObject())); if (!JS_GetProperty(cx, opts, "disableLazyParsing", &v)) return false; diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index d381480b6c3f..e8874589a468 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -346,27 +346,10 @@ GlobalObject::new_(JSContext* cx, const Class* clasp, JSPrincipals* principals, MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); - JSRuntime* rt = cx->runtime(); - - auto zoneSpecifier = options.creationOptions().zoneSpecifier(); - Zone* zone; - if (zoneSpecifier == JS::SystemZone) - zone = rt->gc.systemZone; - else if (zoneSpecifier == JS::FreshZone) - zone = nullptr; - else - zone = static_cast(options.creationOptions().zonePointer()); - - JSCompartment* compartment = NewCompartment(cx, zone, principals, options); + JSCompartment* compartment = NewCompartment(cx, principals, options); if (!compartment) return nullptr; - // Lazily create the system zone. - if (!rt->gc.systemZone && zoneSpecifier == JS::SystemZone) { - rt->gc.systemZone = compartment->zone(); - rt->gc.systemZone->isSystem = true; - } - Rooted global(cx); { AutoCompartment ac(cx, compartment); diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index 148546d28c81..58396a550440 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -541,7 +541,7 @@ CreateGlobalForOffThreadParse(JSContext* cx, ParseTaskKind kind, const gc::AutoS creationOptions.setInvisibleToDebugger(true) .setMergeable(true) - .setZone(JS::FreshZone); + .setNewZoneInSystemZoneGroup(); // Don't falsely inherit the host's global trace hook. creationOptions.setTrace(nullptr); @@ -1565,15 +1565,24 @@ HelperThread::handleIonWorkload(AutoLockHelperThreadState& locked) } FinishOffThreadIonCompile(builder, locked); + + // Ping any thread currently operating on the compiled script's zone group + // so that the compiled code can be incorporated at the next interrupt + // callback. Don't interrupt Ion code for this, as this incorporation can + // be delayed indefinitely without affecting performance as long as the + // main thread is actually executing Ion code. + // + // This must happen before the current task is reset. DestroyContext + // cancels in progress Ion compilations before destroying its target + // context, and after we reset the current task we are no longer considered + // to be Ion compiling. + JSContext* target = builder->script()->zoneFromAnyThread()->group()->ownerContext().context(); + if (target) + target->requestInterrupt(JSContext::RequestInterruptCanWait); + currentTask.reset(); pause = false; - // Ping the main thread so that the compiled code can be incorporated - // at the next interrupt callback. Don't interrupt Ion code for this, as - // this incorporation can be delayed indefinitely without affecting - // performance as long as the main thread is actually executing Ion code. - rt->unsafeContextFromAnyThread()->requestInterrupt(JSContext::RequestInterruptCanWait); - // Notify the main thread in case it is waiting for the compilation to finish. HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked); diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 89913ee06d1b..577cb6f901a1 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -143,8 +143,6 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) lcovOutput_(), jitRuntime_(nullptr), selfHostingGlobal_(nullptr), - singletonContext(nullptr), - singletonZoneGroup(nullptr), gc(thisFromCtor()), gcInitialized(false), NaNValue(DoubleNaNValue()), @@ -199,24 +197,13 @@ JSRuntime::init(JSContext* cx, uint32_t maxbytes, uint32_t maxNurseryBytes) if (!cooperatingContexts().append(cx)) return false; - singletonContext = cx; - defaultFreeOp_ = js_new(this); if (!defaultFreeOp_) return false; - ScopedJSDeletePtr zoneGroup(js_new(this)); - if (!zoneGroup) - return false; - singletonZoneGroup = zoneGroup; - if (!gc.init(maxbytes, maxNurseryBytes)) return false; - if (!zoneGroup->init(maxNurseryBytes) || !gc.groups.ref().append(zoneGroup)) - return false; - zoneGroup.forget(); - ScopedJSDeletePtr atomsZone(new_(this, nullptr)); if (!atomsZone || !atomsZone->init(true)) return false; @@ -342,8 +329,6 @@ JSRuntime::destroyRuntime() DebugOnly oldCount = liveRuntimesCount--; MOZ_ASSERT(oldCount > 0); - - js_delete(zoneGroupFromMainThread()); } void diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 94acbb99c5dd..5480c5ec6f3e 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -616,24 +616,6 @@ struct JSRuntime : public js::MallocProvider return !!jitRuntime_; } - // These will be removed soon. - private: - JSContext* singletonContext; - js::ZoneGroup* singletonZoneGroup; - public: - - JSContext* unsafeContextFromAnyThread() const { return singletonContext; } - JSContext* contextFromMainThread() const { - MOZ_ASSERT(CurrentThreadCanAccessRuntime(this)); - return singletonContext; - } - - js::ZoneGroup* zoneGroupFromAnyThread() const { return singletonZoneGroup; } - js::ZoneGroup* zoneGroupFromMainThread() const { - MOZ_ASSERT(CurrentThreadCanAccessRuntime(this)); - return singletonZoneGroup; - } - private: // Used to generate random keys for hash tables. mozilla::Maybe randomKeyGenerator_; diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index dc48a8dc086b..9c91df43e996 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -2745,10 +2745,10 @@ JSRuntime::createSelfHostingGlobal(JSContext* cx) MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); JS::CompartmentOptions options; - options.creationOptions().setZone(JS::FreshZone); + options.creationOptions().setNewZoneInSystemZoneGroup(); options.behaviors().setDiscardSource(true); - JSCompartment* compartment = NewCompartment(cx, nullptr, nullptr, options); + JSCompartment* compartment = NewCompartment(cx, nullptr, options); if (!compartment) return nullptr; diff --git a/js/xpconnect/loader/mozJSComponentLoader.cpp b/js/xpconnect/loader/mozJSComponentLoader.cpp index b5cb2a092c4b..2ec4cc02f250 100644 --- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -562,7 +562,7 @@ mozJSComponentLoader::PrepareObjectForLocation(JSContext* aCx, CompartmentOptions options; options.creationOptions() - .setZone(SystemZone) + .setSystemZone() .setAddonId(aReuseLoaderGlobal ? nullptr : MapURIToAddonID(aURI)); options.behaviors().setVersion(JSVERSION_LATEST); diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index 8882e7743b46..e7bf8b990c4c 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -1067,11 +1067,11 @@ xpc::CreateSandboxObject(JSContext* cx, MutableHandleValue vp, nsISupports* prin creationOptions.setSharedMemoryAndAtomicsEnabled(true); if (options.sameZoneAs) - creationOptions.setSameZoneAs(js::UncheckedUnwrap(options.sameZoneAs)); + creationOptions.setExistingZone(js::UncheckedUnwrap(options.sameZoneAs)); else if (options.freshZone) - creationOptions.setZone(JS::FreshZone); + creationOptions.setNewZoneInSystemZoneGroup(); else - creationOptions.setZone(JS::SystemZone); + creationOptions.setSystemZone(); creationOptions.setInvisibleToDebugger(options.invisibleToDebugger) .setTrace(TraceXPCGlobal); diff --git a/js/xpconnect/src/XPCShellImpl.cpp b/js/xpconnect/src/XPCShellImpl.cpp index 86e889be9a7f..7aad16b0cf5a 100644 --- a/js/xpconnect/src/XPCShellImpl.cpp +++ b/js/xpconnect/src/XPCShellImpl.cpp @@ -1522,7 +1522,7 @@ XRE_XPCShellMain(int argc, char** argv, char** envp, // Make the default XPCShell global use a fresh zone (rather than the // System Zone) to improve cross-zone test coverage. JS::CompartmentOptions options; - options.creationOptions().setZone(JS::FreshZone); + options.creationOptions().setNewZoneInSystemZoneGroup(); if (xpc::SharedMemoryEnabled()) options.creationOptions().setSharedMemoryAndAtomicsEnabled(true); options.behaviors().setVersion(JSVERSION_LATEST); diff --git a/netwerk/base/ProxyAutoConfig.cpp b/netwerk/base/ProxyAutoConfig.cpp index 4d7a6c1fd528..ef29379d24db 100644 --- a/netwerk/base/ProxyAutoConfig.cpp +++ b/netwerk/base/ProxyAutoConfig.cpp @@ -628,7 +628,7 @@ private: JSAutoRequest ar(mContext); JS::CompartmentOptions options; - options.creationOptions().setZone(JS::SystemZone); + options.creationOptions().setSystemZone(); options.behaviors().setVersion(JSVERSION_LATEST); mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr, JS::DontFireOnNewGlobalHook, options); diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp index 18114b70ead0..153b0ed21aa6 100644 --- a/xpcom/base/CycleCollectedJSContext.cpp +++ b/xpcom/base/CycleCollectedJSContext.cpp @@ -493,7 +493,7 @@ MozCrashWarningReporter(JSContext*, JSErrorReport*) } nsresult -CycleCollectedJSContext::Initialize(JSContext* aParentContext, +CycleCollectedJSContext::Initialize(JSRuntime* aParentRuntime, uint32_t aMaxBytes, uint32_t aMaxNurseryBytes) { @@ -504,7 +504,7 @@ CycleCollectedJSContext::Initialize(JSContext* aParentContext, mBaseRecursionDepth = RecursionDepth(); mozilla::dom::InitScriptSettings(); - mJSContext = JS_NewContext(aMaxBytes, aMaxNurseryBytes, aParentContext); + mJSContext = JS_NewContext(aMaxBytes, aMaxNurseryBytes, aParentRuntime); if (!mJSContext) { return NS_ERROR_OUT_OF_MEMORY; } diff --git a/xpcom/base/CycleCollectedJSContext.h b/xpcom/base/CycleCollectedJSContext.h index 29017d702999..fa03694a8b3c 100644 --- a/xpcom/base/CycleCollectedJSContext.h +++ b/xpcom/base/CycleCollectedJSContext.h @@ -147,7 +147,7 @@ protected: virtual ~CycleCollectedJSContext(); MOZ_IS_CLASS_INIT - nsresult Initialize(JSContext* aParentContext, + nsresult Initialize(JSRuntime* aParentRuntime, uint32_t aMaxBytes, uint32_t aMaxNurseryBytes);