diff --git a/js/src/gc/GC.cpp b/js/src/gc/GC.cpp index 585ec96ed891..4e1ce13608b4 100644 --- a/js/src/gc/GC.cpp +++ b/js/src/gc/GC.cpp @@ -1646,6 +1646,49 @@ void GCRuntime::finish() { stats().printTotalProfileTimes(); } +void GCRuntime::freezePermanentAtoms() { + // This is called just after the permanent atoms have been created. At this + // point all existing atoms are permanent. Move the arenas containing atoms + // out of atoms zone arena lists until shutdown. Since we won't sweep them, we + // don't need to mark them at the start of every GC. + + MOZ_ASSERT(atomsZone); + MOZ_ASSERT(zones().empty()); + + atomsZone->arenas.clearFreeLists(); + freezePermanentAtomsOfKind(AllocKind::ATOM, permanentAtoms.ref()); + freezePermanentAtomsOfKind(AllocKind::FAT_INLINE_ATOM, + permanentFatInlineAtoms.ref()); +} + +void GCRuntime::freezePermanentAtomsOfKind(AllocKind kind, + ArenaList& arenaList) { + for (auto atom = atomsZone->cellIterUnsafe(kind); !atom.done(); + atom.next()) { + MOZ_ASSERT(atom->isPermanentAtom()); + atom->asTenured().markBlack(); + } + + arenaList = std::move(atomsZone->arenas.arenaList(kind)); +} + +void GCRuntime::restorePermanentAtoms() { + // Move the arenas containing permanent atoms that were removed by + // freezePermanentAtoms() back to the atoms zone arena lists so we can collect + // them. + + MOZ_ASSERT(heapState() == JS::HeapState::MajorCollecting); + + restorePermanentAtomsOfKind(AllocKind::ATOM, permanentAtoms.ref()); + restorePermanentAtomsOfKind(AllocKind::FAT_INLINE_ATOM, + permanentFatInlineAtoms.ref()); +} + +void GCRuntime::restorePermanentAtomsOfKind(AllocKind kind, + ArenaList& arenaList) { + atomsZone->arenas.arenaList(kind).insertListWithCursorAtEnd(arenaList); +} + bool GCRuntime::setParameter(JSGCParamKey key, uint32_t value) { MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); waitBackgroundSweepEnd(); @@ -4414,6 +4457,10 @@ bool GCRuntime::beginPreparePhase(JS::GCReason reason, AutoGCSession& session) { session.maybeCheckAtomsAccess.emplace(rt); } + if (reason == JS::GCReason::DESTROY_RUNTIME) { + restorePermanentAtoms(); + } + /* * Start a parallel task to clear all mark state for the zones we are * collecting. This is linear in the size of the heap we are collecting and so diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 1082197c6aed..527696cbc2ba 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -288,6 +288,11 @@ class GCRuntime { void finishRoots(); void finish(); + void freezePermanentAtoms(); + void freezePermanentAtomsOfKind(AllocKind kind, ArenaList& arenaList); + void restorePermanentAtoms(); + void restorePermanentAtomsOfKind(AllocKind kind, ArenaList& arenaList); + JS::HeapState heapState() const { return heapState_; } inline bool hasZealMode(ZealMode mode); @@ -934,6 +939,10 @@ class GCRuntime { AtomMarkingRuntime atomMarking; private: + // Arenas used for permanent atoms and static strings created at startup. + MainThreadData permanentAtoms; + MainThreadData permanentFatInlineAtoms; + // When chunks are empty, they reside in the emptyChunks pool and are // re-used as needed or eventually expired if not re-used. The emptyChunks // pool gets refilled from the background allocation task heuristically so diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 73dccceb6afb..04a36add2399 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -311,7 +311,7 @@ void js::gc::GCRuntime::traceRuntime(JSTracer* trc, AutoTraceSession& session) { void js::gc::GCRuntime::traceRuntimeAtoms(JSTracer* trc, const AutoAccessAtomsZone& access) { gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_RUNTIME_DATA); - rt->tracePermanentAtoms(trc); + rt->tracePermanentAtomsDuringInit(trc); TraceAtoms(trc, access); TraceWellKnownSymbols(trc); jit::JitRuntime::TraceAtomZoneRoots(trc, access); diff --git a/js/src/vm/JSAtom.cpp b/js/src/vm/JSAtom.cpp index 93076f8d19dd..3f99864f52e0 100644 --- a/js/src/vm/JSAtom.cpp +++ b/js/src/vm/JSAtom.cpp @@ -446,24 +446,15 @@ static void TracePermanentAtoms(JSTracer* trc, AtomSet::Range atoms) { } } -void JSRuntime::tracePermanentAtoms(JSTracer* trc) { +void JSRuntime::tracePermanentAtomsDuringInit(JSTracer* trc) { // Permanent atoms only need to be traced in the runtime which owns them. if (parentRuntime) { return; } - // Static strings are not included in the permanent atoms table. - if (staticStrings) { - staticStrings->trace(trc); - } - if (permanentAtomsDuringInit_) { TracePermanentAtoms(trc, permanentAtomsDuringInit_->all()); } - - if (permanentAtoms_) { - TracePermanentAtoms(trc, permanentAtoms_->all()); - } } void js::TraceWellKnownSymbols(JSTracer* trc) { @@ -636,6 +627,8 @@ bool JSRuntime::initMainAtomsTables(JSContext* cx) { MOZ_ASSERT(!parentRuntime); MOZ_ASSERT(!permanentAtomsPopulated()); + gc.freezePermanentAtoms(); + // The permanent atoms table has now been populated. permanentAtoms_ = js_new(permanentAtomsDuringInit_); // Takes ownership. diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index a8ceecefce73..b02c183bfb5d 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -860,7 +860,7 @@ struct JSRuntime { } bool initMainAtomsTables(JSContext* cx); - void tracePermanentAtoms(JSTracer* trc); + void tracePermanentAtomsDuringInit(JSTracer* trc); // Cached well-known symbols (ES6 rev 24 6.1.5.1). Like permanent atoms, // these are shared with the parentRuntime, if any.