diff --git a/js/src/gc/Barrier.cpp b/js/src/gc/Barrier.cpp index 5e5f34b12a2a..dad97b85bd40 100644 --- a/js/src/gc/Barrier.cpp +++ b/js/src/gc/Barrier.cpp @@ -18,6 +18,8 @@ #include "vm/SymbolType.h" #include "wasm/WasmJS.h" +#include "gc/Zone-inl.h" + namespace js { bool diff --git a/js/src/gc/GC.cpp b/js/src/gc/GC.cpp index 03eed7f633c7..6dcdccfcccbd 100644 --- a/js/src/gc/GC.cpp +++ b/js/src/gc/GC.cpp @@ -251,6 +251,7 @@ #include "gc/Marking-inl.h" #include "gc/Nursery-inl.h" #include "gc/PrivateIterators-inl.h" +#include "gc/Zone-inl.h" #include "vm/GeckoProfiler-inl.h" #include "vm/JSObject-inl.h" #include "vm/JSScript-inl.h" diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 0bf06defc8a7..b5c18f5ccba4 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -37,6 +37,7 @@ #include "gc/GC-inl.h" #include "gc/Nursery-inl.h" #include "gc/PrivateIterators-inl.h" +#include "gc/Zone-inl.h" #include "vm/NativeObject-inl.h" #include "vm/Realm-inl.h" #include "vm/StringType-inl.h" diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index e1890b98f2f3..277f4721f262 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -32,6 +32,7 @@ #include "vm/TypeInference.h" #include "gc/Marking-inl.h" +#include "gc/Zone-inl.h" #include "vm/NativeObject-inl.h" using namespace js; diff --git a/js/src/gc/Zone-inl.h b/js/src/gc/Zone-inl.h new file mode 100644 index 000000000000..4e9d93bb75cd --- /dev/null +++ b/js/src/gc/Zone-inl.h @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef gc_Zone_inl_h +#define gc_Zone_inl_h + +#include "gc/Zone.h" + +#ifdef DEBUG +inline bool +JS::Zone::requireGCTracer() const +{ + JSRuntime* rt = runtimeFromAnyThread(); + return RuntimeHeapIsMajorCollecting() && !rt->gc.isHeapCompacting() && gcState_ != NoGC; +} +#endif + +inline void +JS::Zone::updateAllGCMallocCountersOnGCStart() +{ + gcMallocCounter.updateOnGCStart(); + jitCodeCounter.updateOnGCStart(); +} + +inline void +JS::Zone::updateAllGCMallocCountersOnGCEnd(const js::AutoLockGC& lock) +{ + auto& gc = runtimeFromAnyThread()->gc; + gcMallocCounter.updateOnGCEnd(gc.tunables, lock); + jitCodeCounter.updateOnGCEnd(gc.tunables, lock); +} + +inline js::gc::TriggerKind +JS::Zone::shouldTriggerGCForTooMuchMalloc() +{ + auto& gc = runtimeFromAnyThread()->gc; + return std::max(gcMallocCounter.shouldTriggerGC(gc.tunables), + jitCodeCounter.shouldTriggerGC(gc.tunables)); +} + + +/* static */ inline js::HashNumber +JS::Zone::UniqueIdToHash(uint64_t uid) +{ + return mozilla::HashGeneric(uid); +} + +inline bool +JS::Zone::getHashCode(js::gc::Cell* cell, js::HashNumber* hashp) +{ + uint64_t uid; + if (!getOrCreateUniqueId(cell, &uid)) + return false; + *hashp = UniqueIdToHash(uid); + return true; +} + +inline bool +JS::Zone::maybeGetUniqueId(js::gc::Cell* cell, uint64_t* uidp) +{ + MOZ_ASSERT(uidp); + MOZ_ASSERT(js::CurrentThreadCanAccessZone(this)); + + // Get an existing uid, if one has been set. + auto p = uniqueIds().lookup(cell); + if (p) + *uidp = p->value(); + + return p.found(); +} + +inline bool +JS::Zone::getOrCreateUniqueId(js::gc::Cell* cell, uint64_t* uidp) +{ + MOZ_ASSERT(uidp); + MOZ_ASSERT(js::CurrentThreadCanAccessZone(this) || js::CurrentThreadIsPerformingGC()); + + // Get an existing uid, if one has been set. + auto p = uniqueIds().lookupForAdd(cell); + if (p) { + *uidp = p->value(); + return true; + } + + MOZ_ASSERT(js::CurrentThreadCanAccessZone(this)); + + // Set a new uid on the cell. + *uidp = js::gc::NextCellUniqueId(runtimeFromAnyThread()); + if (!uniqueIds().add(p, cell, *uidp)) + return false; + + // If the cell was in the nursery, hopefully unlikely, then we need to + // tell the nursery about it so that it can sweep the uid if the thing + // does not get tenured. + if (IsInsideNursery(cell) && + !runtimeFromMainThread()->gc.nursery().addedUniqueIdToCell(cell)) + { + uniqueIds().remove(cell); + return false; + } + + return true; +} + +inline js::HashNumber +JS::Zone::getHashCodeInfallible(js::gc::Cell* cell) +{ + return UniqueIdToHash(getUniqueIdInfallible(cell)); +} + +inline uint64_t +JS::Zone::getUniqueIdInfallible(js::gc::Cell* cell) +{ + uint64_t uid; + js::AutoEnterOOMUnsafeRegion oomUnsafe; + if (!getOrCreateUniqueId(cell, &uid)) + oomUnsafe.crash("failed to allocate uid"); + return uid; +} + +inline bool +JS::Zone::hasUniqueId(js::gc::Cell* cell) +{ + MOZ_ASSERT(js::CurrentThreadCanAccessZone(this) || js::CurrentThreadIsPerformingGC()); + return uniqueIds().has(cell); +} + +inline void +JS::Zone::transferUniqueId(js::gc::Cell* tgt, js::gc::Cell* src) +{ + MOZ_ASSERT(src != tgt); + MOZ_ASSERT(!IsInsideNursery(tgt)); + MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtimeFromMainThread())); + MOZ_ASSERT(js::CurrentThreadCanAccessZone(this)); + MOZ_ASSERT(!uniqueIds().has(tgt)); + uniqueIds().rekeyIfMoved(src, tgt); +} + +inline void +JS::Zone::removeUniqueId(js::gc::Cell* cell) +{ + MOZ_ASSERT(js::CurrentThreadCanAccessZone(this)); + uniqueIds().remove(cell); +} + +inline void +JS::Zone::adoptUniqueIds(JS::Zone* source) +{ + js::AutoEnterOOMUnsafeRegion oomUnsafe; + for (js::gc::UniqueIdMap::Enum e(source->uniqueIds()); !e.empty(); e.popFront()) { + MOZ_ASSERT(!uniqueIds().has(e.front().key())); + if (!uniqueIds().put(e.front().key(), e.front().value())) + oomUnsafe.crash("failed to transfer unique ids from off-thread"); + } + source->uniqueIds().clear(); +} + +#endif // gc_Zone_inl_h diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index 725f73f0d09d..c79423e210d5 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -4,7 +4,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "gc/Zone.h" +#include "gc/Zone-inl.h" #include "gc/FreeOp.h" #include "gc/Policy.h" @@ -465,6 +465,41 @@ Zone::traceAtomCache(JSTracer* trc) } } +void* +Zone::onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes, void* reallocPtr) +{ + if (!js::CurrentThreadCanAccessRuntime(runtime_)) + return nullptr; + return runtimeFromMainThread()->onOutOfMemory(allocFunc, nbytes, reallocPtr); +} + +void +Zone::reportAllocationOverflow() +{ + js::ReportAllocationOverflow(nullptr); +} + +void +JS::Zone::maybeTriggerGCForTooMuchMalloc(js::gc::MemoryCounter& counter, TriggerKind trigger) +{ + JSRuntime* rt = runtimeFromAnyThread(); + + if (!js::CurrentThreadCanAccessRuntime(rt)) + return; + + bool wouldInterruptGC = rt->gc.isIncrementalGCInProgress() && !isCollecting(); + if (wouldInterruptGC && !counter.shouldResetIncrementalGC(rt->gc.tunables)) + return; + + if (!rt->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC, + counter.bytes(), counter.maxBytes())) + { + return; + } + + counter.recordTrigger(trigger); +} + ZoneList::ZoneList() : head(nullptr), tail(nullptr) {} diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 61f03330934b..1a1b5bec9228 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -11,7 +11,6 @@ #include "mozilla/HashFunctions.h" #include "gc/FindSCCs.h" -#include "gc/GCRuntime.h" #include "js/GCHashTable.h" #include "vm/MallocProvider.h" #include "vm/Runtime.h" @@ -218,12 +217,8 @@ class Zone : public JS::shadow::Zone, } MOZ_MUST_USE void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes, - void* reallocPtr = nullptr) { - if (!js::CurrentThreadCanAccessRuntime(runtime_)) - return nullptr; - return runtimeFromMainThread()->onOutOfMemory(allocFunc, nbytes, reallocPtr); - } - void reportAllocationOverflow() { js::ReportAllocationOverflow(nullptr); } + void* reallocPtr = nullptr); + void reportAllocationOverflow(); void beginSweepTypes(bool releaseTypes); @@ -259,13 +254,6 @@ class Zone : public JS::shadow::Zone, return needsIncrementalBarrier(); } - // If this returns true, all object tracing must be done with a GC marking - // tracer. - bool requireGCTracer() const { - JSRuntime* rt = runtimeFromAnyThread(); - return RuntimeHeapIsMajorCollecting() && !rt->gc.isHeapCompacting() && gcState_ != NoGC; - } - bool shouldMarkInZone() const { return needsIncrementalBarrier() || isGCMarking(); } @@ -274,12 +262,6 @@ class Zone : public JS::shadow::Zone, // possibly at other times too. uint64_t gcNumber(); - bool compileBarriers() const { return compileBarriers(needsIncrementalBarrier()); } - bool compileBarriers(bool needsIncrementalBarrier) const { - return needsIncrementalBarrier || - runtimeFromMainThread()->hasZealMode(js::gc::ZealMode::VerifierPre); - } - void setNeedsIncrementalBarrier(bool needs); const uint32_t* addressOfNeedsIncrementalBarrier() const { return &needsIncrementalBarrier_; } @@ -292,6 +274,10 @@ class Zone : public JS::shadow::Zone, void prepareForCompacting(); #ifdef DEBUG + // If this returns true, all object tracing must be done with a GC marking + // tracer. + bool requireGCTracer() const; + // For testing purposes, return the index of the sweep group which this zone // was swept in in the last GC. unsigned lastSweepGroupIndex() { return gcLastSweepGroupIndex; } @@ -443,22 +429,12 @@ class Zone : public JS::shadow::Zone, if (MOZ_LIKELY(trigger == js::gc::NoTrigger) || trigger <= counter.triggered()) return; - if (!js::CurrentThreadCanAccessRuntime(rt)) - return; - - bool wouldInterruptGC = rt->gc.isIncrementalGCInProgress() && !isCollecting(); - if (wouldInterruptGC && !counter.shouldResetIncrementalGC(rt->gc.tunables)) - return; - - if (!rt->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC, - counter.bytes(), counter.maxBytes())) - { - return; - } - - counter.recordTrigger(trigger); + maybeTriggerGCForTooMuchMalloc(counter, trigger); } + void maybeTriggerGCForTooMuchMalloc(js::gc::MemoryCounter& counter, + js::gc::TriggerKind trigger); + js::MainThreadData> regExps_; public: @@ -484,20 +460,9 @@ class Zone : public JS::shadow::Zone, updateMemoryCounter(jitCodeCounter, nbytes); } - void updateAllGCMallocCountersOnGCStart() { - gcMallocCounter.updateOnGCStart(); - jitCodeCounter.updateOnGCStart(); - } - void updateAllGCMallocCountersOnGCEnd(const js::AutoLockGC& lock) { - auto& gc = runtimeFromAnyThread()->gc; - gcMallocCounter.updateOnGCEnd(gc.tunables, lock); - jitCodeCounter.updateOnGCEnd(gc.tunables, lock); - } - js::gc::TriggerKind shouldTriggerGCForTooMuchMalloc() { - auto& gc = runtimeFromAnyThread()->gc; - return std::max(gcMallocCounter.shouldTriggerGC(gc.tunables), - jitCodeCounter.shouldTriggerGC(gc.tunables)); - } + void updateAllGCMallocCountersOnGCStart(); + void updateAllGCMallocCountersOnGCEnd(const js::AutoLockGC& lock); + js::gc::TriggerKind shouldTriggerGCForTooMuchMalloc(); void keepAtoms() { keepAtomsCount++; @@ -606,111 +571,34 @@ class Zone : public JS::shadow::Zone, js::MainThreadData gcLastSweepGroupIndex; #endif - static js::HashNumber UniqueIdToHash(uint64_t uid) { - return mozilla::HashGeneric(uid); - } + static js::HashNumber UniqueIdToHash(uint64_t uid); // Creates a HashNumber based on getUniqueId. Returns false on OOM. - MOZ_MUST_USE bool getHashCode(js::gc::Cell* cell, js::HashNumber* hashp) { - uint64_t uid; - if (!getOrCreateUniqueId(cell, &uid)) - return false; - *hashp = UniqueIdToHash(uid); - return true; - } + MOZ_MUST_USE bool getHashCode(js::gc::Cell* cell, js::HashNumber* hashp); // Gets an existing UID in |uidp| if one exists. - MOZ_MUST_USE bool maybeGetUniqueId(js::gc::Cell* cell, uint64_t* uidp) { - MOZ_ASSERT(uidp); - MOZ_ASSERT(js::CurrentThreadCanAccessZone(this)); - - // Get an existing uid, if one has been set. - auto p = uniqueIds().lookup(cell); - if (p) - *uidp = p->value(); - - return p.found(); - } + MOZ_MUST_USE bool maybeGetUniqueId(js::gc::Cell* cell, uint64_t* uidp); // Puts an existing UID in |uidp|, or creates a new UID for this Cell and // puts that into |uidp|. Returns false on OOM. - MOZ_MUST_USE bool getOrCreateUniqueId(js::gc::Cell* cell, uint64_t* uidp) { - MOZ_ASSERT(uidp); - MOZ_ASSERT(js::CurrentThreadCanAccessZone(this) || js::CurrentThreadIsPerformingGC()); + MOZ_MUST_USE bool getOrCreateUniqueId(js::gc::Cell* cell, uint64_t* uidp); - // Get an existing uid, if one has been set. - auto p = uniqueIds().lookupForAdd(cell); - if (p) { - *uidp = p->value(); - return true; - } - - MOZ_ASSERT(js::CurrentThreadCanAccessZone(this)); - - // Set a new uid on the cell. - *uidp = js::gc::NextCellUniqueId(runtimeFromAnyThread()); - if (!uniqueIds().add(p, cell, *uidp)) - return false; - - // If the cell was in the nursery, hopefully unlikely, then we need to - // tell the nursery about it so that it can sweep the uid if the thing - // does not get tenured. - if (IsInsideNursery(cell) && - !runtimeFromMainThread()->gc.nursery().addedUniqueIdToCell(cell)) - { - uniqueIds().remove(cell); - return false; - } - - return true; - } - - js::HashNumber getHashCodeInfallible(js::gc::Cell* cell) { - return UniqueIdToHash(getUniqueIdInfallible(cell)); - } - - uint64_t getUniqueIdInfallible(js::gc::Cell* cell) { - uint64_t uid; - js::AutoEnterOOMUnsafeRegion oomUnsafe; - if (!getOrCreateUniqueId(cell, &uid)) - oomUnsafe.crash("failed to allocate uid"); - return uid; - } + js::HashNumber getHashCodeInfallible(js::gc::Cell* cell); + uint64_t getUniqueIdInfallible(js::gc::Cell* cell); // Return true if this cell has a UID associated with it. - MOZ_MUST_USE bool hasUniqueId(js::gc::Cell* cell) { - MOZ_ASSERT(js::CurrentThreadCanAccessZone(this) || js::CurrentThreadIsPerformingGC()); - return uniqueIds().has(cell); - } + MOZ_MUST_USE bool hasUniqueId(js::gc::Cell* cell); // Transfer an id from another cell. This must only be called on behalf of a // moving GC. This method is infallible. - void transferUniqueId(js::gc::Cell* tgt, js::gc::Cell* src) { - MOZ_ASSERT(src != tgt); - MOZ_ASSERT(!IsInsideNursery(tgt)); - MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtimeFromMainThread())); - MOZ_ASSERT(js::CurrentThreadCanAccessZone(this)); - MOZ_ASSERT(!uniqueIds().has(tgt)); - uniqueIds().rekeyIfMoved(src, tgt); - } + void transferUniqueId(js::gc::Cell* tgt, js::gc::Cell* src); // Remove any unique id associated with this Cell. - void removeUniqueId(js::gc::Cell* cell) { - MOZ_ASSERT(js::CurrentThreadCanAccessZone(this)); - uniqueIds().remove(cell); - } + void removeUniqueId(js::gc::Cell* cell); // When finished parsing off-thread, transfer any UIDs we created in the // off-thread zone into the target zone. - void adoptUniqueIds(JS::Zone* source) { - js::AutoEnterOOMUnsafeRegion oomUnsafe; - for (js::gc::UniqueIdMap::Enum e(source->uniqueIds()); !e.empty(); e.popFront()) { - MOZ_ASSERT(!uniqueIds().has(e.front().key())); - if (!uniqueIds().put(e.front().key(), e.front().value())) - oomUnsafe.crash("failed to transfer unique ids from off-thread"); - } - source->uniqueIds().clear(); - } + void adoptUniqueIds(JS::Zone* source); #ifdef JSGC_HASH_TABLE_CHECKS // Assert that the UniqueId table has been redirected successfully. diff --git a/js/src/jsapi-tests/testGCUniqueId.cpp b/js/src/jsapi-tests/testGCUniqueId.cpp index 9e78a56a900d..945c4c7a6bc1 100644 --- a/js/src/jsapi-tests/testGCUniqueId.cpp +++ b/js/src/jsapi-tests/testGCUniqueId.cpp @@ -11,6 +11,8 @@ #include "jsapi-tests/tests.h" +#include "gc/Zone-inl.h" + static void MinimizeHeap(JSContext* cx) {