blob: c245f1026d859241d41acb6de46bf191fa44876e [file] [log] [blame]
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_HEAP_CPPGC_MARKER_H_
#define V8_HEAP_CPPGC_MARKER_H_
#include <memory>
#include "include/cppgc/heap.h"
#include "include/cppgc/platform.h"
#include "include/cppgc/visitor.h"
#include "src/base/macros.h"
#include "src/base/platform/time.h"
#include "src/heap/base/incremental-marking-schedule.h"
#include "src/heap/base/worklist.h"
#include "src/heap/cppgc/concurrent-marker.h"
#include "src/heap/cppgc/globals.h"
#include "src/heap/cppgc/heap-config.h"
#include "src/heap/cppgc/marking-state.h"
#include "src/heap/cppgc/marking-visitor.h"
#include "src/heap/cppgc/marking-worklists.h"
#include "src/heap/cppgc/stats-collector.h"
#include "src/heap/cppgc/task-handle.h"
namespace cppgc {
namespace internal {
class HeapBase;
// Marking algorithm. Example for a valid call sequence creating the marking
// phase:
// 1. StartMarking()
// 2. AdvanceMarkingWithLimits() [Optional, depending on environment.]
// 3. EnterAtomicPause()
// 4. AdvanceMarkingWithLimits() [Optional]
// 5. EnterProcessGlobalAtomicPause()
// 6. AdvanceMarkingWithLimits()
// 7. LeaveAtomicPause()
//
// Alternatively, FinishMarking() combines steps 3.-7.
//
// The marker protects cross-thread roots from being created between 5.-7. This
// currently requires entering a process-global atomic pause.
class V8_EXPORT_PRIVATE MarkerBase {
public:
class IncrementalMarkingTask;
enum class WriteBarrierType {
kDijkstra,
kSteele,
};
// Pauses concurrent marking if running while this scope is active.
class PauseConcurrentMarkingScope final {
public:
explicit PauseConcurrentMarkingScope(MarkerBase&);
~PauseConcurrentMarkingScope();
private:
MarkerBase& marker_;
const bool resume_on_exit_;
};
virtual ~MarkerBase();
MarkerBase(const MarkerBase&) = delete;
MarkerBase& operator=(const MarkerBase&) = delete;
template <typename Class>
Class& To() {
return *static_cast<Class*>(this);
}
// Signals entering the atomic marking pause. The method
// - stops incremental/concurrent marking;
// - flushes back any in-construction worklists if needed;
// - Updates the MarkingConfig if the stack state has changed;
// - marks local roots
void EnterAtomicPause(StackState);
// Enters the process-global pause. The phase marks cross-thread roots and
// acquires a lock that prevents any cross-thread references from being
// created.
//
// The phase is ended with `LeaveAtomicPause()`.
void EnterProcessGlobalAtomicPause();
// Re-enable concurrent marking assuming it isn't enabled yet in GC cycle.
void ReEnableConcurrentMarking();
// Makes marking progress. The deadlines are only checked every few objects.
// - `max_duration`: A maximum marking duration.
// - `marked_bytes_limit`: Only process this many bytes. Ignored for
// processing concurrent bailout objects.
bool AdvanceMarkingWithLimits(v8::base::TimeDelta max_duration,
size_t marked_bytes_limit);
// Returns the size of the bytes marked in the last invocation of
// `AdvanceMarkingWithLimits()`.
size_t last_bytes_marked() const { return last_bytes_marked_; }
// Signals leaving the atomic marking pause. This method expects no more
// objects to be marked and merely updates marking states if needed.
void LeaveAtomicPause();
// Initialize marking according to the given config. This method will
// trigger incremental/concurrent marking if needed.
void StartMarking();
// Combines:
// - EnterAtomicPause()
// - EnterProcessGlobalAtomicPause()
// - AdvanceMarkingWithLimits()
// - ProcessWeakness()
// - LeaveAtomicPause()
void FinishMarking(StackState);
void ProcessCrossThreadWeaknessIfNeeded();
void ProcessWeakness();
bool JoinConcurrentMarkingIfNeeded();
void NotifyConcurrentMarkingOfWorkIfNeeded(cppgc::TaskPriority);
template <WriteBarrierType type>
inline void WriteBarrierForObject(HeapObjectHeader&);
HeapBase& heap() { return heap_; }
cppgc::Visitor& Visitor() { return visitor(); }
bool IsMarking() const { return is_marking_; }
void SetMainThreadMarkingDisabledForTesting(bool);
void WaitForConcurrentMarkingForTesting();
void ClearAllWorklistsForTesting();
bool IncrementalMarkingStepForTesting(StackState);
MarkingWorklists& MarkingWorklistsForTesting() { return marking_worklists_; }
MutatorMarkingState& MutatorMarkingStateForTesting() {
return mutator_marking_state_;
}
protected:
class IncrementalMarkingAllocationObserver final
: public StatsCollector::AllocationObserver {
public:
static constexpr size_t kMinAllocatedBytesPerStep = 256 * kKB;
explicit IncrementalMarkingAllocationObserver(MarkerBase& marker);
void AllocatedObjectSizeIncreased(size_t delta) final;
private:
MarkerBase& marker_;
size_t current_allocated_size_ = 0;
};
using IncrementalMarkingTaskHandle = SingleThreadedHandle;
static constexpr v8::base::TimeDelta kMaximumIncrementalStepDuration =
v8::base::TimeDelta::FromMilliseconds(2);
MarkerBase(HeapBase&, cppgc::Platform*, MarkingConfig);
virtual cppgc::Visitor& visitor() = 0;
virtual ConservativeTracingVisitor& conservative_visitor() = 0;
virtual heap::base::StackVisitor& stack_visitor() = 0;
virtual ConcurrentMarkerBase& concurrent_marker() = 0;
virtual heap::base::IncrementalMarkingSchedule& schedule() = 0;
// Processes the worklists with given deadlines. The deadlines are only
// checked every few objects.
// - `time_deadline`: Time deadline that is always respected.
// - `marked_bytes_deadline`: Only process this many bytes. Ignored for
// processing concurrent bailout objects.
bool ProcessWorklistsWithDeadline(v8::base::TimeTicks time_deadline,
size_t marked_bytes_deadline);
void AdvanceMarkingWithLimitsEpilogue();
void VisitLocalRoots(StackState);
void VisitCrossThreadRoots();
void MarkNotFullyConstructedObjects();
virtual void ScheduleIncrementalMarkingTask();
bool IncrementalMarkingStep(StackState);
void AdvanceMarkingOnAllocation();
virtual void AdvanceMarkingOnAllocationImpl();
void HandleNotFullyConstructedObjects();
void MarkStrongCrossThreadRoots();
HeapBase& heap_;
MarkingConfig config_ = MarkingConfig::Default();
cppgc::Platform* platform_;
std::shared_ptr<cppgc::TaskRunner> foreground_task_runner_;
IncrementalMarkingTaskHandle incremental_marking_handle_;
IncrementalMarkingAllocationObserver incremental_marking_allocation_observer_;
MarkingWorklists marking_worklists_;
MutatorMarkingState mutator_marking_state_;
size_t last_bytes_marked_ = 0;
bool is_marking_{false};
bool main_marking_disabled_for_testing_{false};
bool visited_cross_thread_persistents_in_atomic_pause_{false};
bool processed_cross_thread_weakness_{false};
};
class V8_EXPORT_PRIVATE Marker final : public MarkerBase {
public:
Marker(HeapBase&, cppgc::Platform*, MarkingConfig = MarkingConfig::Default());
protected:
cppgc::Visitor& visitor() final { return marking_visitor_; }
ConservativeTracingVisitor& conservative_visitor() final {
return conservative_marking_visitor_;
}
heap::base::StackVisitor& stack_visitor() final {
return conservative_marking_visitor_;
}
ConcurrentMarkerBase& concurrent_marker() final { return concurrent_marker_; }
heap::base::IncrementalMarkingSchedule& schedule() final {
return *schedule_.get();
}
private:
MutatorMarkingVisitor marking_visitor_;
ConservativeMarkingVisitor conservative_marking_visitor_;
std::unique_ptr<heap::base::IncrementalMarkingSchedule> schedule_;
ConcurrentMarker concurrent_marker_;
};
template <MarkerBase::WriteBarrierType type>
void MarkerBase::WriteBarrierForObject(HeapObjectHeader& header) {
// The barrier optimizes for the bailout cases:
// - kDijkstra: Marked objects.
// - kSteele: Unmarked objects.
switch (type) {
case MarkerBase::WriteBarrierType::kDijkstra:
if (!header.TryMarkAtomic()) {
return;
}
break;
case MarkerBase::WriteBarrierType::kSteele:
if (!header.IsMarked<AccessMode::kAtomic>()) {
return;
}
break;
}
// The barrier fired. Filter out in-construction objects here. This possibly
// requires unmarking the object again.
if (V8_UNLIKELY(header.IsInConstruction<AccessMode::kNonAtomic>())) {
// In construction objects are traced only if they are unmarked. If marking
// reaches this object again when it is fully constructed, it will re-mark
// it and tracing it as a previously not fully constructed object would know
// to bail out.
header.Unmark<AccessMode::kAtomic>();
mutator_marking_state_.not_fully_constructed_worklist()
.Push<AccessMode::kAtomic>(&header);
return;
}
switch (type) {
case MarkerBase::WriteBarrierType::kDijkstra:
mutator_marking_state_.write_barrier_worklist().Push(&header);
break;
case MarkerBase::WriteBarrierType::kSteele:
mutator_marking_state_.retrace_marked_objects_worklist().Push(&header);
break;
}
}
} // namespace internal
} // namespace cppgc
#endif // V8_HEAP_CPPGC_MARKER_H_