1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
10 #include "mozilla/Maybe.h"
11 #include "mozilla/Variant.h"
13 #include "ds/OrderedHashTable.h"
14 #include "gc/Barrier.h"
15 #include "js/TracingAPI.h"
16 #include "js/TypeDecls.h"
17 #include "threading/ProtectedData.h"
27 static const size_t MARK_STACK_BASE_CAPACITY
= 4096;
29 enum class SlotsOrElementsKind
{
30 Unused
= 0, // Must match SlotsOrElementsRangeTag
38 enum IncrementalProgress
{ NotFinished
= 0, Finished
};
40 class AutoSetMarkColor
;
43 class UnmarkGrayTracer
;
45 struct EphemeronEdgeTableHashPolicy
{
47 static HashNumber
hash(const Lookup
& v
,
48 const mozilla::HashCodeScrambler
& hcs
) {
49 return hcs
.scramble(mozilla::HashGeneric(v
));
51 static bool match(Cell
* const& k
, const Lookup
& l
) { return k
== l
; }
52 static bool isEmpty(Cell
* const& v
) { return !v
; }
53 static void makeEmpty(Cell
** vp
) { *vp
= nullptr; }
56 // Ephemeron edges have two source nodes and one target, and mark the target
57 // with the minimum (least-marked) color of the sources. Currently, one of
58 // those sources will always be a WeakMapBase, so this will refer to its color
59 // at the time the edge is traced through. The other source's color will be
60 // given by the current mark color of the GCMarker.
61 struct EphemeronEdge
{
65 EphemeronEdge(CellColor color_
, Cell
* cell
) : color(color_
), target(cell
) {}
68 using EphemeronEdgeVector
= Vector
<EphemeronEdge
, 2, js::SystemAllocPolicy
>;
70 using EphemeronEdgeTable
=
71 OrderedHashMap
<Cell
*, EphemeronEdgeVector
, EphemeronEdgeTableHashPolicy
,
72 js::SystemAllocPolicy
>;
75 * The mark stack. Pointers in this stack are "gray" in the GC sense, but
76 * their references may be marked either black or gray (in the CC sense).
78 * When the mark stack is full, the GC does not call js::TraceChildren to mark
79 * the reachable "children" of the thing. Rather the thing is put aside and
80 * js::TraceChildren is called later when the mark stack is empty.
82 * To implement such delayed marking of the children with minimal overhead for
83 * the normal case of sufficient stack, we link arenas into a list using
84 * Arena::setNextDelayedMarkingArena(). The head of the list is stored in
85 * GCMarker::delayedMarkingList. GCMarker::delayMarkingChildren() adds arenas
86 * to the list as necessary while markAllDelayedChildren() pops the arenas from
87 * the stack until it is empty.
92 * We use a common mark stack to mark GC things of different types and use
93 * the explicit tags to distinguish them when it cannot be deduced from
94 * the context of push or pop operation.
97 SlotsOrElementsRangeTag
= 0, // Must match SlotsOrElementsKind::Unused.
103 LastTag
= TempRopeTag
106 static const uintptr_t TagMask
= 7;
107 static_assert(TagMask
>= uintptr_t(LastTag
),
108 "The tag mask must subsume the tags.");
109 static_assert(TagMask
<= gc::CellAlignMask
,
110 "The tag mask must be embeddable in a Cell*.");
118 TaggedPtr() = default;
119 TaggedPtr(Tag tag
, Cell
* ptr
);
121 uintptr_t tagUnchecked() const;
122 template <typename T
>
125 JSObject
* asRangeObject() const;
126 JSRope
* asTempRope() const;
128 void assertValid() const;
131 struct SlotsOrElementsRange
{
132 SlotsOrElementsRange(SlotsOrElementsKind kind
, JSObject
* obj
, size_t start
);
133 void assertValid() const;
135 SlotsOrElementsKind
kind() const;
136 size_t start() const;
137 TaggedPtr
ptr() const;
139 static constexpr size_t StartShift
= 2;
140 static constexpr size_t KindMask
= (1 << StartShift
) - 1;
143 uintptr_t startAndKind_
;
150 explicit MarkStack(const MarkStack
& other
);
151 MarkStack
& operator=(const MarkStack
& other
);
153 MarkStack(MarkStack
&& other
) noexcept
;
154 MarkStack
& operator=(MarkStack
&& other
) noexcept
;
156 // The unit for MarkStack::capacity() is mark stack words.
157 size_t capacity() { return stack().length(); }
159 size_t position() const { return topIndex_
; }
161 [[nodiscard
]] bool init();
162 [[nodiscard
]] bool resetStackCapacity();
165 void setMaxCapacity(size_t maxCapacity
);
168 template <typename T
>
169 [[nodiscard
]] bool push(T
* ptr
);
171 [[nodiscard
]] bool push(JSObject
* obj
, SlotsOrElementsKind kind
,
173 [[nodiscard
]] bool push(const TaggedPtr
& ptr
);
174 [[nodiscard
]] bool push(const SlotsOrElementsRange
& array
);
175 void infalliblePush(const TaggedPtr
& ptr
);
176 void infalliblePush(const SlotsOrElementsRange
& array
);
178 // GCMarker::eagerlyMarkChildren uses unused marking stack as temporary
179 // storage to hold rope pointers.
180 [[nodiscard
]] bool pushTempRope(JSRope
* rope
);
182 bool isEmpty() const { return position() == 0; }
183 bool hasEntries() const { return !isEmpty(); }
187 SlotsOrElementsRange
popSlotsOrElementsRange();
189 void clearAndResetCapacity();
190 void clearAndFreeStack();
194 [[nodiscard
]] bool ensureSpace(size_t count
);
196 static void moveWork(MarkStack
& dst
, MarkStack
& src
);
198 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const;
201 using StackVector
= Vector
<TaggedPtr
, 0, SystemAllocPolicy
>;
202 const StackVector
& stack() const { return stack_
.ref(); }
203 StackVector
& stack() { return stack_
.ref(); }
205 /* Grow the stack, ensuring there is space for at least count elements. */
206 [[nodiscard
]] bool enlarge(size_t count
);
208 [[nodiscard
]] bool resize(size_t newCapacity
);
212 const TaggedPtr
& peekPtr() const;
213 [[nodiscard
]] bool pushTaggedPtr(Tag tag
, Cell
* ptr
);
215 bool indexIsEntryBase(size_t index
) const;
217 // Vector containing allocated stack memory. Unused beyond topIndex_.
218 MainThreadOrGCTaskData
<StackVector
> stack_
;
220 // Index of the top of the stack.
221 MainThreadOrGCTaskData
<size_t> topIndex_
;
224 // The maximum stack capacity to grow to.
225 MainThreadOrGCTaskData
<size_t> maxCapacity_
{SIZE_MAX
};
229 static_assert(unsigned(SlotsOrElementsKind::Unused
) ==
230 unsigned(MarkStack::SlotsOrElementsRangeTag
),
231 "To split the mark stack we depend on being able to tell the "
232 "difference between SlotsOrElementsRange::startAndKind_ and a "
233 "tagged SlotsOrElementsRange");
235 // Bitmask of options to parameterize MarkingTracerT.
236 namespace MarkingOptions
{
240 // Set the compartment's hasMarkedCells flag for roots.
241 MarkRootCompartments
= 1,
243 // The marking tracer is operating in parallel. Use appropriate atomic
244 // accesses to update the mark bits correctly.
247 // Mark any implicit edges if we are in weak marking mode.
248 MarkImplicitEdges
= 4,
250 } // namespace MarkingOptions
252 // A default set of marking options that works during normal marking and weak
253 // marking modes. Used for barriers and testing code.
254 constexpr uint32_t NormalMarkingOptions
= MarkingOptions::MarkImplicitEdges
;
256 template <uint32_t markingOptions
>
258 : public GenericTracerImpl
<MarkingTracerT
<markingOptions
>> {
260 MarkingTracerT(JSRuntime
* runtime
, GCMarker
* marker
);
261 virtual ~MarkingTracerT() = default;
263 template <typename T
>
264 void onEdge(T
** thingp
, const char* name
);
265 friend class GenericTracerImpl
<MarkingTracerT
<markingOptions
>>;
267 GCMarker
* getMarker();
270 using MarkingTracer
= MarkingTracerT
<MarkingOptions::None
>;
271 using RootMarkingTracer
= MarkingTracerT
<MarkingOptions::MarkRootCompartments
>;
272 using WeakMarkingTracer
= MarkingTracerT
<MarkingOptions::MarkImplicitEdges
>;
273 using ParallelMarkingTracer
= MarkingTracerT
<MarkingOptions::ParallelMarking
>;
275 enum ShouldReportMarkTime
: bool {
276 ReportMarkTime
= true,
277 DontReportMarkTime
= false
283 enum MarkingState
: uint8_t {
284 // Have not yet started marking.
287 // Root marking mode. This sets the hasMarkedCells flag on compartments
288 // containing objects and scripts, which is used to make sure we clean up
289 // dead compartments.
292 // Main marking mode. Weakmap marking will be populating the
293 // gcEphemeronEdges tables but not consulting them. The state will
294 // transition to WeakMarking until it is done, then back to RegularMarking.
297 // Like RegularMarking but with multiple threads running in parallel.
300 // Same as RegularMarking except now every marked obj/script is immediately
301 // looked up in the gcEphemeronEdges table to find edges generated by
302 // weakmap keys, and traversing them to their values. Transitions back to
303 // RegularMarking when done.
308 explicit GCMarker(JSRuntime
* rt
);
309 [[nodiscard
]] bool init();
311 JSRuntime
* runtime() { return runtime_
; }
313 return tracer_
.match([](auto& t
) -> JSTracer
* { return &t
; });
317 void setMaxCapacity(size_t maxCap
) { stack
.setMaxCapacity(maxCap
); }
320 bool isActive() const { return state
!= NotActive
; }
321 bool isRegularMarking() const { return state
== RegularMarking
; }
322 bool isParallelMarking() const { return state
== ParallelMarking
; }
323 bool isWeakMarking() const { return state
== WeakMarking
; }
325 gc::MarkColor
markColor() const { return markColor_
; }
327 bool isDrained() const { return stack
.isEmpty() && otherStack
.isEmpty(); }
329 bool hasEntriesForCurrentColor() { return stack
.hasEntries(); }
330 bool hasBlackEntries() const { return hasEntries(gc::MarkColor::Black
); }
331 bool hasGrayEntries() const { return hasEntries(gc::MarkColor::Gray
); }
332 bool hasEntries(gc::MarkColor color
) const;
334 bool canDonateWork() const;
340 [[nodiscard
]] bool markUntilBudgetExhausted(
342 gc::ShouldReportMarkTime reportTime
= gc::ReportMarkTime
);
344 void setRootMarkingMode(bool newState
);
346 bool enterWeakMarkingMode();
347 void leaveWeakMarkingMode();
349 void enterParallelMarkingMode(gc::ParallelMarker
* pm
);
350 void leaveParallelMarkingMode();
352 // Do not use linear-time weak marking for the rest of this collection.
353 // Currently, this will only be triggered by an OOM when updating needed data
355 void abortLinearWeakMarking();
357 // 'delegate' is no longer the delegate of 'key'.
358 void severWeakDelegate(JSObject
* key
, JSObject
* delegate
);
360 // 'delegate' is now the delegate of 'key'. Update weakmap marking state.
361 void restoreWeakDelegate(JSObject
* key
, JSObject
* delegate
);
364 // We can't check atom marking if the helper thread lock is already held by
365 // the current thread. This allows us to disable the check.
366 void setCheckAtomMarking(bool check
);
368 bool shouldCheckCompartments() { return strictCompartmentChecking
; }
371 bool markCurrentColorInParallel(SliceBudget
& budget
);
373 template <uint32_t markingOptions
, gc::MarkColor
>
374 bool markOneColor(SliceBudget
& budget
);
376 static void moveWork(GCMarker
* dst
, GCMarker
* src
);
378 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const;
380 static GCMarker
* fromTracer(JSTracer
* trc
) {
381 MOZ_ASSERT(trc
->isMarkingTracer());
382 auto* marker
= reinterpret_cast<GCMarker
*>(uintptr_t(trc
) -
383 offsetof(GCMarker
, tracer_
));
384 MOZ_ASSERT(marker
->tracer() == trc
);
388 // Internal public methods, for ease of use by the rest of the GC:
390 // If |thing| is unmarked, mark it and then traverse its children.
391 template <uint32_t, typename T
>
392 void markAndTraverse(T
* thing
);
394 template <typename T
>
395 void markImplicitEdges(T
* oldThing
);
399 * Care must be taken changing the mark color from gray to black. The cycle
400 * collector depends on the invariant that there are no black to gray edges
401 * in the GC heap. This invariant lets the CC not trace through black
402 * objects. If this invariant is violated, the cycle collector may free
403 * objects that are still reachable.
405 void setMarkColor(gc::MarkColor newColor
);
406 friend class js::gc::AutoSetMarkColor
;
408 template <typename Tracer
>
409 void setMarkingStateAndTracer(MarkingState prev
, MarkingState next
);
411 template <uint32_t markingOptions
>
412 bool processMarkStackTop(SliceBudget
& budget
);
413 friend class gc::GCRuntime
;
415 // Helper methods that coerce their second argument to the base pointer
417 template <uint32_t markingOptions
, typename S
>
418 void markAndTraverseObjectEdge(S source
, JSObject
* target
) {
419 markAndTraverseEdge
<markingOptions
>(source
, target
);
421 template <uint32_t markingOptions
, typename S
>
422 void markAndTraverseStringEdge(S source
, JSString
* target
) {
423 markAndTraverseEdge
<markingOptions
>(source
, target
);
426 template <uint32_t markingOptions
, typename S
, typename T
>
427 void markAndTraverseEdge(S source
, T
* target
);
428 template <uint32_t markingOptions
, typename S
, typename T
>
429 void markAndTraverseEdge(S source
, const T
& target
);
431 template <typename S
, typename T
>
432 void checkTraversedEdge(S source
, T
* target
);
434 // Mark the given GC thing, but do not trace its children. Return true
435 // if the thing became marked.
436 template <uint32_t markingOptions
, typename T
>
437 [[nodiscard
]] bool mark(T
* thing
);
439 // Traverse a GC thing's children, using a strategy depending on the type.
440 // This can either processing them immediately or push them onto the mark
442 #define DEFINE_TRAVERSE_METHOD(_1, Type, _2, _3) \
443 template <uint32_t> \
444 void traverse(Type* thing);
445 JS_FOR_EACH_TRACEKIND(DEFINE_TRAVERSE_METHOD
)
446 #undef DEFINE_TRAVERSE_METHOD
448 // Process a marked thing's children by calling T::traceChildren().
449 template <uint32_t markingOptions
, typename T
>
450 void traceChildren(T
* thing
);
452 // Process a marked thing's children recursively using an iterative loop and
453 // manual dispatch, for kinds where this is possible.
454 template <uint32_t markingOptions
, typename T
>
455 void scanChildren(T
* thing
);
457 // Push a marked thing onto the mark stack. Its children will be marked later.
458 template <uint32_t markingOptions
, typename T
>
459 void pushThing(T
* thing
);
461 template <uint32_t markingOptions
>
462 void eagerlyMarkChildren(JSLinearString
* str
);
463 template <uint32_t markingOptions
>
464 void eagerlyMarkChildren(JSRope
* rope
);
465 template <uint32_t markingOptions
>
466 void eagerlyMarkChildren(JSString
* str
);
467 template <uint32_t markingOptions
>
468 void eagerlyMarkChildren(Shape
* shape
);
469 template <uint32_t markingOptions
>
470 void eagerlyMarkChildren(PropMap
* map
);
471 template <uint32_t markingOptions
>
472 void eagerlyMarkChildren(Scope
* scope
);
474 template <typename T
>
475 inline void pushTaggedPtr(T
* ptr
);
477 inline void pushValueRange(JSObject
* obj
, SlotsOrElementsKind kind
,
478 size_t start
, size_t end
);
480 // Push an object onto the stack for later tracing and assert that it has
481 // already been marked.
482 inline void repush(JSObject
* obj
);
484 template <typename T
>
485 void markImplicitEdgesHelper(T markedThing
);
487 // Mark through edges whose target color depends on the colors of two source
488 // entities (eg a WeakMap and one of its keys), and push the target onto the
490 void markEphemeronEdges(gc::EphemeronEdgeVector
& edges
,
491 gc::CellColor srcColor
);
492 friend class JS::Zone
;
495 void checkZone(void* p
);
497 void checkZone(void* p
) {}
500 template <uint32_t markingOptions
>
501 bool doMarking(SliceBudget
& budget
, gc::ShouldReportMarkTime reportTime
);
503 void delayMarkingChildrenOnOOM(gc::Cell
* cell
);
506 * The JSTracer used for marking. This can change depending on the current
509 mozilla::Variant
<gc::MarkingTracer
, gc::RootMarkingTracer
,
510 gc::WeakMarkingTracer
, gc::ParallelMarkingTracer
>
513 JSRuntime
* const runtime_
;
515 // The main mark stack, holding entries of color |markColor_|.
518 // The auxiliary mark stack, which may contain entries of the other color.
519 gc::MarkStack otherStack
;
521 // Track whether we're using the main or auxiliary stack.
522 MainThreadOrGCTaskData
<bool> haveSwappedStacks
;
524 // The current mark stack color.
525 MainThreadOrGCTaskData
<gc::MarkColor
> markColor_
;
527 MainThreadOrGCTaskData
<gc::ParallelMarker
*> parallelMarker_
;
529 Vector
<JS::GCCellPtr
, 0, SystemAllocPolicy
> unmarkGrayStack
;
530 friend class gc::UnmarkGrayTracer
;
532 /* Track the state of marking. */
533 MainThreadOrGCTaskData
<MarkingState
> state
;
535 /* Whether we successfully added all edges to the implicit edges table. */
536 MainThreadOrGCTaskData
<bool> haveAllImplicitEdges
;
540 * Whether weakmaps can be marked incrementally.
542 * JSGC_INCREMENTAL_WEAKMAP_ENABLED
543 * pref: javascript.options.mem.incremental_weakmap
545 MainThreadOrGCTaskData
<bool> incrementalWeakMapMarkingEnabled
;
549 /* Assert that start and stop are called with correct ordering. */
550 MainThreadOrGCTaskData
<bool> started
;
553 * Whether to check that atoms traversed are present in atom marking
556 MainThreadOrGCTaskData
<bool> checkAtomMarking
;
559 * If this is true, all marked objects must belong to a compartment being
560 * GCed. This is used to look for compartment bugs.
562 MainThreadOrGCTaskData
<bool> strictCompartmentChecking
;
566 * The compartment and zone of the object whose trace hook is currently being
567 * called, if any. Used to catch cross-compartment edges traced without use of
568 * TraceCrossCompartmentEdge.
570 MainThreadOrGCTaskData
<Compartment
*> tracingCompartment
;
571 MainThreadOrGCTaskData
<Zone
*> tracingZone
;
578 * Temporarily change the mark color while this class is on the stack.
580 * During incremental sweeping this also transitions zones in the
581 * current sweep group into the Mark or MarkGray state as appropriate.
583 class MOZ_RAII AutoSetMarkColor
{
585 MarkColor initialColor_
;
588 AutoSetMarkColor(GCMarker
& marker
, MarkColor newColor
)
589 : marker_(marker
), initialColor_(marker
.markColor()) {
590 marker_
.setMarkColor(newColor
);
593 AutoSetMarkColor(GCMarker
& marker
, CellColor newColor
)
594 : AutoSetMarkColor(marker
, newColor
.asMarkColor()) {}
596 ~AutoSetMarkColor() { marker_
.setMarkColor(initialColor_
); }
603 #endif /* gc_GCMarker_h */