| Index: src/heap/mark-compact.cc | 
| diff --git a/src/heap/mark-compact.cc b/src/heap/mark-compact.cc | 
| index 438bb4abf2ea076e48da074548eb28129f280260..33bf3da5c71664a5c1d0903daed9d3b7049c1dd3 100644 | 
| --- a/src/heap/mark-compact.cc | 
| +++ b/src/heap/mark-compact.cc | 
| @@ -665,11 +665,6 @@ void MarkCompactCollector::CollectEvacuationCandidates(PagedSpace* space) { | 
| while (it.has_next()) { | 
| Page* p = it.next(); | 
| if (p->NeverEvacuate()) continue; | 
| -    if (p->IsFlagSet(Page::POPULAR_PAGE)) { | 
| -      // This page had slots buffer overflow on previous GC, skip it. | 
| -      p->ClearFlag(Page::POPULAR_PAGE); | 
| -      continue; | 
| -    } | 
| // Invariant: Evacuation candidates are just created when marking is | 
| // started. This means that sweeping has finished. Furthermore, at the end | 
| // of a GC all evacuation candidates are cleared and their slot buffers are | 
| @@ -2958,12 +2953,8 @@ void MarkCompactCollector::EvacuateNewSpaceEpilogue() { | 
|  | 
| class MarkCompactCollector::Evacuator : public Malloced { | 
| public: | 
| -  Evacuator(MarkCompactCollector* collector, | 
| -            const List<Page*>& evacuation_candidates, | 
| -            const List<NewSpacePage*>& newspace_evacuation_candidates) | 
| +  explicit Evacuator(MarkCompactCollector* collector) | 
| : collector_(collector), | 
| -        evacuation_candidates_(evacuation_candidates), | 
| -        newspace_evacuation_candidates_(newspace_evacuation_candidates), | 
| compaction_spaces_(collector->heap()), | 
| local_pretenuring_feedback_(HashMap::PointersMatch, | 
| kInitialLocalPretenuringFeedbackCapacity), | 
| @@ -2973,11 +2964,9 @@ class MarkCompactCollector::Evacuator : public Malloced { | 
| old_space_visitor_(collector->heap(), &compaction_spaces_, | 
| &old_to_old_slots_, &old_to_new_slots_), | 
| duration_(0.0), | 
| -        bytes_compacted_(0), | 
| -        task_id_(0) {} | 
| +        bytes_compacted_(0) {} | 
|  | 
| -  // Evacuate the configured set of pages in parallel. | 
| -  inline void EvacuatePages(); | 
| +  inline bool EvacuatePage(MemoryChunk* chunk); | 
|  | 
| // Merge back locally cached info sequentially. Note that this method needs | 
| // to be called from the main thread. | 
| @@ -2985,9 +2974,6 @@ class MarkCompactCollector::Evacuator : public Malloced { | 
|  | 
| CompactionSpaceCollection* compaction_spaces() { return &compaction_spaces_; } | 
|  | 
| -  uint32_t task_id() { return task_id_; } | 
| -  void set_task_id(uint32_t id) { task_id_ = id; } | 
| - | 
| private: | 
| static const int kInitialLocalPretenuringFeedbackCapacity = 256; | 
|  | 
| @@ -3002,10 +2988,6 @@ class MarkCompactCollector::Evacuator : public Malloced { | 
|  | 
| MarkCompactCollector* collector_; | 
|  | 
| -  // Pages to process. | 
| -  const List<Page*>& evacuation_candidates_; | 
| -  const List<NewSpacePage*>& newspace_evacuation_candidates_; | 
| - | 
| // Locally cached collector data. | 
| CompactionSpaceCollection compaction_spaces_; | 
| LocalSlotsBuffer old_to_old_slots_; | 
| @@ -3019,60 +3001,40 @@ class MarkCompactCollector::Evacuator : public Malloced { | 
| // Book keeping info. | 
| double duration_; | 
| intptr_t bytes_compacted_; | 
| - | 
| -  // Task id, if this evacuator is executed on a background task instead of | 
| -  // the main thread. Can be used to try to abort the task currently scheduled | 
| -  // to executed to evacuate pages. | 
| -  uint32_t task_id_; | 
| }; | 
|  | 
| bool MarkCompactCollector::Evacuator::EvacuateSinglePage( | 
| MemoryChunk* p, HeapObjectVisitor* visitor) { | 
| -  bool success = true; | 
| -  if (p->parallel_compaction_state().TrySetValue( | 
| -          MemoryChunk::kCompactingDone, MemoryChunk::kCompactingInProgress)) { | 
| -    if (p->IsEvacuationCandidate() || p->InNewSpace()) { | 
| -      DCHECK_EQ(p->parallel_compaction_state().Value(), | 
| -                MemoryChunk::kCompactingInProgress); | 
| -      int saved_live_bytes = p->LiveBytes(); | 
| -      double evacuation_time; | 
| -      { | 
| -        AlwaysAllocateScope always_allocate(heap()->isolate()); | 
| -        TimedScope timed_scope(&evacuation_time); | 
| -        success = collector_->VisitLiveObjects(p, visitor, kClearMarkbits); | 
| -      } | 
| -      if (success) { | 
| -        ReportCompactionProgress(evacuation_time, saved_live_bytes); | 
| -        p->parallel_compaction_state().SetValue( | 
| -            MemoryChunk::kCompactingFinalize); | 
| -      } else { | 
| -        p->parallel_compaction_state().SetValue( | 
| -            MemoryChunk::kCompactingAborted); | 
| -      } | 
| -    } else { | 
| -      // There could be popular pages in the list of evacuation candidates | 
| -      // which we do not compact. | 
| -      p->parallel_compaction_state().SetValue(MemoryChunk::kCompactingDone); | 
| -    } | 
| +  bool success = false; | 
| +  DCHECK(p->IsEvacuationCandidate() || p->InNewSpace()); | 
| +  int saved_live_bytes = p->LiveBytes(); | 
| +  double evacuation_time; | 
| +  { | 
| +    AlwaysAllocateScope always_allocate(heap()->isolate()); | 
| +    TimedScope timed_scope(&evacuation_time); | 
| +    success = collector_->VisitLiveObjects(p, visitor, kClearMarkbits); | 
| +  } | 
| +  if (success) { | 
| +    ReportCompactionProgress(evacuation_time, saved_live_bytes); | 
| } | 
| return success; | 
| } | 
|  | 
| -void MarkCompactCollector::Evacuator::EvacuatePages() { | 
| -  for (NewSpacePage* p : newspace_evacuation_candidates_) { | 
| -    DCHECK(p->InNewSpace()); | 
| -    DCHECK_EQ(p->concurrent_sweeping_state().Value(), | 
| +bool MarkCompactCollector::Evacuator::EvacuatePage(MemoryChunk* chunk) { | 
| +  bool success = false; | 
| +  if (chunk->InNewSpace()) { | 
| +    DCHECK_EQ(chunk->concurrent_sweeping_state().Value(), | 
| NewSpacePage::kSweepingDone); | 
| -    bool success = EvacuateSinglePage(p, &new_space_visitor_); | 
| +    success = EvacuateSinglePage(chunk, &new_space_visitor_); | 
| DCHECK(success); | 
| USE(success); | 
| +  } else { | 
| +    DCHECK(chunk->IsEvacuationCandidate() || | 
| +           chunk->IsFlagSet(MemoryChunk::RESCAN_ON_EVACUATION)); | 
| +    DCHECK_EQ(chunk->concurrent_sweeping_state().Value(), Page::kSweepingDone); | 
| +    success = EvacuateSinglePage(chunk, &old_space_visitor_); | 
| } | 
| -  for (Page* p : evacuation_candidates_) { | 
| -    DCHECK(p->IsEvacuationCandidate() || | 
| -           p->IsFlagSet(MemoryChunk::RESCAN_ON_EVACUATION)); | 
| -    DCHECK_EQ(p->concurrent_sweeping_state().Value(), Page::kSweepingDone); | 
| -    EvacuateSinglePage(p, &old_space_visitor_); | 
| -  } | 
| +  return success; | 
| } | 
|  | 
| void MarkCompactCollector::Evacuator::Finalize() { | 
| @@ -3105,29 +3067,6 @@ void MarkCompactCollector::Evacuator::Finalize() { | 
| }); | 
| } | 
|  | 
| -class MarkCompactCollector::CompactionTask : public CancelableTask { | 
| - public: | 
| -  explicit CompactionTask(Heap* heap, Evacuator* evacuator) | 
| -      : CancelableTask(heap->isolate()), heap_(heap), evacuator_(evacuator) { | 
| -    evacuator->set_task_id(id()); | 
| -  } | 
| - | 
| -  virtual ~CompactionTask() {} | 
| - | 
| - private: | 
| -  // v8::internal::CancelableTask overrides. | 
| -  void RunInternal() override { | 
| -    evacuator_->EvacuatePages(); | 
| -    heap_->mark_compact_collector() | 
| -        ->pending_compaction_tasks_semaphore_.Signal(); | 
| -  } | 
| - | 
| -  Heap* heap_; | 
| -  Evacuator* evacuator_; | 
| - | 
| -  DISALLOW_COPY_AND_ASSIGN(CompactionTask); | 
| -}; | 
| - | 
| int MarkCompactCollector::NumberOfParallelCompactionTasks(int pages, | 
| intptr_t live_bytes) { | 
| if (!FLAG_parallel_compaction) return 1; | 
| @@ -3158,19 +3097,63 @@ int MarkCompactCollector::NumberOfParallelCompactionTasks(int pages, | 
| return Min(available_cores, tasks_capped_pages); | 
| } | 
|  | 
| +class EvacuationJobTraits { | 
| + public: | 
| +  typedef int* PerPageData;  // Pointer to number of aborted pages. | 
| +  typedef MarkCompactCollector::Evacuator* PerTaskData; | 
| + | 
| +  static const bool NeedSequentialFinalization = true; | 
| + | 
| +  static bool ProcessPageInParallel(Heap* heap, PerTaskData evacuator, | 
| +                                    MemoryChunk* chunk, PerPageData) { | 
| +    return evacuator->EvacuatePage(chunk); | 
| +  } | 
| + | 
| +  static void FinalizePageSequentially(Heap*, MemoryChunk* chunk, bool success, | 
| +                                       PerPageData data) { | 
| +    if (chunk->InNewSpace()) { | 
| +      DCHECK(success); | 
| +    } else { | 
| +      Page* p = static_cast<Page*>(chunk); | 
| +      if (success) { | 
| +        DCHECK(p->IsEvacuationCandidate()); | 
| +        DCHECK(p->SweepingDone()); | 
| +        p->Unlink(); | 
| +      } else { | 
| +        // We have partially compacted the page, i.e., some objects may have | 
| +        // moved, others are still in place. | 
| +        // We need to: | 
| +        // - Leave the evacuation candidate flag for later processing of slots | 
| +        //   buffer entries. | 
| +        // - Leave the slots buffer there for processing of entries added by | 
| +        //   the write barrier. | 
| +        // - Rescan the page as slot recording in the migration buffer only | 
| +        //   happens upon moving (which we potentially didn't do). | 
| +        // - Leave the page in the list of pages of a space since we could not | 
| +        //   fully evacuate it. | 
| +        DCHECK(p->IsEvacuationCandidate()); | 
| +        p->SetFlag(Page::COMPACTION_WAS_ABORTED); | 
| +        *data += 1; | 
| +      } | 
| +    } | 
| +  } | 
| +}; | 
|  | 
| void MarkCompactCollector::EvacuatePagesInParallel() { | 
| -  int num_pages = 0; | 
| +  PageParallelJob<EvacuationJobTraits> job( | 
| +      heap_, heap_->isolate()->cancelable_task_manager()); | 
| + | 
| +  int abandoned_pages = 0; | 
| intptr_t live_bytes = 0; | 
| for (Page* page : evacuation_candidates_) { | 
| -    num_pages++; | 
| live_bytes += page->LiveBytes(); | 
| +    job.AddPage(page, &abandoned_pages); | 
| } | 
| for (NewSpacePage* page : newspace_evacuation_candidates_) { | 
| -    num_pages++; | 
| live_bytes += page->LiveBytes(); | 
| +    job.AddPage(page, &abandoned_pages); | 
| } | 
| -  DCHECK_GE(num_pages, 1); | 
| +  DCHECK_GE(job.NumberOfPages(), 1); | 
|  | 
| // Used for trace summary. | 
| intptr_t compaction_speed = 0; | 
| @@ -3178,113 +3161,32 @@ void MarkCompactCollector::EvacuatePagesInParallel() { | 
| compaction_speed = heap()->tracer()->CompactionSpeedInBytesPerMillisecond(); | 
| } | 
|  | 
| -  const int num_tasks = NumberOfParallelCompactionTasks(num_pages, live_bytes); | 
| - | 
| -  // Set up compaction spaces. | 
| -  Evacuator** evacuators = new Evacuator*[num_tasks]; | 
| -  for (int i = 0; i < num_tasks; i++) { | 
| -    evacuators[i] = new Evacuator(this, evacuation_candidates_, | 
| -                                  newspace_evacuation_candidates_); | 
| +  const int wanted_num_tasks = | 
| +      NumberOfParallelCompactionTasks(job.NumberOfPages(), live_bytes); | 
| +  Evacuator** evacuators = new Evacuator*[wanted_num_tasks]; | 
| +  for (int i = 0; i < wanted_num_tasks; i++) { | 
| +    evacuators[i] = new Evacuator(this); | 
| } | 
| - | 
| -  // Kick off parallel tasks. | 
| -  StartParallelCompaction(evacuators, num_tasks); | 
| -  // Wait for unfinished and not-yet-started tasks. | 
| -  WaitUntilCompactionCompleted(&evacuators[1], num_tasks - 1); | 
| - | 
| -  // Finalize local evacuators by merging back all locally cached data. | 
| -  for (int i = 0; i < num_tasks; i++) { | 
| +  job.Run(wanted_num_tasks, [evacuators](int i) { return evacuators[i]; }); | 
| +  for (int i = 0; i < wanted_num_tasks; i++) { | 
| evacuators[i]->Finalize(); | 
| delete evacuators[i]; | 
| } | 
| delete[] evacuators; | 
|  | 
| -  // Finalize pages sequentially. | 
| -  for (NewSpacePage* p : newspace_evacuation_candidates_) { | 
| -    DCHECK_EQ(p->parallel_compaction_state().Value(), | 
| -              MemoryChunk::kCompactingFinalize); | 
| -    p->parallel_compaction_state().SetValue(MemoryChunk::kCompactingDone); | 
| -  } | 
| - | 
| -  int abandoned_pages = 0; | 
| -  for (Page* p : evacuation_candidates_) { | 
| -    switch (p->parallel_compaction_state().Value()) { | 
| -      case MemoryChunk::ParallelCompactingState::kCompactingAborted: | 
| -        // We have partially compacted the page, i.e., some objects may have | 
| -        // moved, others are still in place. | 
| -        // We need to: | 
| -        // - Leave the evacuation candidate flag for later processing of | 
| -        //   slots buffer entries. | 
| -        // - Leave the slots buffer there for processing of entries added by | 
| -        //   the write barrier. | 
| -        // - Rescan the page as slot recording in the migration buffer only | 
| -        //   happens upon moving (which we potentially didn't do). | 
| -        // - Leave the page in the list of pages of a space since we could not | 
| -        //   fully evacuate it. | 
| -        // - Mark them for rescanning for store buffer entries as we otherwise | 
| -        //   might have stale store buffer entries that become "valid" again | 
| -        //   after reusing the memory. Note that all existing store buffer | 
| -        //   entries of such pages are filtered before rescanning. | 
| -        DCHECK(p->IsEvacuationCandidate()); | 
| -        p->SetFlag(Page::COMPACTION_WAS_ABORTED); | 
| -        abandoned_pages++; | 
| -        break; | 
| -      case MemoryChunk::kCompactingFinalize: | 
| -        DCHECK(p->IsEvacuationCandidate()); | 
| -        DCHECK(p->SweepingDone()); | 
| -        p->Unlink(); | 
| -        break; | 
| -      case MemoryChunk::kCompactingDone: | 
| -        DCHECK(p->IsFlagSet(Page::POPULAR_PAGE)); | 
| -        DCHECK(p->IsFlagSet(Page::RESCAN_ON_EVACUATION)); | 
| -        break; | 
| -      default: | 
| -        // MemoryChunk::kCompactingInProgress. | 
| -        UNREACHABLE(); | 
| -    } | 
| -    p->parallel_compaction_state().SetValue(MemoryChunk::kCompactingDone); | 
| -  } | 
| if (FLAG_trace_fragmentation) { | 
| PrintIsolate(isolate(), | 
| "%8.0f ms: compaction: parallel=%d pages=%d aborted=%d " | 
| -                 "tasks=%d cores=%d live_bytes=%" V8_PTR_PREFIX | 
| +                 "wanted_tasks=%d tasks=%d cores=%d live_bytes=%" V8_PTR_PREFIX | 
| "d compaction_speed=%" V8_PTR_PREFIX "d\n", | 
| isolate()->time_millis_since_init(), FLAG_parallel_compaction, | 
| -                 num_pages, abandoned_pages, num_tasks, | 
| -                 base::SysInfo::NumberOfProcessors(), live_bytes, | 
| -                 compaction_speed); | 
| -  } | 
| -} | 
| - | 
| -void MarkCompactCollector::StartParallelCompaction(Evacuator** evacuators, | 
| -                                                   int len) { | 
| -  compaction_in_progress_ = true; | 
| -  for (int i = 1; i < len; i++) { | 
| -    CompactionTask* task = new CompactionTask(heap(), evacuators[i]); | 
| -    V8::GetCurrentPlatform()->CallOnBackgroundThread( | 
| -        task, v8::Platform::kShortRunningTask); | 
| +                 job.NumberOfPages(), abandoned_pages, wanted_num_tasks, | 
| +                 job.NumberOfTasks(), | 
| +                 V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads(), | 
| +                 live_bytes, compaction_speed); | 
| } | 
| - | 
| -  // Contribute on main thread. | 
| -  evacuators[0]->EvacuatePages(); | 
| } | 
|  | 
| -void MarkCompactCollector::WaitUntilCompactionCompleted(Evacuator** evacuators, | 
| -                                                        int len) { | 
| -  // Try to cancel compaction tasks that have not been run (as they might be | 
| -  // stuck in a worker queue). Tasks that cannot be canceled, have either | 
| -  // already completed or are still running, hence we need to wait for their | 
| -  // semaphore signal. | 
| -  for (int i = 0; i < len; i++) { | 
| -    if (!heap()->isolate()->cancelable_task_manager()->TryAbort( | 
| -            evacuators[i]->task_id())) { | 
| -      pending_compaction_tasks_semaphore_.Wait(); | 
| -    } | 
| -  } | 
| -  compaction_in_progress_ = false; | 
| -} | 
| - | 
| - | 
| class EvacuationWeakObjectRetainer : public WeakObjectRetainer { | 
| public: | 
| virtual Object* RetainAs(Object* object) { | 
|  |