1// Copyright 2012 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/heap/incremental-marking-job.h"
6
7#include "src/base/platform/mutex.h"
8#include "src/base/platform/time.h"
9#include "src/execution/isolate.h"
10#include "src/execution/vm-state-inl.h"
11#include "src/heap/embedder-tracing.h"
12#include "src/heap/gc-tracer.h"
13#include "src/heap/heap-inl.h"
14#include "src/heap/heap.h"
15#include "src/heap/incremental-marking.h"
16#include "src/init/v8.h"
17
18namespace v8 {
19namespace internal {
20
21class IncrementalMarkingJob::Task : public CancelableTask {
22 public:
23  static StepResult Step(Heap* heap);
24
25  Task(Isolate* isolate, IncrementalMarkingJob* job,
26       EmbedderHeapTracer::EmbedderStackState stack_state, TaskType task_type)
27      : CancelableTask(isolate),
28        isolate_(isolate),
29        job_(job),
30        stack_state_(stack_state),
31        task_type_(task_type) {}
32
33  // CancelableTask overrides.
34  void RunInternal() override;
35
36  Isolate* isolate() const { return isolate_; }
37
38 private:
39  Isolate* const isolate_;
40  IncrementalMarkingJob* const job_;
41  const EmbedderHeapTracer::EmbedderStackState stack_state_;
42  const TaskType task_type_;
43};
44
45void IncrementalMarkingJob::Start(Heap* heap) {
46  DCHECK(!heap->incremental_marking()->IsStopped());
47  ScheduleTask(heap);
48}
49
50void IncrementalMarkingJob::ScheduleTask(Heap* heap, TaskType task_type) {
51  base::MutexGuard guard(&mutex_);
52
53  if (!IsTaskPending(task_type) && !heap->IsTearingDown() &&
54      FLAG_incremental_marking_task) {
55    v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(heap->isolate());
56    SetTaskPending(task_type, true);
57    auto taskrunner =
58        V8::GetCurrentPlatform()->GetForegroundTaskRunner(isolate);
59
60    const EmbedderHeapTracer::EmbedderStackState stack_state =
61        taskrunner->NonNestableTasksEnabled()
62            ? EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers
63            : EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers;
64    auto task =
65        std::make_unique<Task>(heap->isolate(), this, stack_state, task_type);
66    if (task_type == TaskType::kNormal) {
67      scheduled_time_ = heap->MonotonicallyIncreasingTimeInMs();
68      if (taskrunner->NonNestableTasksEnabled()) {
69        taskrunner->PostNonNestableTask(std::move(task));
70      } else {
71        taskrunner->PostTask(std::move(task));
72      }
73    } else {
74      if (taskrunner->NonNestableDelayedTasksEnabled()) {
75        taskrunner->PostNonNestableDelayedTask(std::move(task),
76                                               kDelayInSeconds);
77      } else {
78        taskrunner->PostDelayedTask(std::move(task), kDelayInSeconds);
79      }
80    }
81  }
82}
83
84StepResult IncrementalMarkingJob::Task::Step(Heap* heap) {
85  const int kIncrementalMarkingDelayMs = 1;
86  double deadline =
87      heap->MonotonicallyIncreasingTimeInMs() + kIncrementalMarkingDelayMs;
88  StepResult result = heap->incremental_marking()->AdvanceWithDeadline(
89      deadline, i::IncrementalMarking::NO_GC_VIA_STACK_GUARD,
90      i::StepOrigin::kTask);
91  heap->FinalizeIncrementalMarkingIfComplete(
92      GarbageCollectionReason::kFinalizeMarkingViaTask);
93  return result;
94}
95
96void IncrementalMarkingJob::Task::RunInternal() {
97  VMState<GC> state(isolate());
98  TRACE_EVENT_CALL_STATS_SCOPED(isolate(), "v8", "V8.Task");
99
100  Heap* heap = isolate()->heap();
101  EmbedderStackStateScope scope(
102      heap, EmbedderStackStateScope::kImplicitThroughTask, stack_state_);
103  if (task_type_ == TaskType::kNormal) {
104    heap->tracer()->RecordTimeToIncrementalMarkingTask(
105        heap->MonotonicallyIncreasingTimeInMs() - job_->scheduled_time_);
106    job_->scheduled_time_ = 0.0;
107  }
108  IncrementalMarking* incremental_marking = heap->incremental_marking();
109  if (incremental_marking->IsStopped()) {
110    if (heap->IncrementalMarkingLimitReached() !=
111        Heap::IncrementalMarkingLimit::kNoLimit) {
112      heap->StartIncrementalMarking(heap->GCFlagsForIncrementalMarking(),
113                                    GarbageCollectionReason::kTask,
114                                    kGCCallbackScheduleIdleGarbageCollection);
115    }
116  }
117
118  // Clear this flag after StartIncrementalMarking call to avoid
119  // scheduling a new task when starting incremental marking.
120  {
121    base::MutexGuard guard(&job_->mutex_);
122    job_->SetTaskPending(task_type_, false);
123  }
124
125  if (!incremental_marking->IsStopped()) {
126    // All objects are initialized at that point.
127    heap->new_space()->MarkLabStartInitialized();
128    heap->new_lo_space()->ResetPendingObject();
129    StepResult step_result = Step(heap);
130    if (!incremental_marking->IsStopped()) {
131      const TaskType task_type =
132          incremental_marking->finalize_marking_completed() ||
133                  step_result != StepResult::kNoImmediateWork
134              ? TaskType::kNormal
135              : TaskType::kDelayed;
136      job_->ScheduleTask(heap, task_type);
137    }
138  }
139}
140
141double IncrementalMarkingJob::CurrentTimeToTask(Heap* heap) const {
142  if (scheduled_time_ == 0.0) return 0.0;
143
144  return heap->MonotonicallyIncreasingTimeInMs() - scheduled_time_;
145}
146
147}  // namespace internal
148}  // namespace v8
149