/* 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_GCMarker_h
#define gc_GCMarker_h
#include "mozilla/Variant.h"
#include "mozilla/XorShift128PlusRNG.h"
#include "gc/Barrier.h"
#include "gc/Cell.h"
#include "gc/WeakMap.h"
#include "js/HashTable.h"
#include "js/TracingAPI.h"
#include "js/TypeDecls.h"
#include "threading/ProtectedData.h"
class JSRope;
namespace JS {
class SliceBudget;
}
namespace js {
class GCMarker;
class WeakMapBase;
#ifdef DEBUG
// Force stack resizing to ensure OOM test coverage in debug builds.
static const size_t MARK_STACK_BASE_CAPACITY =
4;
#else
static const size_t MARK_STACK_BASE_CAPACITY =
4096;
#endif
enum class SlotsOrElementsKind {
Unused =
0,
// Must match SlotsOrElementsRangeTag
Elements,
FixedSlots,
DynamicSlots
};
namespace gc {
enum IncrementalProgress { NotFinished =
0, Finished };
class AutoSetMarkColor;
class AutoUpdateMarkStackRanges;
class Cell;
class MarkStackIter;
class ParallelMarkTask;
template <uint32_t markingOptions>
class UnmarkGrayTracer;
// Ephemerons are edges from a source to a target that are only materialized
// into a table when the owner is marked. (The owner is something like a
// WeakMap, which contains a set of ephemerons each going from a WeakMap key to
// its value.) When marking a ephemeron, only the color of the owner is needed:
// the target is marked with the minimum (least-marked) color of the owner and
// source. So an EphemeronEdge need store only the owner color and the target
// pointer, which can fit into a tagged pointer since targets are aligned Cells.
//
// Note: if the owner's color changes, new EphemeronEdges will be created for
// it.
class EphemeronEdge {
static constexpr uintptr_t ColorMask =
0x3;
static_assert(uintptr_t(MarkColor::Gray) <= ColorMask);
static_assert(uintptr_t(MarkColor::Black) <= ColorMask);
static_assert(ColorMask < CellAlignBytes);
uintptr_t taggedTarget;
public:
EphemeronEdge(MarkColor color, TenuredCell* cell)
: taggedTarget(uintptr_t(cell) | uintptr_t(color)) {
MOZ_ASSERT((uintptr_t(cell) & ColorMask) ==
0);
}
MarkColor color()
const {
return MarkColor(taggedTarget & ColorMask); }
TenuredCell* target()
const {
return reinterpret_cast<TenuredCell*>(taggedTarget & ~ColorMask);
}
};
using EphemeronEdgeVector = Vector<EphemeronEdge,
2, js::SystemAllocPolicy>;
using EphemeronEdgeTable =
HashMap<TenuredCell*, EphemeronEdgeVector, PointerHasher<TenuredCell*>,
js::SystemAllocPolicy>;
/*
* The mark stack. Pointers in this stack are "gray" in the GC sense, but
* their references may be marked either black or gray (in the CC sense).
*
* When the mark stack is full, the GC does not call js::TraceChildren to mark
* the reachable "children" of the thing. Rather the thing is put aside and
* js::TraceChildren is called later when the mark stack is empty.
*
* To implement such delayed marking of the children with minimal overhead for
* the normal case of sufficient stack, we link arenas into a list using
* Arena::setNextDelayedMarkingArena(). The head of the list is stored in
* GCMarker::delayedMarkingList. GCMarker::delayMarkingChildren() adds arenas
* to the list as necessary while markAllDelayedChildren() pops the arenas from
* the stack until it is empty.
*/
class MarkStack {
public:
/*
* We use a common mark stack to mark GC things of different types and use
* the explicit tags to distinguish them when it cannot be deduced from
* the context of push or pop operation.
*/
enum Tag {
SlotsOrElementsRangeTag =
0,
// Must match SlotsOrElementsKind::Unused.
ObjectTag,
SymbolTag,
JitCodeTag,
ScriptTag,
TempRopeTag,
LastTag = TempRopeTag
};
static const uintptr_t TagMask =
7;
static_assert(TagMask >= uintptr_t(LastTag),
"The tag mask must subsume the tags.");
static_assert(TagMask <= gc::CellAlignMask,
"The tag mask must be embeddable in a Cell*.");
class TaggedPtr {
uintptr_t bits;
Cell* ptr()
const;
explicit TaggedPtr(uintptr_t bits);
public:
TaggedPtr(Tag tag, Cell* ptr);
static TaggedPtr fromBits(uintptr_t bits);
uintptr_t asBits()
const;
Tag tag()
const;
template <typename T>
T* as()
const;
JSObject* asRangeObject()
const;
JSRope* asTempRope()
const;
void assertValid()
const;
};
class SlotsOrElementsRange {
uintptr_t startAndKind_;
TaggedPtr ptr_;
static constexpr size_t StartShift =
2;
static constexpr size_t KindMask = (
1 << StartShift) -
1;
SlotsOrElementsRange(uintptr_t startAndKind, uintptr_t ptr);
public:
SlotsOrElementsRange(SlotsOrElementsKind kind, JSObject* obj, size_t start);
static SlotsOrElementsRange fromBits(uintptr_t startAndKind, uintptr_t ptr);
void assertValid()
const;
uintptr_t asBits0()
const;
uintptr_t asBits1()
const;
SlotsOrElementsKind kind()
const;
size_t start()
const;
TaggedPtr ptr()
const;
void setStart(size_t newStart);
void setEmpty();
};
MarkStack();
~MarkStack();
MarkStack(
const MarkStack& other) =
delete;
MarkStack&
operator=(
const MarkStack& other) =
delete;
void swap(MarkStack& other);
// The unit for capacity is mark stack words.
size_t capacity()
const {
return capacity_; }
#ifdef JS_GC_ZEAL
void setMaxCapacity(size_t maxCapacity);
#endif
size_t position()
const {
return topIndex_; }
[[nodiscard]]
bool init();
[[nodiscard]]
bool resetStackCapacity();
template <typename T>
[[nodiscard]]
bool push(T* ptr);
void infalliblePush(
const SlotsOrElementsRange& range);
void infalliblePush(JSObject* obj, SlotsOrElementsKind kind, size_t start);
[[nodiscard]]
bool push(
const TaggedPtr& ptr);
void infalliblePush(
const TaggedPtr& ptr);
// GCMarker::eagerlyMarkChildren uses unused marking stack as temporary
// storage to hold rope pointers.
[[nodiscard]]
bool pushTempRope(JSRope* rope);
bool isEmpty()
const {
return position() ==
0; }
bool hasEntries()
const {
return !isEmpty(); }
Tag peekTag()
const;
TaggedPtr popPtr();
SlotsOrElementsRange popSlotsOrElementsRange();
void clearAndResetCapacity();
void clearAndFreeStack();
void poisonUnused();
// Ensuring there is space to push |count| more words, growing the stack if
// necessary.
template <
bool checkMaxCapacity = true>
[[nodiscard]]
bool ensureSpace(size_t count);
static void moveAllWork(MarkStack& dst, MarkStack& src);
static size_t moveSomeWork(GCMarker* marker, MarkStack& dst, MarkStack& src,
bool allowDistribute);
size_t sizeOfExcludingThis()
const;
private:
uintptr_t at(size_t index)
const {
MOZ_ASSERT(topIndex_ <= capacity_);
MOZ_ASSERT(index < topIndex_);
return stack_[index];
}
uintptr_t* ptr(size_t index) {
MOZ_ASSERT(topIndex_ <= capacity_);
MOZ_ASSERT(index <= topIndex_);
return stack_ + index;
}
// Return a pointer to the first unused word beyond the top of the stack.
uintptr_t* end() {
return ptr(topIndex_); }
[[nodiscard]]
bool resize(size_t newCapacity);
TaggedPtr peekPtr()
const;
[[nodiscard]]
bool pushTaggedPtr(Tag tag, Cell* ptr);
bool indexIsEntryBase(size_t index)
const;
// Area of memory containing the stack.
MainThreadOrGCTaskData<uintptr_t*> stack_;
// Size of the stack in words.
MainThreadOrGCTaskData<size_t> capacity_;
// Index of the top of the stack.
MainThreadOrGCTaskData<size_t> topIndex_;
#ifdef JS_GC_ZEAL
// The maximum stack capacity to grow to.
MainThreadOrGCTaskData<size_t> maxCapacity_{SIZE_MAX};
#endif
#ifdef DEBUG
public:
MainThreadOrGCTaskData<
bool> elementsRangesAreValid;
#endif
friend class MarkStackIter;
};
static_assert(
unsigned(SlotsOrElementsKind::Unused) ==
unsigned(MarkStack::SlotsOrElementsRangeTag),
"To split the mark stack we depend on being able to tell the "
"difference between SlotsOrElementsRange::startAndKind_ and a "
"tagged SlotsOrElementsRange");
class MOZ_STACK_CLASS MarkStackIter {
MarkStack& stack_;
size_t pos_;
public:
explicit MarkStackIter(MarkStack& stack);
bool done()
const;
void next();
MarkStack::Tag peekTag()
const;
MarkStack::TaggedPtr peekPtr()
const;
bool isSlotsOrElementsRange()
const;
MarkStack::SlotsOrElementsRange slotsOrElementsRange()
const;
void setSlotsOrElementsRange(
const MarkStack::SlotsOrElementsRange& range);
private:
size_t position()
const;
};
// Bitmask of options to parameterize MarkingTracerT.
namespace MarkingOptions {
enum : uint32_t {
None =
0,
// Set the compartment's hasMarkedCells flag for roots.
MarkRootCompartments =
1,
// The marking tracer is using multiple threads. Use appropriate atomic
// accesses to update the mark bits correctly.
AtomicMarking =
2,
// The marking tracer is operating concurrently with the main thread. Use
// appropriate atomic accesses to update the mark bits correctly. Don't trace
// objects that are not safe to access off the main thread.
ConcurrentMarking =
4,
// Mark any implicit edges if we are in weak marking mode.
MarkImplicitEdges =
8,
};
}
// namespace MarkingOptions
// A default set of marking options that works during normal marking and weak
// marking modes. Used for barriers and testing code.
constexpr uint32_t NormalMarkingOptions = MarkingOptions::MarkImplicitEdges;
constexpr uint32_t ConcurrentMarkingOptions =
MarkingOptions::AtomicMarking | MarkingOptions::ConcurrentMarking;
enum ShouldReportMarkTime :
bool {
ReportMarkTime = true,
DontReportMarkTime =
false
};
template <uint32_t markingOptions>
class MarkingTracerT
: public GenericTracerImpl<MarkingTracerT<markingOptions>> {
public:
MarkingTracerT(JSRuntime* runtime, GCMarker* marker);
virtual ~MarkingTracerT() =
default;
template <typename T>
bool onEdge(T** thingp,
const char* name);
friend class GenericTracerImpl<MarkingTracerT<markingOptions>>;
GCMarker* gcMarker();
const GCMarker* gcMarker()
const;
// If |thing| is unmarked, mark it and then traverse its children.
template <typename T>
void markAndTraverse(T* thing);
bool doMarking(JS::SliceBudget& budget, gc::ShouldReportMarkTime reportTime);
bool processMarkStackTop(JS::SliceBudget& budget);
template <typename T>
void maybeMarkImplicitEdges(T* markedThing);
template <typename T>
void markImplicitEdges(T* markedThing);
void markEphemeronEdges(gc::EphemeronEdgeVector& edges,
gc::MarkColor srcColor);
static constexpr
bool hasOption(uint32_t option) {
return markingOptions & option;
}
private:
gc::MarkColor markColor()
const {
return gcMarker()->markColor(); }
Zone* tracingZone()
const {
return gcMarker()->tracingZone; }
template <gc::MarkColor color>
bool markOneColor(JS::SliceBudget& budget);
bool markCurrentColor(JS::SliceBudget& budget);
friend class GCRuntime;
bool callOrDelayTraceHook(JSObject* obj,
const JSClass* clasp,
JS::SliceBudget& budget);
// Helper methods that coerce their second argument to the base pointer
// type.
template <typename S>
void markAndTraverseObjectEdge(S source, JSObject* target) {
markAndTraverseEdge(source, target);
}
template <typename S>
void markAndTraverseStringEdge(S source, JSString* target) {
markAndTraverseEdge(source, target);
}
template <typename S, typename T>
void markAndTraverseEdge(S* source, T* target);
template <typename S, typename T>
void markAndTraverseEdge(S* source,
const T& target);
bool markAndTraversePrivateGCThing(JSObject* source, gc::Cell* target);
bool markAndTraverseSymbol(JSObject* source, JS::Symbol* target);
// Mark the given GC thing, but do not trace its children. Return true
// if the thing became marked.
template <typename T>
[[nodiscard]]
bool mark(T* thing);
// Traverse a GC thing's children, using a strategy depending on the type.
// This can either processing them immediately or push them onto the mark
// stack for later.
#define DEFINE_TRAVERSE_METHOD(_
1, Type, _
2, _
3)
void traverse(Type* thing);
JS_FOR_EACH_TRACEKIND(DEFINE_TRAVERSE_METHOD)
#undef DEFINE_TRAVERSE_METHOD
// Process a marked thing's children by calling T::traceChildren().
template <typename T>
void traceChildren(T* thing);
// Process a marked thing's children recursively using an iterative loop and
// manual dispatch, for kinds where this is possible.
template <typename T>
void scanChildren(T* thing);
// Push a marked thing onto the mark stack. Its children will be marked later.
template <typename T>
void pushThing(T* thing);
void eagerlyMarkChildren(JSString* str);
void eagerlyMarkChildren(JSLinearString* str);
void eagerlyMarkChildren(JSRope* rope);
void eagerlyMarkChildren(Shape* shape);
void eagerlyMarkChildren(BaseShape* shape);
void eagerlyMarkChildren(PropMap* map);
void eagerlyMarkChildren(Scope* scope);
};
using MarkingTracer = MarkingTracerT<MarkingOptions::None>;
using RootMarkingTracer = MarkingTracerT<MarkingOptions::MarkRootCompartments>;
using WeakMarkingTracer = MarkingTracerT<MarkingOptions::MarkImplicitEdges>;
using ParallelMarkingTracer = MarkingTracerT<MarkingOptions::AtomicMarking>;
using ConcurrentMarkingTracer = MarkingTracerT<ConcurrentMarkingOptions>;
}
/* namespace gc */
class GCMarker {
template <uint32_t>
friend class gc::MarkingTracerT;
enum MarkingState : uint8_t {
// Have not yet started marking.
NotActive,
// Root marking mode. This sets the hasMarkedCells flag on compartments
// containing objects and scripts, which is used to make sure we clean up
// dead compartments.
RootMarking,
// Main marking mode. Weakmap marking will be populating the
// gcEphemeronEdges tables but not consulting them. The state will
// transition to WeakMarking until it is done, then back to RegularMarking.
RegularMarking,
// Like RegularMarking but with multiple threads running in parallel.
ParallelMarking,
// Same as above, but there is a single thread running (possibly not the
// main marker thread).
ParallelMarkingSingleThread,
// Like RegularMarking but with a single thread running in the background.
ConcurrentMarking,
// Same as RegularMarking except now every marked obj/script is immediately
// looked up in the gcEphemeronEdges table to find edges generated by
// weakmap keys, and traversing them to their values. Transitions back to
// RegularMarking when done.
WeakMarking,
};
public:
explicit GCMarker(JSRuntime* rt);
[[nodiscard]]
bool init();
JSRuntime* runtime() {
return runtime_; }
JSTracer* tracer() {
return tracer_.match([](
auto& t) -> JSTracer* {
return &t; });
}
gc::MarkingTracer* getRegularTracer() {
MOZ_ASSERT(isRegularMarking());
return &tracer_.as<gc::MarkingTracer>();
}
gc::WeakMarkingTracer* getWeakMarkingTracer() {
MOZ_ASSERT(isWeakMarking());
return &tracer_.as<gc::WeakMarkingTracer>();
}
template <typename F>
decltype(
auto) matchTracer(F&& f) {
return tracer_.match(std::forward<F>(f));
}
template <typename F>
decltype(
auto) matchRegularOrParallelTracer(F&& f) {
if (isRegularMarking()) {
return f(tracer_.as<gc::MarkingTracer>());
}
MOZ_ASSERT(isParallelMarking());
return f(tracer_.as<gc::ParallelMarkingTracer>());
}
#ifdef JS_GC_ZEAL
void setMaxCapacity(size_t maxCap) { stack.setMaxCapacity(maxCap); }
#endif
bool isActive()
const {
return state != NotActive; }
bool isRegularMarking()
const {
return state == RegularMarking; }
bool isParallelMarking()
const {
return state == ParallelMarking || state == ParallelMarkingSingleThread;
}
bool isParallelMarkingMultipleThreads()
const {
return state == ParallelMarking;
}
bool isWeakMarking()
const {
return state == WeakMarking; }
bool isConcurrentMarking()
const {
return state == ConcurrentMarking; }
gc::MarkColor markColor()
const {
return markColor_; }
bool isDrained()
const;
bool isMarkStackEmpty()
const {
return stack.isEmpty() && otherStack.isEmpty();
}
bool hasEntriesForCurrentColor() {
return stack.hasEntries(); }
bool hasBlackEntries()
const {
return hasEntries(gc::MarkColor::Black); }
bool hasGrayEntries()
const {
return hasEntries(gc::MarkColor::Gray); }
bool hasEntries(gc::MarkColor color)
const;
bool canDonateWork()
const;
bool shouldDonateWork()
const;
void start();
void stop();
void reset();
[[nodiscard]]
bool markUntilBudgetExhausted(
JS::SliceBudget& budget,
gc::ShouldReportMarkTime reportTime = gc::ReportMarkTime);
void setRootMarkingMode(
bool newState);
bool enterWeakMarkingMode();
void leaveWeakMarkingMode();
void enterParallelMarkingMode();
void leaveParallelMarkingMode();
void enterConcurrentMarkingMode();
void leaveConcurrentMarkingMode();
// Only relevant when parallel marking: transition to a mode where it is known
// that a single thread is running.
void enterSingleThreadedMode();
void leaveSingleThreadedMode();
// Do not use linear-time weak marking for the rest of this collection.
// Currently, this will only be triggered by an OOM when updating needed data
// structures.
void abortLinearWeakMarking();
#ifdef DEBUG
// We can't check atom marking if the helper thread lock is already held by
// the current thread. This allows us to disable the check.
void setCheckAtomMarking(
bool check);
bool shouldCheckCompartments() {
return strictCompartmentChecking; }
void markOneObjectForTest(JSObject* obj);
bool isRootMarking()
const {
return state == RootMarking; }
#endif
bool markCurrentColorInParallel(gc::ParallelMarkTask* task,
JS::SliceBudget& budget);
// Trace the children of WeakMaps that were marked while draining the mark
// stack (on any thread).
void markDeferredWeakMapChildren(WeakMapList& deferred);
static void moveAllWork(GCMarker* dst, GCMarker* src);
static size_t moveSomeWork(GCMarker* dst, GCMarker* src,
bool allowDistribute);
[[nodiscard]]
bool initStack();
void resetStackCapacity();
void freeStack();
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
const;
static GCMarker* fromTracer(JSTracer* trc) {
MOZ_ASSERT(trc->isMarkingTracer());
auto* marker = reinterpret_cast<GCMarker*>(uintptr_t(trc) -
offsetof(GCMarker, tracer_));
MOZ_ASSERT(marker->tracer() == trc);
return marker;
}
// Internal public methods, for ease of use by the rest of the GC:
#ifdef JS_GC_CONCURRENT_MARKING
using MainThreadBuffer = js::Vector<JSObject*,
0, SystemAllocPolicy>;
bool processMainThreadBuffers(JS::SliceBudget& budget);
bool processMainThreadBuffer(MainThreadBuffer& buffer,
JS::SliceBudget& budget);
bool mainThreadBuffersAreEmpty()
const {
return blackMainThreadBuffer_.ref().empty() &&
grayMainThreadBuffer_.ref().empty();
}
bool addToMainThreadBuffer(JSObject* object, JS::SliceBudget& budget);
#endif // JS_GC_CONCURRENT_MARKING
private:
/*
* Care must be taken changing the mark color from gray to black. The cycle
* collector depends on the invariant that there are no black to gray edges
* in the GC heap. This invariant lets the CC not trace through black
* objects. If this invariant is violated, the cycle collector may free
* objects that are still reachable.
*/
void setMarkColor(gc::MarkColor newColor);
friend class js::gc::AutoSetMarkColor;
void swapMarkStacks();
template <typename Tracer>
void setMarkingStateAndTracer(MarkingState prev, MarkingState next);
// The mutator can shift object elements which could invalidate any elements
// index on the mark stack. Change the index to be relative to the elements
// allocation (to ignore shifted elements) while the mutator is running.
void updateRangesAtStartOfSlice();
void updateRangesAtEndOfSlice();
friend class gc::AutoUpdateMarkStackRanges;
friend class gc::GCRuntime;
template <typename S, typename T>
void checkTraversedEdge(S source, T* target);
template <typename T>
inline void pushTaggedPtr(T* ptr);
inline void pushValueRange(JSObject* obj, SlotsOrElementsKind kind,
size_t start, size_t end);
#ifdef DEBUG
void checkZone(gc::Cell* cell);
#else
void checkZone(gc::Cell* cell) {}
#endif
void delayMarkingChildrenOnOOM(gc::Cell* cell);
// Called by stop() and reset().
void deactivate();
/*
* The JSTracer used for marking. This can change depending on the current
* state.
*/
mozilla::Variant<gc::MarkingTracer, gc::RootMarkingTracer,
gc::WeakMarkingTracer, gc::ParallelMarkingTracer,
gc::ConcurrentMarkingTracer>
tracer_;
JSRuntime*
const runtime_;
// The main mark stack, holding entries of color |markColor_|.
gc::MarkStack stack;
// The auxiliary mark stack, which may contain entries of the other color.
gc::MarkStack otherStack;
// Track whether we're using the main or auxiliary stack.
MainThreadOrGCTaskData<
bool> haveSwappedStacks;
// The current mark stack color.
MainThreadOrGCTaskData<gc::MarkColor> markColor_;
#ifdef JS_GC_CONCURRENT_MARKING
// List of cells that have tracing work that cannot be performed concurrently
// with the main thread.
MainThreadOrGCTaskData<MainThreadBuffer> blackMainThreadBuffer_;
MainThreadOrGCTaskData<MainThreadBuffer> grayMainThreadBuffer_;
#endif
Vector<JS::GCCellPtr,
0, SystemAllocPolicy> unmarkGrayStack;
template <uint32_t markingOptions>
friend class gc::UnmarkGrayTracer;
/* Track the state of marking. */
MainThreadOrGCTaskData<MarkingState> state;
public:
/*
* Whether weakmaps can be marked incrementally.
*
* JSGC_INCREMENTAL_WEAKMAP_ENABLED
* pref: javascript.options.mem.incremental_weakmap
*/
MainThreadOrGCTaskData<
bool> incrementalWeakMapMarkingEnabled;
/* Random number generator state. */
MainThreadOrGCTaskData<mozilla::non_crypto::XorShift128PlusRNG> random;
/*
* The zone of the object whose trace hook is currently being
* called, if any. Set with AutoSetTracingSource.
*
* This is required so that MarkingTracerT::onEdge can keep the source zone's
* atom-marking bitmap entry for Symbol edges in sync. It's also used in debug
* builds to catch cross-compartment edges traced without
* TraceCrossCompartmentEdge.
*/
MainThreadOrGCTaskData<Zone*> tracingZone;
#ifdef DEBUG
private:
/* Assert that start and stop are called with correct ordering. */
MainThreadOrGCTaskData<
bool> started;
/*
* Whether to check that atoms traversed are present in atom marking
* bitmap.
*/
MainThreadOrGCTaskData<
bool> checkAtomMarking;
/*
* If this is true, all marked objects must belong to a compartment being
* GCed. This is used to look for compartment bugs.
*/
MainThreadOrGCTaskData<
bool> strictCompartmentChecking;
public:
/*
* The compartment of the object whose trace hook is currently being called,
* if any. Used to catch cross-compartment edges traced without use of
* TraceCrossCompartmentEdge.
*/
MainThreadOrGCTaskData<Compartment*> tracingCompartment;
#endif // DEBUG
};
inline bool IsConcurrentMarkingTracer(JSTracer* trc) {
return trc->isMarkingTracer() &&
GCMarker::fromTracer(trc)->isConcurrentMarking();
}
namespace gc {
enum class AllowGrayMarkingBeforeEndOfBlackMarking :
bool {
No =
false,
Yes = true
};
/*
* Temporarily change the mark color while this class is on the stack.
*
* For efficiency reasons we don't normally allow gray marking while there is
* black marking work to do, since we might end up marking the same things
* twice. If you really need this you can pass a parameter to allow it.
*/
class MOZ_RAII AutoSetMarkColor {
GCMarker& marker_;
MarkColor initialColor_;
public:
AutoSetMarkColor(GCMarker& marker, MarkColor newColor,
AllowGrayMarkingBeforeEndOfBlackMarking allowGrayMarking =
AllowGrayMarkingBeforeEndOfBlackMarking::No)
: marker_(marker), initialColor_(marker.markColor()) {
MOZ_ASSERT_IF(newColor == MarkColor::Gray && !
bool(allowGrayMarking),
!marker.hasBlackEntries());
marker.setMarkColor(newColor);
}
AutoSetMarkColor(GCMarker& marker, CellColor newColor)
: AutoSetMarkColor(marker, AsMarkColor(newColor)) {}
~AutoSetMarkColor() { marker_.setMarkColor(initialColor_); }
};
inline AutoMarkingLock::AutoMarkingLock(JSTracer* trc,
MarkingLock& markingLock) {
#ifdef JS_GC_CONCURRENT_MARKING
if (IsConcurrentMarkingTracer(trc)) {
lock = &markingLock;
runtime = trc->runtime();
lock->lock(runtime);
}
#endif
}
MOZ_ALWAYS_INLINE
void MemoryAcquireFence(JSTracer* trc) {
#ifdef JS_GC_CONCURRENT_MARKING
if (trc->isMarkingTracer() &&
GCMarker::fromTracer(trc)->isConcurrentMarking()) {
std::atomic_thread_fence(std::memory_order_acquire);
# ifdef MOZ_TSAN
TSANMemoryAcquireFence(trc->runtime());
# endif
}
#endif
}
template <uint32_t markingOptions>
MOZ_ALWAYS_INLINE
void MemoryAcquireFence(JSRuntime* runtime) {
#ifdef JS_GC_CONCURRENT_MARKING
if (
bool(markingOptions & MarkingOptions::ConcurrentMarking)) {
std::atomic_thread_fence(std::memory_order_acquire);
# ifdef MOZ_TSAN
TSANMemoryAcquireFence(runtime);
# endif
}
#endif
}
}
/* namespace gc */
}
/* namespace js */
#endif /* gc_GCMarker_h */