Use idle task to perform incremental marking steps.
authorulan <ulan@chromium.org>
Tue, 8 Sep 2015 15:54:24 +0000 (08:54 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 8 Sep 2015 15:54:37 +0000 (15:54 +0000)
This moves incremental marking steps from gc-idle-time-handler and heap to the new incremental marking task.

BUG=chromium:490559
LOG=NO

Review URL: https://codereview.chromium.org/1265423002

Cr-Commit-Position: refs/heads/master@{#30641}

15 files changed:
BUILD.gn
src/heap/gc-idle-time-handler.cc
src/heap/gc-idle-time-handler.h
src/heap/heap.cc
src/heap/heap.h
src/heap/incremental-marking-job.cc [new file with mode: 0644]
src/heap/incremental-marking-job.h [new file with mode: 0644]
src/heap/incremental-marking.cc
src/heap/incremental-marking.h
src/v8.cc
src/v8.h
test/cctest/cctest.gyp
test/cctest/test-incremental-marking.cc [new file with mode: 0644]
test/unittests/heap/gc-idle-time-handler-unittest.cc
tools/gyp/v8.gyp

index ea8b558..0191508 100644 (file)
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -968,6 +968,8 @@ source_set("v8_base") {
     "src/heap/heap-inl.h",
     "src/heap/heap.cc",
     "src/heap/heap.h",
+    "src/heap/incremental-marking-job.cc",
+    "src/heap/incremental-marking-job.h",
     "src/heap/incremental-marking.cc",
     "src/heap/incremental-marking.h",
     "src/heap/mark-compact-inl.h",
index b51b53b..f9783b3 100644 (file)
@@ -26,9 +26,8 @@ void GCIdleTimeAction::Print() {
     case DO_NOTHING:
       PrintF("no action");
       break;
-    case DO_INCREMENTAL_MARKING:
-      PrintF("incremental marking with step %" V8_PTR_PREFIX "d / ms",
-             parameter);
+    case DO_INCREMENTAL_STEP:
+      PrintF("incremental step");
       if (additional_work) {
         PrintF("; finalized marking");
       }
@@ -39,9 +38,6 @@ void GCIdleTimeAction::Print() {
     case DO_FULL_GC:
       PrintF("full GC");
       break;
-    case DO_FINALIZE_SWEEPING:
-      PrintF("finalize sweeping");
-      break;
   }
 }
 
@@ -271,22 +267,11 @@ GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms,
     return GCIdleTimeAction::Scavenge();
   }
 
-  if (heap_state.sweeping_in_progress) {
-    if (heap_state.sweeping_completed) {
-      return GCIdleTimeAction::FinalizeSweeping();
-    } else {
-      return NothingOrDone(idle_time_in_ms);
-    }
-  }
-
   if (!FLAG_incremental_marking || heap_state.incremental_marking_stopped) {
     return GCIdleTimeAction::Done();
   }
 
-  size_t step_size = EstimateMarkingStepSize(
-      static_cast<size_t>(kIncrementalMarkingStepTimeInMs),
-      heap_state.incremental_marking_speed_in_bytes_per_ms);
-  return GCIdleTimeAction::IncrementalMarking(step_size);
+  return GCIdleTimeAction::IncrementalStep();
 }
 
 
index ebd132e..3f7f02d 100644 (file)
@@ -13,10 +13,9 @@ namespace internal {
 enum GCIdleTimeActionType {
   DONE,
   DO_NOTHING,
-  DO_INCREMENTAL_MARKING,
+  DO_INCREMENTAL_STEP,
   DO_SCAVENGE,
   DO_FULL_GC,
-  DO_FINALIZE_SWEEPING
 };
 
 
@@ -25,7 +24,6 @@ class GCIdleTimeAction {
   static GCIdleTimeAction Done() {
     GCIdleTimeAction result;
     result.type = DONE;
-    result.parameter = 0;
     result.additional_work = false;
     return result;
   }
@@ -33,15 +31,13 @@ class GCIdleTimeAction {
   static GCIdleTimeAction Nothing() {
     GCIdleTimeAction result;
     result.type = DO_NOTHING;
-    result.parameter = 0;
     result.additional_work = false;
     return result;
   }
 
-  static GCIdleTimeAction IncrementalMarking(intptr_t step_size) {
+  static GCIdleTimeAction IncrementalStep() {
     GCIdleTimeAction result;
-    result.type = DO_INCREMENTAL_MARKING;
-    result.parameter = step_size;
+    result.type = DO_INCREMENTAL_STEP;
     result.additional_work = false;
     return result;
   }
@@ -49,7 +45,6 @@ class GCIdleTimeAction {
   static GCIdleTimeAction Scavenge() {
     GCIdleTimeAction result;
     result.type = DO_SCAVENGE;
-    result.parameter = 0;
     result.additional_work = false;
     return result;
   }
@@ -57,15 +52,6 @@ class GCIdleTimeAction {
   static GCIdleTimeAction FullGC() {
     GCIdleTimeAction result;
     result.type = DO_FULL_GC;
-    result.parameter = 0;
-    result.additional_work = false;
-    return result;
-  }
-
-  static GCIdleTimeAction FinalizeSweeping() {
-    GCIdleTimeAction result;
-    result.type = DO_FINALIZE_SWEEPING;
-    result.parameter = 0;
     result.additional_work = false;
     return result;
   }
@@ -73,7 +59,6 @@ class GCIdleTimeAction {
   void Print();
 
   GCIdleTimeActionType type;
-  intptr_t parameter;
   bool additional_work;
 };
 
index a0858ee..cfe4630 100644 (file)
@@ -4535,10 +4535,12 @@ void Heap::FinalizeIncrementalMarkingIfComplete(const char* comment) {
 }
 
 
-bool Heap::TryFinalizeIdleIncrementalMarking(
-    double idle_time_in_ms, size_t size_of_objects,
-    size_t final_incremental_mark_compact_speed_in_bytes_per_ms) {
-  if (FLAG_overapproximate_weak_closure && incremental_marking()->IsMarking() &&
+bool Heap::TryFinalizeIdleIncrementalMarking(double idle_time_in_ms) {
+  size_t size_of_objects = static_cast<size_t>(SizeOfObjects());
+  size_t final_incremental_mark_compact_speed_in_bytes_per_ms =
+      static_cast<size_t>(
+          tracer()->FinalIncrementalMarkCompactSpeedInBytesPerMillisecond());
+  if (FLAG_overapproximate_weak_closure &&
       (incremental_marking()->IsReadyToOverApproximateWeakClosure() ||
        (!incremental_marking()->weak_closure_was_overapproximated() &&
         mark_compact_collector_.marking_deque()->IsEmpty() &&
@@ -4565,19 +4567,9 @@ GCIdleTimeHandler::HeapState Heap::ComputeHeapState() {
   heap_state.contexts_disposed = contexts_disposed_;
   heap_state.contexts_disposal_rate =
       tracer()->ContextDisposalRateInMilliseconds();
-  heap_state.size_of_objects = static_cast<size_t>(SizeOfObjects());
   heap_state.incremental_marking_stopped = incremental_marking()->IsStopped();
-  heap_state.sweeping_in_progress =
-      mark_compact_collector()->sweeping_in_progress();
-  heap_state.sweeping_completed =
-      mark_compact_collector()->IsSweepingCompleted();
   heap_state.mark_compact_speed_in_bytes_per_ms =
       static_cast<size_t>(tracer()->MarkCompactSpeedInBytesPerMillisecond());
-  heap_state.incremental_marking_speed_in_bytes_per_ms = static_cast<size_t>(
-      tracer()->IncrementalMarkingSpeedInBytesPerMillisecond());
-  heap_state.final_incremental_mark_compact_speed_in_bytes_per_ms =
-      static_cast<size_t>(
-          tracer()->FinalIncrementalMarkCompactSpeedInBytesPerMillisecond());
   heap_state.scavenge_speed_in_bytes_per_ms =
       static_cast<size_t>(tracer()->ScavengeSpeedInBytesPerMillisecond());
   heap_state.used_new_space_size = new_space_.Size();
@@ -4622,14 +4614,15 @@ bool Heap::PerformIdleTimeAction(GCIdleTimeAction action,
     case DONE:
       result = true;
       break;
-    case DO_INCREMENTAL_MARKING: {
-      const double remaining_idle_time_in_ms =
-          AdvanceIncrementalMarking(action.parameter, deadline_in_ms,
-                                    IncrementalMarking::IdleStepActions());
-      if (remaining_idle_time_in_ms > 0.0) {
-        action.additional_work = TryFinalizeIdleIncrementalMarking(
-            remaining_idle_time_in_ms, heap_state.size_of_objects,
-            heap_state.final_incremental_mark_compact_speed_in_bytes_per_ms);
+    case DO_INCREMENTAL_STEP: {
+      if (incremental_marking()->incremental_marking_job()->IdleTaskPending()) {
+        result = true;
+      } else {
+        incremental_marking()
+            ->incremental_marking_job()
+            ->NotifyIdleTaskProgress();
+        result = IncrementalMarkingJob::IdleTask::Step(this, deadline_in_ms) ==
+                 IncrementalMarkingJob::IdleTask::kDone;
       }
       break;
     }
@@ -4642,9 +4635,6 @@ bool Heap::PerformIdleTimeAction(GCIdleTimeAction action,
     case DO_SCAVENGE:
       CollectGarbage(NEW_SPACE, "idle notification: scavenge");
       break;
-    case DO_FINALIZE_SWEEPING:
-      mark_compact_collector()->EnsureSweepingCompleted();
-      break;
     case DO_NOTHING:
       break;
   }
index bb54a1d..9ae12f7 100644 (file)
@@ -1278,6 +1278,8 @@ class Heap {
 
   void FinalizeIncrementalMarkingIfComplete(const char* comment);
 
+  bool TryFinalizeIdleIncrementalMarking(double idle_time_in_ms);
+
   IncrementalMarking* incremental_marking() { return &incremental_marking_; }
 
   // ===========================================================================
diff --git a/src/heap/incremental-marking-job.cc b/src/heap/incremental-marking-job.cc
new file mode 100644 (file)
index 0000000..308d1b9
--- /dev/null
@@ -0,0 +1,144 @@
+// Copyright 2012 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/incremental-marking-job.h"
+
+#include "src/base/platform/time.h"
+#include "src/heap/heap-inl.h"
+#include "src/heap/heap.h"
+#include "src/heap/incremental-marking.h"
+#include "src/isolate.h"
+#include "src/v8.h"
+
+namespace v8 {
+namespace internal {
+
+
+void IncrementalMarkingJob::Start(Heap* heap) {
+  DCHECK(!heap->incremental_marking()->IsStopped());
+  // We don't need to reset the flags because tasks from the previous job
+  // can still be pending. We just want to ensure that tasks are posted
+  // if they are not pending.
+  // If delayed task is pending and made_progress_since_last_delayed_task_ is
+  // true, then the delayed task will clear that flag when it is rescheduled.
+  ScheduleIdleTask(heap);
+  ScheduleDelayedTask(heap);
+}
+
+
+void IncrementalMarkingJob::NotifyIdleTask() { idle_task_pending_ = false; }
+
+
+void IncrementalMarkingJob::NotifyDelayedTask() {
+  delayed_task_pending_ = false;
+}
+
+
+void IncrementalMarkingJob::NotifyIdleTaskProgress() {
+  made_progress_since_last_delayed_task_ = true;
+}
+
+
+void IncrementalMarkingJob::ScheduleIdleTask(Heap* heap) {
+  if (!idle_task_pending_) {
+    v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(heap->isolate());
+    if (V8::GetCurrentPlatform()->IdleTasksEnabled(isolate)) {
+      idle_task_pending_ = true;
+      auto task = new IdleTask(heap->isolate(), this);
+      V8::GetCurrentPlatform()->CallIdleOnForegroundThread(isolate, task);
+    }
+  }
+}
+
+
+void IncrementalMarkingJob::ScheduleDelayedTask(Heap* heap) {
+  if (!delayed_task_pending_) {
+    v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(heap->isolate());
+    delayed_task_pending_ = true;
+    made_progress_since_last_delayed_task_ = false;
+    auto task = new DelayedTask(heap->isolate(), this);
+    V8::GetCurrentPlatform()->CallDelayedOnForegroundThread(isolate, task,
+                                                            kDelayInSeconds);
+  }
+}
+
+
+IncrementalMarkingJob::IdleTask::Progress IncrementalMarkingJob::IdleTask::Step(
+    Heap* heap, double deadline_in_ms) {
+  IncrementalMarking* incremental_marking = heap->incremental_marking();
+  MarkCompactCollector* mark_compact_collector = heap->mark_compact_collector();
+  if (incremental_marking->IsStopped()) {
+    return kDone;
+  }
+  if (mark_compact_collector->sweeping_in_progress()) {
+    if (mark_compact_collector->IsSweepingCompleted()) {
+      mark_compact_collector->EnsureSweepingCompleted();
+    }
+    return kMoreWork;
+  }
+  const double remaining_idle_time_in_ms = heap->AdvanceIncrementalMarking(
+      0, deadline_in_ms, IncrementalMarking::IdleStepActions());
+  if (remaining_idle_time_in_ms > 0.0) {
+    heap->TryFinalizeIdleIncrementalMarking(remaining_idle_time_in_ms);
+  }
+  return incremental_marking->IsStopped() ? kDone : kMoreWork;
+}
+
+
+void IncrementalMarkingJob::IdleTask::RunInternal(double deadline_in_seconds) {
+  double deadline_in_ms =
+      deadline_in_seconds *
+      static_cast<double>(base::Time::kMillisecondsPerSecond);
+  Heap* heap = isolate_->heap();
+  double start_ms = heap->MonotonicallyIncreasingTimeInMs();
+  job_->NotifyIdleTask();
+  job_->NotifyIdleTaskProgress();
+  if (Step(heap, deadline_in_ms) == kMoreWork) {
+    job_->ScheduleIdleTask(heap);
+  }
+  if (FLAG_trace_idle_notification) {
+    double current_time_ms = heap->MonotonicallyIncreasingTimeInMs();
+    double idle_time_in_ms = deadline_in_ms - start_ms;
+    double deadline_difference = deadline_in_ms - current_time_ms;
+    PrintIsolate(isolate_, "%8.0f ms: ", isolate_->time_millis_since_init());
+    PrintF(
+        "Idle task: requested idle time %.2f ms, used idle time %.2f "
+        "ms, deadline usage %.2f ms\n",
+        idle_time_in_ms, idle_time_in_ms - deadline_difference,
+        deadline_difference);
+  }
+}
+
+
+void IncrementalMarkingJob::DelayedTask::Step(Heap* heap) {
+  const int kIncrementalMarkingDelayMs = 50;
+  double deadline =
+      heap->MonotonicallyIncreasingTimeInMs() + kIncrementalMarkingDelayMs;
+  heap->AdvanceIncrementalMarking(
+      0, deadline, i::IncrementalMarking::StepActions(
+                       i::IncrementalMarking::NO_GC_VIA_STACK_GUARD,
+                       i::IncrementalMarking::FORCE_MARKING,
+                       i::IncrementalMarking::FORCE_COMPLETION));
+  heap->FinalizeIncrementalMarkingIfComplete(
+      "Incremental marking task: finalize incremental marking");
+}
+
+
+void IncrementalMarkingJob::DelayedTask::RunInternal() {
+  Heap* heap = isolate_->heap();
+  job_->NotifyDelayedTask();
+  IncrementalMarking* incremental_marking = heap->incremental_marking();
+  if (!incremental_marking->IsStopped()) {
+    if (job_->ShouldForceMarkingStep()) {
+      Step(heap);
+    }
+    // The Step() above could have finished incremental marking.
+    if (!incremental_marking->IsStopped()) {
+      job_->ScheduleDelayedTask(heap);
+    }
+  }
+}
+
+}  // namespace internal
+}  // namespace v8
diff --git a/src/heap/incremental-marking-job.h b/src/heap/incremental-marking-job.h
new file mode 100644 (file)
index 0000000..fad46c1
--- /dev/null
@@ -0,0 +1,81 @@
+// Copyright 2012 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_INCREMENTAL_MARKING_JOB_H_
+#define V8_HEAP_INCREMENTAL_MARKING_JOB_H_
+
+#include "src/cancelable-task.h"
+
+namespace v8 {
+namespace internal {
+
+class Heap;
+class Isolate;
+
+// The incremental marking job uses platform tasks to perform incremental
+// marking steps. The job posts an idle and a delayed task with a large delay.
+// The delayed task performs steps only if the idle task is not making progress.
+// We expect this to be a rare event since incremental marking should finish
+// quickly with the help of the mutator and the idle task.
+// The delayed task guarantees that we eventually finish incremental marking
+// even if the mutator becomes idle and the platform stops running idle tasks,
+// which can happen for background tabs in Chrome.
+class IncrementalMarkingJob {
+ public:
+  class IdleTask : public CancelableIdleTask {
+   public:
+    explicit IdleTask(Isolate* isolate, IncrementalMarkingJob* job)
+        : CancelableIdleTask(isolate), job_(job) {}
+    enum Progress { kDone, kMoreWork };
+    static Progress Step(Heap* heap, double deadline_in_ms);
+    // CancelableIdleTask overrides.
+    void RunInternal(double deadline_in_seconds) override;
+
+   private:
+    IncrementalMarkingJob* job_;
+  };
+
+  class DelayedTask : public CancelableTask {
+   public:
+    explicit DelayedTask(Isolate* isolate, IncrementalMarkingJob* job)
+        : CancelableTask(isolate), job_(job) {}
+    static void Step(Heap* heap);
+    // CancelableTask overrides.
+    void RunInternal() override;
+
+   private:
+    IncrementalMarkingJob* job_;
+  };
+
+  // Delay of the delayed task.
+  static const int kDelayInSeconds = 5;
+
+  IncrementalMarkingJob()
+      : idle_task_pending_(false),
+        delayed_task_pending_(false),
+        made_progress_since_last_delayed_task_(false) {}
+
+  bool ShouldForceMarkingStep() {
+    return !made_progress_since_last_delayed_task_;
+  }
+
+  bool IdleTaskPending() { return idle_task_pending_; }
+
+  void Start(Heap* heap);
+
+  void NotifyIdleTask();
+  void NotifyDelayedTask();
+  void NotifyIdleTaskProgress();
+  void ScheduleIdleTask(Heap* heap);
+  void ScheduleDelayedTask(Heap* heap);
+
+ private:
+  bool idle_task_pending_;
+  bool delayed_task_pending_;
+  bool made_progress_since_last_delayed_task_;
+};
+}
+}  // namespace v8::internal
+
+#endif  // V8_HEAP_INCREMENTAL_MARKING_JOB_H_
index b420cb7..4fe9eef 100644 (file)
@@ -11,6 +11,7 @@
 #include "src/heap/mark-compact-inl.h"
 #include "src/heap/objects-visiting.h"
 #include "src/heap/objects-visiting-inl.h"
+#include "src/v8.h"
 
 namespace v8 {
 namespace internal {
@@ -486,6 +487,7 @@ void IncrementalMarking::Start(const char* reason) {
   }
 
   heap_->new_space()->LowerInlineAllocationLimit(kAllocatedThreshold);
+  incremental_marking_job()->Start(heap_);
 }
 
 
index 18c8c0d..e0b449a 100644 (file)
@@ -5,14 +5,15 @@
 #ifndef V8_HEAP_INCREMENTAL_MARKING_H_
 #define V8_HEAP_INCREMENTAL_MARKING_H_
 
+#include "src/cancelable-task.h"
 #include "src/execution.h"
+#include "src/heap/incremental-marking-job.h"
 #include "src/heap/mark-compact.h"
 #include "src/objects.h"
 
 namespace v8 {
 namespace internal {
 
-
 class IncrementalMarking {
  public:
   enum State { STOPPED, SWEEPING, MARKING, COMPLETE };
@@ -197,6 +198,10 @@ class IncrementalMarking {
 
   Heap* heap() const { return heap_; }
 
+  IncrementalMarkingJob* incremental_marking_job() {
+    return &incremental_marking_job_;
+  }
+
  private:
   int64_t SpaceLeftInOldSpace();
 
@@ -255,6 +260,8 @@ class IncrementalMarking {
 
   GCRequestType request_type_;
 
+  IncrementalMarkingJob incremental_marking_job_;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(IncrementalMarking);
 };
 }
index c7effb6..d7cc797 100644 (file)
--- a/src/v8.cc
+++ b/src/v8.cc
@@ -119,6 +119,9 @@ v8::Platform* V8::GetCurrentPlatform() {
 }
 
 
+void V8::SetPlatformForTesting(v8::Platform* platform) { platform_ = platform; }
+
+
 void V8::SetNativesBlob(StartupData* natives_blob) {
 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
   base::CallOnce(&init_natives_once, &SetNativesFromFile, natives_blob);
index 3035b9a..f5b3b84 100644 (file)
--- a/src/v8.h
+++ b/src/v8.h
@@ -26,6 +26,9 @@ class V8 : public AllStatic {
   static void InitializePlatform(v8::Platform* platform);
   static void ShutdownPlatform();
   static v8::Platform* GetCurrentPlatform();
+  // Replaces the current platform with the given platform.
+  // Should be used only for testing.
+  static void SetPlatformForTesting(v8::Platform* platform);
 
   static void SetNativesBlob(StartupData* natives_blob);
   static void SetSnapshotBlob(StartupData* snapshot_blob);
index f9ffade..d15820a 100644 (file)
         'test-heap-profiler.cc',
         'test-hydrogen-types.cc',
         'test-identity-map.cc',
+        'test-incremental-marking.cc',
         'test-list.cc',
         'test-liveedit.cc',
         'test-lockers.cc',
diff --git a/test/cctest/test-incremental-marking.cc b/test/cctest/test-incremental-marking.cc
new file mode 100644 (file)
index 0000000..d8bdeee
--- /dev/null
@@ -0,0 +1,168 @@
+// Copyright 2015 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 <stdlib.h>
+
+#ifdef __linux__
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include <utility>
+
+#include "src/v8.h"
+
+#include "src/full-codegen/full-codegen.h"
+#include "src/global-handles.h"
+#include "test/cctest/cctest.h"
+
+using v8::IdleTask;
+using v8::Task;
+using v8::Isolate;
+
+
+class MockPlatform : public v8::Platform {
+ public:
+  explicit MockPlatform(v8::Platform* platform)
+      : platform_(platform), idle_task_(nullptr), delayed_task_(nullptr) {}
+  virtual ~MockPlatform() {
+    delete idle_task_;
+    delete delayed_task_;
+  }
+
+  void CallOnBackgroundThread(Task* task,
+                              ExpectedRuntime expected_runtime) override {
+    platform_->CallOnBackgroundThread(task, expected_runtime);
+  }
+
+  void CallOnForegroundThread(Isolate* isolate, Task* task) override {
+    platform_->CallOnForegroundThread(isolate, task);
+  }
+
+  void CallDelayedOnForegroundThread(Isolate* isolate, Task* task,
+                                     double delay_in_seconds) override {
+    if (delayed_task_ != nullptr) {
+      delete delayed_task_;
+    }
+    delayed_task_ = task;
+  }
+
+  double MonotonicallyIncreasingTime() override {
+    return platform_->MonotonicallyIncreasingTime();
+  }
+
+  void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override {
+    CHECK(nullptr == idle_task_);
+    idle_task_ = task;
+  }
+
+  bool IdleTasksEnabled(Isolate* isolate) override { return true; }
+
+  bool PendingIdleTask() { return idle_task_ != nullptr; }
+
+  void PerformIdleTask(double idle_time_in_seconds) {
+    IdleTask* task = idle_task_;
+    idle_task_ = nullptr;
+    task->Run(MonotonicallyIncreasingTime() + idle_time_in_seconds);
+    delete task;
+  }
+
+  bool PendingDelayedTask() { return delayed_task_ != nullptr; }
+
+  void PerformDelayedTask() {
+    Task* task = delayed_task_;
+    delayed_task_ = nullptr;
+    task->Run();
+    delete task;
+  }
+
+ private:
+  v8::Platform* platform_;
+  IdleTask* idle_task_;
+  Task* delayed_task_;
+};
+
+
+TEST(IncrementalMarkingUsingIdleTasks) {
+  if (!i::FLAG_incremental_marking) return;
+  CcTest::InitializeVM();
+  v8::Platform* old_platform = i::V8::GetCurrentPlatform();
+  MockPlatform platform(old_platform);
+  i::V8::SetPlatformForTesting(&platform);
+  SimulateFullSpace(CcTest::heap()->old_space());
+  i::IncrementalMarking* marking = CcTest::heap()->incremental_marking();
+  marking->Stop();
+  marking->Start();
+  CHECK(platform.PendingIdleTask());
+  const double kLongIdleTimeInSeconds = 1;
+  const double kShortIdleTimeInSeconds = 0.010;
+  const int kShortStepCount = 10;
+  for (int i = 0; i < kShortStepCount && platform.PendingIdleTask(); i++) {
+    platform.PerformIdleTask(kShortIdleTimeInSeconds);
+  }
+  while (platform.PendingIdleTask()) {
+    platform.PerformIdleTask(kLongIdleTimeInSeconds);
+  }
+  CHECK(marking->IsStopped());
+  i::V8::SetPlatformForTesting(old_platform);
+}
+
+
+TEST(IncrementalMarkingUsingIdleTasksAfterGC) {
+  if (!i::FLAG_incremental_marking) return;
+  CcTest::InitializeVM();
+  v8::Platform* old_platform = i::V8::GetCurrentPlatform();
+  MockPlatform platform(old_platform);
+  i::V8::SetPlatformForTesting(&platform);
+  SimulateFullSpace(CcTest::heap()->old_space());
+  CcTest::heap()->CollectAllGarbage();
+  i::IncrementalMarking* marking = CcTest::heap()->incremental_marking();
+  marking->Stop();
+  marking->Start();
+  CHECK(platform.PendingIdleTask());
+  const double kLongIdleTimeInSeconds = 1;
+  const double kShortIdleTimeInSeconds = 0.010;
+  const int kShortStepCount = 10;
+  for (int i = 0; i < kShortStepCount && platform.PendingIdleTask(); i++) {
+    platform.PerformIdleTask(kShortIdleTimeInSeconds);
+  }
+  while (platform.PendingIdleTask()) {
+    platform.PerformIdleTask(kLongIdleTimeInSeconds);
+  }
+  CHECK(marking->IsStopped());
+  i::V8::SetPlatformForTesting(old_platform);
+}
+
+
+TEST(IncrementalMarkingUsingDelayedTasks) {
+  if (!i::FLAG_incremental_marking) return;
+  CcTest::InitializeVM();
+  v8::Platform* old_platform = i::V8::GetCurrentPlatform();
+  MockPlatform platform(old_platform);
+  i::V8::SetPlatformForTesting(&platform);
+  SimulateFullSpace(CcTest::heap()->old_space());
+  i::IncrementalMarking* marking = CcTest::heap()->incremental_marking();
+  marking->Stop();
+  marking->Start();
+  CHECK(platform.PendingIdleTask());
+  // The delayed task should be a no-op if the idle task makes progress.
+  const int kIgnoredDelayedTaskStepCount = 1000;
+  for (int i = 0; i < kIgnoredDelayedTaskStepCount; i++) {
+    // Dummy idle task progress.
+    marking->incremental_marking_job()->NotifyIdleTaskProgress();
+    CHECK(platform.PendingDelayedTask());
+    platform.PerformDelayedTask();
+  }
+  // Once we stop notifying idle task progress, the delayed tasks
+  // should finish marking.
+  while (!marking->IsStopped() && platform.PendingDelayedTask()) {
+    platform.PerformDelayedTask();
+  }
+  // There could be pending delayed task from memory reducer after GC finishes.
+  CHECK(marking->IsStopped());
+  i::V8::SetPlatformForTesting(old_platform);
+}
index e74152a..d87a921 100644 (file)
@@ -23,12 +23,8 @@ class GCIdleTimeHandlerTest : public ::testing::Test {
     GCIdleTimeHandler::HeapState result;
     result.contexts_disposed = 0;
     result.contexts_disposal_rate = GCIdleTimeHandler::kHighContextDisposalRate;
-    result.size_of_objects = kSizeOfObjects;
     result.incremental_marking_stopped = false;
-    result.sweeping_in_progress = false;
-    result.sweeping_completed = false;
     result.mark_compact_speed_in_bytes_per_ms = kMarkCompactSpeed;
-    result.incremental_marking_speed_in_bytes_per_ms = kMarkingSpeed;
     result.scavenge_speed_in_bytes_per_ms = kScavengeSpeed;
     result.used_new_space_size = 0;
     result.new_space_capacity = kNewSpaceCapacity;
@@ -259,10 +255,9 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime1) {
   heap_state.contexts_disposal_rate =
       GCIdleTimeHandler::kHighContextDisposalRate;
   size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
-  double idle_time_ms =
-      static_cast<double>(heap_state.size_of_objects / speed - 1);
+  double idle_time_ms = static_cast<double>(kSizeOfObjects / speed - 1);
   GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
-  EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
+  EXPECT_EQ(DO_INCREMENTAL_STEP, action.type);
 }
 
 
@@ -272,34 +267,17 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime2) {
   heap_state.contexts_disposal_rate =
       GCIdleTimeHandler::kHighContextDisposalRate;
   size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
-  double idle_time_ms =
-      static_cast<double>(heap_state.size_of_objects / speed - 1);
+  double idle_time_ms = static_cast<double>(kSizeOfObjects / speed - 1);
   GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
-  EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
+  EXPECT_EQ(DO_INCREMENTAL_STEP, action.type);
 }
 
 
 TEST_F(GCIdleTimeHandlerTest, IncrementalMarking1) {
   GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
-  size_t speed = heap_state.incremental_marking_speed_in_bytes_per_ms;
   double idle_time_ms = 10;
   GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
-  EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
-  EXPECT_GT(speed * static_cast<size_t>(idle_time_ms),
-            static_cast<size_t>(action.parameter));
-  EXPECT_LT(0, action.parameter);
-}
-
-
-TEST_F(GCIdleTimeHandlerTest, IncrementalMarking2) {
-  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
-  size_t speed = heap_state.incremental_marking_speed_in_bytes_per_ms;
-  double idle_time_ms = 10;
-  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
-  EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
-  EXPECT_GT(speed * static_cast<size_t>(idle_time_ms),
-            static_cast<size_t>(action.parameter));
-  EXPECT_LT(0, action.parameter);
+  EXPECT_EQ(DO_INCREMENTAL_STEP, action.type);
 }
 
 
@@ -307,35 +285,12 @@ TEST_F(GCIdleTimeHandlerTest, NotEnoughTime) {
   GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
   heap_state.incremental_marking_stopped = true;
   size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
-  double idle_time_ms =
-      static_cast<double>(heap_state.size_of_objects / speed - 1);
+  double idle_time_ms = static_cast<double>(kSizeOfObjects / speed - 1);
   GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
   EXPECT_EQ(DONE, action.type);
 }
 
 
-TEST_F(GCIdleTimeHandlerTest, FinalizeSweeping) {
-  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
-  heap_state.incremental_marking_stopped = true;
-  heap_state.sweeping_in_progress = true;
-  heap_state.sweeping_completed = true;
-  double idle_time_ms = 10.0;
-  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
-  EXPECT_EQ(DO_FINALIZE_SWEEPING, action.type);
-}
-
-
-TEST_F(GCIdleTimeHandlerTest, CannotFinalizeSweeping) {
-  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
-  heap_state.incremental_marking_stopped = true;
-  heap_state.sweeping_in_progress = true;
-  heap_state.sweeping_completed = false;
-  double idle_time_ms = 10.0;
-  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
-  EXPECT_EQ(DO_NOTHING, action.type);
-}
-
-
 TEST_F(GCIdleTimeHandlerTest, Scavenge) {
   GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
   int idle_time_ms = 10;
@@ -382,7 +337,7 @@ TEST_F(GCIdleTimeHandlerTest, ContinueAfterStop) {
   EXPECT_EQ(DONE, action.type);
   heap_state.incremental_marking_stopped = false;
   action = handler()->Compute(idle_time_ms, heap_state);
-  EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
+  EXPECT_EQ(DO_INCREMENTAL_STEP, action.type);
 }
 
 
@@ -405,25 +360,6 @@ TEST_F(GCIdleTimeHandlerTest, SmallIdleTimeNothingToDo) {
 }
 
 
-TEST_F(GCIdleTimeHandlerTest, DoneIfNotMakingProgressOnSweeping) {
-  // Regression test for crbug.com/489323.
-  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
-
-  // Simulate sweeping being in-progress but not complete.
-  heap_state.incremental_marking_stopped = true;
-  heap_state.sweeping_in_progress = true;
-  heap_state.sweeping_completed = false;
-  double idle_time_ms = 10.0;
-  for (int i = 0; i < GCIdleTimeHandler::kMaxNoProgressIdleTimes; i++) {
-    GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
-    EXPECT_EQ(DO_NOTHING, action.type);
-  }
-  // We should return DONE after not making progress for some time.
-  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
-  EXPECT_EQ(DONE, action.type);
-}
-
-
 TEST_F(GCIdleTimeHandlerTest, DoneIfNotMakingProgressOnIncrementalMarking) {
   // Regression test for crbug.com/489323.
   GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
index f336e04..808098e 100644 (file)
         '../../src/heap/heap.cc',
         '../../src/heap/heap.h',
         '../../src/heap/incremental-marking-inl.h',
+        '../../src/heap/incremental-marking-job.cc',
+        '../../src/heap/incremental-marking-job.h',
         '../../src/heap/incremental-marking.cc',
         '../../src/heap/incremental-marking.h',
         '../../src/heap/mark-compact-inl.h',