%PDF- %PDF-
| Direktori : /home2/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/heap/ |
| Current File : //home2/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/heap/concurrent-marking.cc |
// Copyright 2017 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.
#include "src/heap/concurrent-marking.h"
#include <algorithm>
#include <atomic>
#include <stack>
#include <unordered_map>
#include "include/v8config.h"
#include "src/base/logging.h"
#include "src/common/globals.h"
#include "src/execution/isolate.h"
#include "src/flags/flags.h"
#include "src/heap/ephemeron-remembered-set.h"
#include "src/heap/gc-tracer-inl.h"
#include "src/heap/gc-tracer.h"
#include "src/heap/heap-inl.h"
#include "src/heap/heap.h"
#include "src/heap/mark-compact-inl.h"
#include "src/heap/mark-compact.h"
#include "src/heap/marking-state-inl.h"
#include "src/heap/marking-visitor-inl.h"
#include "src/heap/marking-visitor.h"
#include "src/heap/marking.h"
#include "src/heap/memory-chunk.h"
#include "src/heap/memory-measurement-inl.h"
#include "src/heap/memory-measurement.h"
#include "src/heap/minor-mark-sweep-inl.h"
#include "src/heap/minor-mark-sweep.h"
#include "src/heap/object-lock.h"
#include "src/heap/objects-visiting-inl.h"
#include "src/heap/objects-visiting.h"
#include "src/heap/pretenuring-handler.h"
#include "src/heap/weak-object-worklists.h"
#include "src/heap/young-generation-marking-visitor.h"
#include "src/init/v8.h"
#include "src/objects/data-handler-inl.h"
#include "src/objects/embedder-data-array-inl.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/heap-object.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/slots-inl.h"
#include "src/objects/transitions-inl.h"
#include "src/objects/visitors.h"
#include "src/utils/utils-inl.h"
#include "src/utils/utils.h"
namespace v8 {
namespace internal {
class ConcurrentMarkingVisitor final
: public FullMarkingVisitorBase<ConcurrentMarkingVisitor> {
public:
ConcurrentMarkingVisitor(MarkingWorklists::Local* local_marking_worklists,
WeakObjects::Local* local_weak_objects, Heap* heap,
unsigned mark_compact_epoch,
base::EnumSet<CodeFlushMode> code_flush_mode,
bool embedder_tracing_enabled,
bool should_keep_ages_unchanged,
uint16_t code_flushing_increase,
MemoryChunkDataMap* memory_chunk_data)
: FullMarkingVisitorBase(
local_marking_worklists, local_weak_objects, heap,
mark_compact_epoch, code_flush_mode, embedder_tracing_enabled,
should_keep_ages_unchanged, code_flushing_increase),
memory_chunk_data_(memory_chunk_data) {}
using FullMarkingVisitorBase<
ConcurrentMarkingVisitor>::VisitMapPointerIfNeeded;
static constexpr bool EnableConcurrentVisitation() { return true; }
// Implements ephemeron semantics: Marks value if key is already reachable.
// Returns true if value was actually marked.
bool ProcessEphemeron(Tagged<HeapObject> key, Tagged<HeapObject> value) {
if (IsMarked(key)) {
if (TryMark(value)) {
local_marking_worklists_->Push(value);
return true;
}
} else if (IsUnmarked(value)) {
local_weak_objects_->next_ephemerons_local.Push(Ephemeron{key, value});
}
return false;
}
template <typename TSlot>
void RecordSlot(Tagged<HeapObject> object, TSlot slot,
Tagged<HeapObject> target) {
MarkCompactCollector::RecordSlot(object, slot, target);
}
void IncrementLiveBytesCached(MemoryChunk* chunk, intptr_t by) {
DCHECK_IMPLIES(V8_COMPRESS_POINTERS_8GB_BOOL,
IsAligned(by, kObjectAlignment8GbHeap));
(*memory_chunk_data_)[chunk].live_bytes += by;
}
private:
void RecordRelocSlot(Tagged<InstructionStream> host, RelocInfo* rinfo,
Tagged<HeapObject> target) {
if (!MarkCompactCollector::ShouldRecordRelocSlot(host, rinfo, target)) {
return;
}
MarkCompactCollector::RecordRelocSlotInfo info =
MarkCompactCollector::ProcessRelocInfo(host, rinfo, target);
MemoryChunkData& data = (*memory_chunk_data_)[info.memory_chunk];
if (!data.typed_slots) {
data.typed_slots.reset(new TypedSlots());
}
data.typed_slots->Insert(info.slot_type, info.offset);
}
TraceRetainingPathMode retaining_path_mode() {
return TraceRetainingPathMode::kDisabled;
}
MemoryChunkDataMap* memory_chunk_data_;
friend class MarkingVisitorBase<ConcurrentMarkingVisitor>;
};
struct ConcurrentMarking::TaskState {
size_t marked_bytes = 0;
MemoryChunkDataMap memory_chunk_data;
NativeContextStats native_context_stats;
PretenuringHandler::PretenuringFeedbackMap local_pretenuring_feedback{
PretenuringHandler::kInitialFeedbackCapacity};
};
class ConcurrentMarking::JobTaskMajor : public v8::JobTask {
public:
JobTaskMajor(ConcurrentMarking* concurrent_marking,
unsigned mark_compact_epoch,
base::EnumSet<CodeFlushMode> code_flush_mode,
bool should_keep_ages_unchanged)
: concurrent_marking_(concurrent_marking),
mark_compact_epoch_(mark_compact_epoch),
code_flush_mode_(code_flush_mode),
should_keep_ages_unchanged_(should_keep_ages_unchanged),
trace_id_(reinterpret_cast<uint64_t>(concurrent_marking) ^
concurrent_marking->heap_->tracer()->CurrentEpoch(
GCTracer::Scope::MC_BACKGROUND_MARKING)) {}
~JobTaskMajor() override = default;
JobTaskMajor(const JobTaskMajor&) = delete;
JobTaskMajor& operator=(const JobTaskMajor&) = delete;
// v8::JobTask overrides.
void Run(JobDelegate* delegate) override {
// In case multi-cage pointer compression mode is enabled ensure that
// current thread's cage base values are properly initialized.
PtrComprCageAccessScope ptr_compr_cage_access_scope(
concurrent_marking_->heap_->isolate());
if (delegate->IsJoiningThread()) {
// TRACE_GC is not needed here because the caller opens the right scope.
concurrent_marking_->RunMajor(delegate, code_flush_mode_,
mark_compact_epoch_,
should_keep_ages_unchanged_);
} else {
TRACE_GC_EPOCH_WITH_FLOW(concurrent_marking_->heap_->tracer(),
GCTracer::Scope::MC_BACKGROUND_MARKING,
ThreadKind::kBackground, trace_id_,
TRACE_EVENT_FLAG_FLOW_IN);
concurrent_marking_->RunMajor(delegate, code_flush_mode_,
mark_compact_epoch_,
should_keep_ages_unchanged_);
}
}
size_t GetMaxConcurrency(size_t worker_count) const override {
return concurrent_marking_->GetMajorMaxConcurrency(worker_count);
}
uint64_t trace_id() const { return trace_id_; }
private:
ConcurrentMarking* concurrent_marking_;
const unsigned mark_compact_epoch_;
base::EnumSet<CodeFlushMode> code_flush_mode_;
const bool should_keep_ages_unchanged_;
const uint64_t trace_id_;
};
class ConcurrentMarking::JobTaskMinor : public v8::JobTask {
public:
explicit JobTaskMinor(ConcurrentMarking* concurrent_marking)
: concurrent_marking_(concurrent_marking),
trace_id_(reinterpret_cast<uint64_t>(concurrent_marking) ^
concurrent_marking->heap_->tracer()->CurrentEpoch(
GCTracer::Scope::MINOR_MS_MARK_PARALLEL)) {}
~JobTaskMinor() override = default;
JobTaskMinor(const JobTaskMinor&) = delete;
JobTaskMinor& operator=(const JobTaskMinor&) = delete;
// v8::JobTask overrides.
void Run(JobDelegate* delegate) override {
// In case multi-cage pointer compression mode is enabled ensure that
// current thread's cage base values are properly initialized.
PtrComprCageAccessScope ptr_compr_cage_access_scope(
concurrent_marking_->heap_->isolate());
if (delegate->IsJoiningThread()) {
TRACE_GC_WITH_FLOW(concurrent_marking_->heap_->tracer(),
GCTracer::Scope::MINOR_MS_MARK_PARALLEL, trace_id_,
TRACE_EVENT_FLAG_FLOW_IN);
// TRACE_GC is not needed here because the caller opens the right scope.
concurrent_marking_->RunMinor(delegate);
} else {
TRACE_GC_EPOCH_WITH_FLOW(concurrent_marking_->heap_->tracer(),
GCTracer::Scope::MINOR_MS_BACKGROUND_MARKING,
ThreadKind::kBackground, trace_id_,
TRACE_EVENT_FLAG_FLOW_IN);
concurrent_marking_->RunMinor(delegate);
}
}
size_t GetMaxConcurrency(size_t worker_count) const override {
return concurrent_marking_->GetMinorMaxConcurrency(worker_count);
}
uint64_t trace_id() const { return trace_id_; }
private:
ConcurrentMarking* concurrent_marking_;
const uint64_t trace_id_;
};
ConcurrentMarking::ConcurrentMarking(Heap* heap, WeakObjects* weak_objects)
: heap_(heap), weak_objects_(weak_objects) {
#ifndef V8_ATOMIC_OBJECT_FIELD_WRITES
// Concurrent marking requires atomic object field writes.
CHECK(!v8_flags.concurrent_marking);
#endif
int max_tasks;
if (v8_flags.concurrent_marking_max_worker_num == 0) {
max_tasks = V8::GetCurrentPlatform()->NumberOfWorkerThreads();
} else {
max_tasks = v8_flags.concurrent_marking_max_worker_num;
}
task_state_.reserve(max_tasks + 1);
for (int i = 0; i <= max_tasks; ++i) {
task_state_.emplace_back(std::make_unique<TaskState>());
}
}
ConcurrentMarking::~ConcurrentMarking() = default;
void ConcurrentMarking::RunMajor(JobDelegate* delegate,
base::EnumSet<CodeFlushMode> code_flush_mode,
unsigned mark_compact_epoch,
bool should_keep_ages_unchanged) {
size_t kBytesUntilInterruptCheck = 64 * KB;
int kObjectsUntilInterruptCheck = 1000;
uint8_t task_id = delegate->GetTaskId() + 1;
TaskState* task_state = task_state_[task_id].get();
auto* cpp_heap = CppHeap::From(heap_->cpp_heap());
MarkingWorklists::Local local_marking_worklists(
marking_worklists_, cpp_heap
? cpp_heap->CreateCppMarkingState()
: MarkingWorklists::Local::kNoCppMarkingState);
WeakObjects::Local local_weak_objects(weak_objects_);
ConcurrentMarkingVisitor visitor(
&local_marking_worklists, &local_weak_objects, heap_, mark_compact_epoch,
code_flush_mode, heap_->cpp_heap(), should_keep_ages_unchanged,
heap_->tracer()->CodeFlushingIncrease(), &task_state->memory_chunk_data);
NativeContextInferrer native_context_inferrer;
NativeContextStats& native_context_stats = task_state->native_context_stats;
double time_ms;
size_t marked_bytes = 0;
Isolate* isolate = heap_->isolate();
if (v8_flags.trace_concurrent_marking) {
isolate->PrintWithTimestamp("Starting major concurrent marking task %d\n",
task_id);
}
bool another_ephemeron_iteration = false;
MainAllocator* const new_space_allocator =
heap_->new_space() ? heap_->new_space()->main_allocator() : nullptr;
{
TimedScope scope(&time_ms);
{
Ephemeron ephemeron;
while (local_weak_objects.current_ephemerons_local.Pop(&ephemeron)) {
if (visitor.ProcessEphemeron(ephemeron.key, ephemeron.value)) {
another_ephemeron_iteration = true;
}
}
}
PtrComprCageBase cage_base(isolate);
bool is_per_context_mode = local_marking_worklists.IsPerContextMode();
bool done = false;
CodePageHeaderModificationScope rwx_write_scope(
"Marking a InstructionStream object requires write access to the "
"Code page header");
while (!done) {
size_t current_marked_bytes = 0;
int objects_processed = 0;
while (current_marked_bytes < kBytesUntilInterruptCheck &&
objects_processed < kObjectsUntilInterruptCheck) {
Tagged<HeapObject> object;
if (!local_marking_worklists.Pop(&object)) {
done = true;
break;
}
DCHECK(!object.InReadOnlySpace());
DCHECK_EQ(GetIsolateFromWritableObject(object), isolate);
objects_processed++;
Address new_space_top = kNullAddress;
Address new_space_limit = kNullAddress;
Address new_large_object = kNullAddress;
if (new_space_allocator) {
// The order of the two loads is important.
new_space_top = new_space_allocator->original_top_acquire();
new_space_limit = new_space_allocator->original_limit_relaxed();
}
if (heap_->new_lo_space()) {
new_large_object = heap_->new_lo_space()->pending_object();
}
Address addr = object.address();
if ((new_space_top <= addr && addr < new_space_limit) ||
addr == new_large_object) {
local_marking_worklists.PushOnHold(object);
} else {
Tagged<Map> map = object->map(cage_base, kAcquireLoad);
// The marking worklist should never contain filler objects.
CHECK(!IsFreeSpaceOrFillerMap(map));
if (is_per_context_mode) {
Address context;
if (native_context_inferrer.Infer(cage_base, map, object,
&context)) {
local_marking_worklists.SwitchToContext(context);
}
}
const auto visited_size = visitor.Visit(map, object);
visitor.IncrementLiveBytesCached(
MemoryChunk::cast(BasicMemoryChunk::FromHeapObject(object)),
ALIGN_TO_ALLOCATION_ALIGNMENT(visited_size));
if (is_per_context_mode) {
native_context_stats.IncrementSize(
local_marking_worklists.Context(), map, object, visited_size);
}
current_marked_bytes += visited_size;
}
}
if (objects_processed > 0) another_ephemeron_iteration = true;
marked_bytes += current_marked_bytes;
base::AsAtomicWord::Relaxed_Store<size_t>(&task_state->marked_bytes,
marked_bytes);
if (delegate->ShouldYield()) {
TRACE_GC_NOTE("ConcurrentMarking::RunMajor Preempted");
break;
}
}
if (done) {
Ephemeron ephemeron;
while (local_weak_objects.discovered_ephemerons_local.Pop(&ephemeron)) {
if (visitor.ProcessEphemeron(ephemeron.key, ephemeron.value)) {
another_ephemeron_iteration = true;
}
}
}
local_marking_worklists.Publish();
local_weak_objects.Publish();
base::AsAtomicWord::Relaxed_Store<size_t>(&task_state->marked_bytes, 0);
total_marked_bytes_ += marked_bytes;
if (another_ephemeron_iteration) {
set_another_ephemeron_iteration(true);
}
}
if (v8_flags.trace_concurrent_marking) {
heap_->isolate()->PrintWithTimestamp(
"Major task %d concurrently marked %dKB in %.2fms\n", task_id,
static_cast<int>(marked_bytes / KB), time_ms);
}
DCHECK(task_state->local_pretenuring_feedback.empty());
}
class ConcurrentMarking::MinorMarkingState {
public:
~MinorMarkingState() { DCHECK_EQ(0, active_markers_); }
V8_INLINE void MarkerStarted() {
active_markers_.fetch_add(1, std::memory_order_relaxed);
}
// Returns true if all markers are done.
V8_INLINE bool MarkerDone() {
return active_markers_.fetch_sub(1, std::memory_order_relaxed) == 1;
}
private:
std::atomic<int> active_markers_{0};
};
namespace {
V8_INLINE bool IsYoungObjectInLab(MainAllocator* new_space_allocator,
NewLargeObjectSpace* new_lo_space,
Tagged<HeapObject> heap_object) {
// The order of the two loads is important.
Address new_space_top = new_space_allocator->original_top_acquire();
Address new_space_limit = new_space_allocator->original_limit_relaxed();
Address new_large_object = new_lo_space->pending_object();
Address addr = heap_object.address();
return (new_space_top <= addr && addr < new_space_limit) ||
addr == new_large_object;
}
} // namespace
template <YoungGenerationMarkingVisitationMode marking_mode>
V8_INLINE size_t ConcurrentMarking::RunMinorImpl(JobDelegate* delegate,
TaskState* task_state) {
static constexpr size_t kBytesUntilInterruptCheck = 64 * KB;
static constexpr int kObjectsUntilInterruptCheck = 1000;
size_t marked_bytes = 0;
size_t current_marked_bytes = 0;
int objects_processed = 0;
YoungGenerationMarkingVisitor<marking_mode> visitor(
heap_, &task_state->local_pretenuring_feedback);
YoungGenerationRememberedSetsMarkingWorklist::Local remembered_sets(
heap_->minor_mark_sweep_collector()->remembered_sets_marking_handler());
auto& marking_worklists_local = visitor.marking_worklists_local();
Isolate* isolate = heap_->isolate();
minor_marking_state_->MarkerStarted();
MainAllocator* const new_space_allocator =
heap_->new_space()->main_allocator();
NewLargeObjectSpace* const new_lo_space = heap_->new_lo_space();
do {
if (delegate->IsJoiningThread()) {
marking_worklists_local.MergeOnHold();
}
Tagged<HeapObject> heap_object;
TRACE_GC_EPOCH(heap_->tracer(),
GCTracer::Scope::MINOR_MS_BACKGROUND_MARKING_CLOSURE,
ThreadKind::kBackground);
while (marking_worklists_local.Pop(&heap_object)) {
if (IsYoungObjectInLab(new_space_allocator, new_lo_space, heap_object)) {
visitor.marking_worklists_local().PushOnHold(heap_object);
} else {
Tagged<Map> map = heap_object->map(isolate);
const auto visited_size = visitor.Visit(map, heap_object);
if (visited_size) {
current_marked_bytes += visited_size;
visitor.IncrementLiveBytesCached(
MemoryChunk::FromHeapObject(heap_object),
ALIGN_TO_ALLOCATION_ALIGNMENT(visited_size));
}
}
if (current_marked_bytes >= kBytesUntilInterruptCheck ||
++objects_processed >= kObjectsUntilInterruptCheck) {
marked_bytes += current_marked_bytes;
if (delegate->ShouldYield()) {
TRACE_GC_NOTE("ConcurrentMarking::RunMinor Preempted");
minor_marking_state_->MarkerDone();
return marked_bytes;
}
objects_processed = 0;
current_marked_bytes = 0;
}
}
} while (remembered_sets.ProcessNextItem(&visitor));
if (minor_marking_state_->MarkerDone()) {
// This is the last active marker and it ran out of work. Request GC
// finalization.
heap_->minor_mark_sweep_collector()->RequestGC();
}
return marked_bytes + current_marked_bytes;
}
void ConcurrentMarking::RunMinor(JobDelegate* delegate) {
DCHECK_NOT_NULL(heap_->new_space());
DCHECK_NOT_NULL(heap_->new_lo_space());
uint8_t task_id = delegate->GetTaskId() + 1;
DCHECK_LT(task_id, task_state_.size());
TaskState* task_state = task_state_[task_id].get();
double time_ms;
size_t marked_bytes = 0;
Isolate* isolate = heap_->isolate();
if (v8_flags.trace_concurrent_marking) {
isolate->PrintWithTimestamp("Starting minor concurrent marking task %d\n",
task_id);
}
{
TimedScope scope(&time_ms);
if (heap_->minor_mark_sweep_collector()->is_in_atomic_pause()) {
marked_bytes =
RunMinorImpl<YoungGenerationMarkingVisitationMode::kParallel>(
delegate, task_state);
} else {
marked_bytes =
RunMinorImpl<YoungGenerationMarkingVisitationMode::kConcurrent>(
delegate, task_state);
}
}
if (v8_flags.trace_concurrent_marking) {
heap_->isolate()->PrintWithTimestamp(
"Minor task %d concurrently marked %dKB in %.2fms\n", task_id,
static_cast<int>(marked_bytes / KB), time_ms);
}
DCHECK(task_state->memory_chunk_data.empty());
DCHECK(task_state->native_context_stats.Empty());
DCHECK_EQ(0, task_state->marked_bytes);
}
size_t ConcurrentMarking::GetMajorMaxConcurrency(size_t worker_count) {
size_t marking_items = marking_worklists_->shared()->Size();
marking_items += marking_worklists_->other()->Size();
for (auto& worklist : marking_worklists_->context_worklists())
marking_items += worklist.worklist->Size();
return std::min<size_t>(
task_state_.size() - 1,
worker_count +
std::max<size_t>({marking_items,
weak_objects_->discovered_ephemerons.Size(),
weak_objects_->current_ephemerons.Size()}));
}
size_t ConcurrentMarking::GetMinorMaxConcurrency(size_t worker_count) {
size_t marking_items = marking_worklists_->shared()->Size() +
heap_->minor_mark_sweep_collector()
->remembered_sets_marking_handler()
->RemainingRememberedSetsMarkingIteams();
DCHECK(marking_worklists_->other()->IsEmpty());
DCHECK(!marking_worklists_->IsUsingContextWorklists());
return std::min<size_t>(task_state_.size() - 1, worker_count + marking_items);
}
void ConcurrentMarking::TryScheduleJob(GarbageCollector garbage_collector,
TaskPriority priority) {
DCHECK(v8_flags.parallel_marking || v8_flags.concurrent_marking);
DCHECK(!heap_->IsTearingDown());
DCHECK(IsStopped());
if (garbage_collector == GarbageCollector::MARK_COMPACTOR &&
!heap_->mark_compact_collector()->UseBackgroundThreadsInCycle()) {
return;
}
if (v8_flags.concurrent_marking_high_priority_threads) {
priority = TaskPriority::kUserBlocking;
}
DCHECK_NULL(minor_marking_state_);
DCHECK(
std::all_of(task_state_.begin(), task_state_.end(), [](auto& task_state) {
return task_state->local_pretenuring_feedback.empty();
}));
garbage_collector_ = garbage_collector;
if (garbage_collector == GarbageCollector::MARK_COMPACTOR) {
marking_worklists_ = heap_->mark_compact_collector()->marking_worklists();
auto job = std::make_unique<JobTaskMajor>(
this, heap_->mark_compact_collector()->epoch(),
heap_->mark_compact_collector()->code_flush_mode(),
heap_->ShouldCurrentGCKeepAgesUnchanged());
current_job_trace_id_.emplace(job->trace_id());
TRACE_GC_NOTE_WITH_FLOW("Major concurrent marking started", job->trace_id(),
TRACE_EVENT_FLAG_FLOW_OUT);
job_handle_ = V8::GetCurrentPlatform()->PostJob(priority, std::move(job));
} else {
DCHECK(garbage_collector == GarbageCollector::MINOR_MARK_SWEEPER);
minor_marking_state_ = std::make_unique<MinorMarkingState>();
marking_worklists_ =
heap_->minor_mark_sweep_collector()->marking_worklists();
auto job = std::make_unique<JobTaskMinor>(this);
current_job_trace_id_.emplace(job->trace_id());
TRACE_GC_NOTE_WITH_FLOW("Minor concurrent marking started", job->trace_id(),
TRACE_EVENT_FLAG_FLOW_OUT);
job_handle_ = V8::GetCurrentPlatform()->PostJob(priority, std::move(job));
}
DCHECK(job_handle_->IsValid());
}
bool ConcurrentMarking::IsWorkLeft() const {
DCHECK(garbage_collector_.has_value());
if (garbage_collector_ == GarbageCollector::MARK_COMPACTOR) {
return !marking_worklists_->shared()->IsEmpty() ||
!weak_objects_->current_ephemerons.IsEmpty() ||
!weak_objects_->discovered_ephemerons.IsEmpty();
}
DCHECK_EQ(GarbageCollector::MINOR_MARK_SWEEPER, garbage_collector_);
return !marking_worklists_->shared()->IsEmpty() ||
(heap_->minor_mark_sweep_collector()
->remembered_sets_marking_handler()
->RemainingRememberedSetsMarkingIteams() > 0);
}
void ConcurrentMarking::RescheduleJobIfNeeded(
GarbageCollector garbage_collector, TaskPriority priority) {
DCHECK(v8_flags.parallel_marking || v8_flags.concurrent_marking);
if (garbage_collector == GarbageCollector::MARK_COMPACTOR &&
!heap_->mark_compact_collector()->UseBackgroundThreadsInCycle()) {
return;
}
if (heap_->IsTearingDown()) return;
if (IsStopped()) {
// This DCHECK is for the case that concurrent marking was paused.
DCHECK_IMPLIES(garbage_collector_.has_value(),
garbage_collector == garbage_collector_);
TryScheduleJob(garbage_collector, priority);
} else {
DCHECK(garbage_collector_.has_value());
DCHECK_EQ(garbage_collector, garbage_collector_.value());
if (!IsWorkLeft()) return;
if (priority != TaskPriority::kUserVisible)
job_handle_->UpdatePriority(priority);
DCHECK(current_job_trace_id_.has_value());
TRACE_GC_NOTE_WITH_FLOW(
garbage_collector_ == GarbageCollector::MARK_COMPACTOR
? "Major concurrent marking rescheduled"
: "Minor concurrent marking rescheduled",
current_job_trace_id_.value(),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
job_handle_->NotifyConcurrencyIncrease();
}
}
void ConcurrentMarking::FlushPretenuringFeedback() {
PretenuringHandler* pretenuring_handler = heap_->pretenuring_handler();
for (auto& task_state : task_state_) {
pretenuring_handler->MergeAllocationSitePretenuringFeedback(
task_state->local_pretenuring_feedback);
task_state->local_pretenuring_feedback.clear();
}
}
void ConcurrentMarking::Join() {
DCHECK(v8_flags.parallel_marking || v8_flags.concurrent_marking);
DCHECK_IMPLIES(
garbage_collector_ == GarbageCollector::MARK_COMPACTOR,
heap_->mark_compact_collector()->UseBackgroundThreadsInCycle());
if (!job_handle_ || !job_handle_->IsValid()) return;
job_handle_->Join();
current_job_trace_id_.reset();
garbage_collector_.reset();
minor_marking_state_.reset();
}
bool ConcurrentMarking::Pause() {
DCHECK(v8_flags.parallel_marking || v8_flags.concurrent_marking);
if (!job_handle_ || !job_handle_->IsValid()) return false;
job_handle_->Cancel();
DCHECK(current_job_trace_id_.has_value());
TRACE_GC_NOTE_WITH_FLOW(garbage_collector_ == GarbageCollector::MARK_COMPACTOR
? "Major concurrent marking paused"
: "Minor concurrent marking paused",
current_job_trace_id_.value(),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
return true;
}
bool ConcurrentMarking::IsStopped() {
if (!v8_flags.concurrent_marking && !v8_flags.parallel_marking) return true;
return !job_handle_ || !job_handle_->IsValid();
}
void ConcurrentMarking::Resume() {
DCHECK(garbage_collector_.has_value());
DCHECK(current_job_trace_id_.has_value());
TRACE_GC_NOTE_WITH_FLOW(garbage_collector_ == GarbageCollector::MARK_COMPACTOR
? "Major concurrent marking resumed"
: "Minor concurrent marking resumed",
current_job_trace_id_.value(),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
RescheduleJobIfNeeded(garbage_collector_.value());
}
void ConcurrentMarking::FlushNativeContexts(NativeContextStats* main_stats) {
DCHECK(!job_handle_ || !job_handle_->IsValid());
for (size_t i = 1; i < task_state_.size(); i++) {
main_stats->Merge(task_state_[i]->native_context_stats);
task_state_[i]->native_context_stats.Clear();
}
}
void ConcurrentMarking::FlushMemoryChunkData() {
DCHECK(!job_handle_ || !job_handle_->IsValid());
for (size_t i = 1; i < task_state_.size(); i++) {
MemoryChunkDataMap& memory_chunk_data = task_state_[i]->memory_chunk_data;
for (auto& pair : memory_chunk_data) {
// ClearLiveness sets the live bytes to zero.
// Pages with zero live bytes might be already unmapped.
MemoryChunk* memory_chunk = pair.first;
MemoryChunkData& data = pair.second;
if (data.live_bytes) {
memory_chunk->IncrementLiveBytesAtomically(data.live_bytes);
}
if (data.typed_slots) {
RememberedSet<OLD_TO_OLD>::MergeTyped(memory_chunk,
std::move(data.typed_slots));
}
}
memory_chunk_data.clear();
task_state_[i]->marked_bytes = 0;
}
total_marked_bytes_ = 0;
}
void ConcurrentMarking::ClearMemoryChunkData(MemoryChunk* chunk) {
DCHECK(!job_handle_ || !job_handle_->IsValid());
for (size_t i = 1; i < task_state_.size(); i++) {
task_state_[i]->memory_chunk_data.erase(chunk);
}
}
size_t ConcurrentMarking::TotalMarkedBytes() {
size_t result = 0;
for (size_t i = 1; i < task_state_.size(); i++) {
result +=
base::AsAtomicWord::Relaxed_Load<size_t>(&task_state_[i]->marked_bytes);
}
result += total_marked_bytes_;
return result;
}
ConcurrentMarking::PauseScope::PauseScope(ConcurrentMarking* concurrent_marking)
: concurrent_marking_(concurrent_marking),
resume_on_exit_(v8_flags.concurrent_marking &&
concurrent_marking_->Pause()) {
DCHECK(!v8_flags.minor_ms);
DCHECK_IMPLIES(resume_on_exit_, v8_flags.concurrent_marking);
}
ConcurrentMarking::PauseScope::~PauseScope() {
if (resume_on_exit_) {
DCHECK_EQ(concurrent_marking_->garbage_collector_,
GarbageCollector::MARK_COMPACTOR);
concurrent_marking_->Resume();
}
}
} // namespace internal
} // namespace v8