Home | History | Annotate | Download | only in heap
      1 // Copyright 2017 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/sweeper.h"
      6 
      7 #include "src/base/template-utils.h"
      8 #include "src/heap/array-buffer-tracker-inl.h"
      9 #include "src/heap/gc-tracer.h"
     10 #include "src/heap/mark-compact-inl.h"
     11 #include "src/heap/remembered-set.h"
     12 #include "src/objects-inl.h"
     13 #include "src/vm-state-inl.h"
     14 
     15 namespace v8 {
     16 namespace internal {
     17 
     18 Sweeper::PauseOrCompleteScope::PauseOrCompleteScope(Sweeper* sweeper)
     19     : sweeper_(sweeper) {
     20   sweeper_->stop_sweeper_tasks_ = true;
     21   if (!sweeper_->sweeping_in_progress()) return;
     22 
     23   sweeper_->AbortAndWaitForTasks();
     24 
     25   // Complete sweeping if there's nothing more to do.
     26   if (sweeper_->IsDoneSweeping()) {
     27     sweeper_->heap_->mark_compact_collector()->EnsureSweepingCompleted();
     28     DCHECK(!sweeper_->sweeping_in_progress());
     29   } else {
     30     // Unless sweeping is complete the flag still indicates that the sweeper
     31     // is enabled. It just cannot use tasks anymore.
     32     DCHECK(sweeper_->sweeping_in_progress());
     33   }
     34 }
     35 
     36 Sweeper::PauseOrCompleteScope::~PauseOrCompleteScope() {
     37   sweeper_->stop_sweeper_tasks_ = false;
     38   if (!sweeper_->sweeping_in_progress()) return;
     39 
     40   sweeper_->StartSweeperTasks();
     41 }
     42 
     43 Sweeper::FilterSweepingPagesScope::FilterSweepingPagesScope(
     44     Sweeper* sweeper, const PauseOrCompleteScope& pause_or_complete_scope)
     45     : sweeper_(sweeper),
     46       pause_or_complete_scope_(pause_or_complete_scope),
     47       sweeping_in_progress_(sweeper_->sweeping_in_progress()) {
     48   USE(pause_or_complete_scope_);
     49   if (!sweeping_in_progress_) return;
     50 
     51   int old_space_index = GetSweepSpaceIndex(OLD_SPACE);
     52   old_space_sweeping_list_ =
     53       std::move(sweeper_->sweeping_list_[old_space_index]);
     54   sweeper_->sweeping_list_[old_space_index].clear();
     55 }
     56 
     57 Sweeper::FilterSweepingPagesScope::~FilterSweepingPagesScope() {
     58   DCHECK_EQ(sweeping_in_progress_, sweeper_->sweeping_in_progress());
     59   if (!sweeping_in_progress_) return;
     60 
     61   sweeper_->sweeping_list_[GetSweepSpaceIndex(OLD_SPACE)] =
     62       std::move(old_space_sweeping_list_);
     63   // old_space_sweeping_list_ does not need to be cleared as we don't use it.
     64 }
     65 
     66 class Sweeper::SweeperTask final : public CancelableTask {
     67  public:
     68   SweeperTask(Isolate* isolate, Sweeper* sweeper,
     69               base::Semaphore* pending_sweeper_tasks,
     70               std::atomic<intptr_t>* num_sweeping_tasks,
     71               AllocationSpace space_to_start)
     72       : CancelableTask(isolate),
     73         sweeper_(sweeper),
     74         pending_sweeper_tasks_(pending_sweeper_tasks),
     75         num_sweeping_tasks_(num_sweeping_tasks),
     76         space_to_start_(space_to_start),
     77         tracer_(isolate->heap()->tracer()) {}
     78 
     79   virtual ~SweeperTask() {}
     80 
     81  private:
     82   void RunInternal() final {
     83     TRACE_BACKGROUND_GC(tracer_,
     84                         GCTracer::BackgroundScope::MC_BACKGROUND_SWEEPING);
     85     DCHECK(IsValidSweepingSpace(space_to_start_));
     86     const int offset = space_to_start_ - FIRST_GROWABLE_PAGED_SPACE;
     87     for (int i = 0; i < kNumberOfSweepingSpaces; i++) {
     88       const AllocationSpace space_id = static_cast<AllocationSpace>(
     89           FIRST_GROWABLE_PAGED_SPACE +
     90           ((i + offset) % kNumberOfSweepingSpaces));
     91       // Do not sweep code space concurrently.
     92       if (space_id == CODE_SPACE) continue;
     93       DCHECK(IsValidSweepingSpace(space_id));
     94       sweeper_->SweepSpaceFromTask(space_id);
     95     }
     96     (*num_sweeping_tasks_)--;
     97     pending_sweeper_tasks_->Signal();
     98   }
     99 
    100   Sweeper* const sweeper_;
    101   base::Semaphore* const pending_sweeper_tasks_;
    102   std::atomic<intptr_t>* const num_sweeping_tasks_;
    103   AllocationSpace space_to_start_;
    104   GCTracer* const tracer_;
    105 
    106   DISALLOW_COPY_AND_ASSIGN(SweeperTask);
    107 };
    108 
    109 class Sweeper::IncrementalSweeperTask final : public CancelableTask {
    110  public:
    111   IncrementalSweeperTask(Isolate* isolate, Sweeper* sweeper)
    112       : CancelableTask(isolate), isolate_(isolate), sweeper_(sweeper) {}
    113 
    114   virtual ~IncrementalSweeperTask() {}
    115 
    116  private:
    117   void RunInternal() final {
    118     VMState<GC> state(isolate_);
    119     TRACE_EVENT_CALL_STATS_SCOPED(isolate_, "v8", "V8.Task");
    120 
    121     sweeper_->incremental_sweeper_pending_ = false;
    122 
    123     if (sweeper_->sweeping_in_progress()) {
    124       if (!sweeper_->SweepSpaceIncrementallyFromTask(CODE_SPACE)) {
    125         sweeper_->ScheduleIncrementalSweepingTask();
    126       }
    127     }
    128   }
    129 
    130   Isolate* const isolate_;
    131   Sweeper* const sweeper_;
    132   DISALLOW_COPY_AND_ASSIGN(IncrementalSweeperTask);
    133 };
    134 
    135 void Sweeper::StartSweeping() {
    136   CHECK(!stop_sweeper_tasks_);
    137   sweeping_in_progress_ = true;
    138   iterability_in_progress_ = true;
    139   MajorNonAtomicMarkingState* marking_state =
    140       heap_->mark_compact_collector()->non_atomic_marking_state();
    141   ForAllSweepingSpaces([this, marking_state](AllocationSpace space) {
    142     int space_index = GetSweepSpaceIndex(space);
    143     std::sort(sweeping_list_[space_index].begin(),
    144               sweeping_list_[space_index].end(),
    145               [marking_state](Page* a, Page* b) {
    146                 return marking_state->live_bytes(a) <
    147                        marking_state->live_bytes(b);
    148               });
    149   });
    150 }
    151 
    152 void Sweeper::StartSweeperTasks() {
    153   DCHECK_EQ(0, num_tasks_);
    154   DCHECK_EQ(0, num_sweeping_tasks_);
    155   if (FLAG_concurrent_sweeping && sweeping_in_progress_ &&
    156       !heap_->delay_sweeper_tasks_for_testing_) {
    157     ForAllSweepingSpaces([this](AllocationSpace space) {
    158       DCHECK(IsValidSweepingSpace(space));
    159       num_sweeping_tasks_++;
    160       auto task = base::make_unique<SweeperTask>(
    161           heap_->isolate(), this, &pending_sweeper_tasks_semaphore_,
    162           &num_sweeping_tasks_, space);
    163       DCHECK_LT(num_tasks_, kMaxSweeperTasks);
    164       task_ids_[num_tasks_++] = task->id();
    165       V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
    166     });
    167     ScheduleIncrementalSweepingTask();
    168   }
    169 }
    170 
    171 void Sweeper::SweepOrWaitUntilSweepingCompleted(Page* page) {
    172   if (!page->SweepingDone()) {
    173     ParallelSweepPage(page, page->owner()->identity());
    174     if (!page->SweepingDone()) {
    175       // We were not able to sweep that page, i.e., a concurrent
    176       // sweeper thread currently owns this page. Wait for the sweeper
    177       // thread to be done with this page.
    178       page->WaitUntilSweepingCompleted();
    179     }
    180   }
    181 }
    182 
    183 Page* Sweeper::GetSweptPageSafe(PagedSpace* space) {
    184   base::LockGuard<base::Mutex> guard(&mutex_);
    185   SweptList& list = swept_list_[GetSweepSpaceIndex(space->identity())];
    186   if (!list.empty()) {
    187     auto last_page = list.back();
    188     list.pop_back();
    189     return last_page;
    190   }
    191   return nullptr;
    192 }
    193 
    194 void Sweeper::AbortAndWaitForTasks() {
    195   if (!FLAG_concurrent_sweeping) return;
    196 
    197   for (int i = 0; i < num_tasks_; i++) {
    198     if (heap_->isolate()->cancelable_task_manager()->TryAbort(task_ids_[i]) !=
    199         CancelableTaskManager::kTaskAborted) {
    200       pending_sweeper_tasks_semaphore_.Wait();
    201     } else {
    202       // Aborted case.
    203       num_sweeping_tasks_--;
    204     }
    205   }
    206   num_tasks_ = 0;
    207   DCHECK_EQ(0, num_sweeping_tasks_);
    208 }
    209 
    210 void Sweeper::EnsureCompleted() {
    211   if (!sweeping_in_progress_) return;
    212 
    213   EnsureIterabilityCompleted();
    214 
    215   // If sweeping is not completed or not running at all, we try to complete it
    216   // here.
    217   ForAllSweepingSpaces(
    218       [this](AllocationSpace space) { ParallelSweepSpace(space, 0); });
    219 
    220   AbortAndWaitForTasks();
    221 
    222   ForAllSweepingSpaces([this](AllocationSpace space) {
    223     CHECK(sweeping_list_[GetSweepSpaceIndex(space)].empty());
    224   });
    225   sweeping_in_progress_ = false;
    226 }
    227 
    228 bool Sweeper::AreSweeperTasksRunning() { return num_sweeping_tasks_ != 0; }
    229 
    230 int Sweeper::RawSweep(Page* p, FreeListRebuildingMode free_list_mode,
    231                       FreeSpaceTreatmentMode free_space_mode) {
    232   Space* space = p->owner();
    233   DCHECK_NOT_NULL(space);
    234   DCHECK(free_list_mode == IGNORE_FREE_LIST || space->identity() == OLD_SPACE ||
    235          space->identity() == CODE_SPACE || space->identity() == MAP_SPACE);
    236   DCHECK(!p->IsEvacuationCandidate() && !p->SweepingDone());
    237 
    238   // TODO(ulan): we don't have to clear type old-to-old slots in code space
    239   // because the concurrent marker doesn't mark code objects. This requires
    240   // the write barrier for code objects to check the color of the code object.
    241   bool non_empty_typed_slots = p->typed_slot_set<OLD_TO_NEW>() != nullptr ||
    242                                p->typed_slot_set<OLD_TO_OLD>() != nullptr;
    243 
    244   // The free ranges map is used for filtering typed slots.
    245   std::map<uint32_t, uint32_t> free_ranges;
    246 
    247   // Before we sweep objects on the page, we free dead array buffers which
    248   // requires valid mark bits.
    249   ArrayBufferTracker::FreeDead(p, marking_state_);
    250 
    251   Address free_start = p->area_start();
    252   DCHECK_EQ(0, free_start % (32 * kPointerSize));
    253 
    254   // If we use the skip list for code space pages, we have to lock the skip
    255   // list because it could be accessed concurrently by the runtime or the
    256   // deoptimizer.
    257   const bool rebuild_skip_list =
    258       space->identity() == CODE_SPACE && p->skip_list() != nullptr;
    259   SkipList* skip_list = p->skip_list();
    260   if (rebuild_skip_list) {
    261     skip_list->Clear();
    262   }
    263 
    264   intptr_t live_bytes = 0;
    265   intptr_t freed_bytes = 0;
    266   intptr_t max_freed_bytes = 0;
    267   int curr_region = -1;
    268 
    269   // Set the allocated_bytes counter to area_size. The free operations below
    270   // will decrease the counter to actual live bytes.
    271   p->ResetAllocatedBytes();
    272 
    273   for (auto object_and_size :
    274        LiveObjectRange<kBlackObjects>(p, marking_state_->bitmap(p))) {
    275     HeapObject* const object = object_and_size.first;
    276     DCHECK(marking_state_->IsBlack(object));
    277     Address free_end = object->address();
    278     if (free_end != free_start) {
    279       CHECK_GT(free_end, free_start);
    280       size_t size = static_cast<size_t>(free_end - free_start);
    281       if (free_space_mode == ZAP_FREE_SPACE) {
    282         ZapCode(free_start, size);
    283       }
    284       if (free_list_mode == REBUILD_FREE_LIST) {
    285         freed_bytes = reinterpret_cast<PagedSpace*>(space)->Free(
    286             free_start, size, SpaceAccountingMode::kSpaceUnaccounted);
    287         max_freed_bytes = Max(freed_bytes, max_freed_bytes);
    288       } else {
    289         p->heap()->CreateFillerObjectAt(
    290             free_start, static_cast<int>(size), ClearRecordedSlots::kNo,
    291             ClearFreedMemoryMode::kClearFreedMemory);
    292       }
    293       RememberedSet<OLD_TO_NEW>::RemoveRange(p, free_start, free_end,
    294                                              SlotSet::KEEP_EMPTY_BUCKETS);
    295       RememberedSet<OLD_TO_OLD>::RemoveRange(p, free_start, free_end,
    296                                              SlotSet::KEEP_EMPTY_BUCKETS);
    297       if (non_empty_typed_slots) {
    298         free_ranges.insert(std::pair<uint32_t, uint32_t>(
    299             static_cast<uint32_t>(free_start - p->address()),
    300             static_cast<uint32_t>(free_end - p->address())));
    301       }
    302     }
    303     Map* map = object->synchronized_map();
    304     int size = object->SizeFromMap(map);
    305     live_bytes += size;
    306     if (rebuild_skip_list) {
    307       int new_region_start = SkipList::RegionNumber(free_end);
    308       int new_region_end =
    309           SkipList::RegionNumber(free_end + size - kPointerSize);
    310       if (new_region_start != curr_region || new_region_end != curr_region) {
    311         skip_list->AddObject(free_end, size);
    312         curr_region = new_region_end;
    313       }
    314     }
    315     free_start = free_end + size;
    316   }
    317 
    318   if (free_start != p->area_end()) {
    319     CHECK_GT(p->area_end(), free_start);
    320     size_t size = static_cast<size_t>(p->area_end() - free_start);
    321     if (free_space_mode == ZAP_FREE_SPACE) {
    322       ZapCode(free_start, size);
    323     }
    324     if (free_list_mode == REBUILD_FREE_LIST) {
    325       freed_bytes = reinterpret_cast<PagedSpace*>(space)->Free(
    326           free_start, size, SpaceAccountingMode::kSpaceUnaccounted);
    327       max_freed_bytes = Max(freed_bytes, max_freed_bytes);
    328     } else {
    329       p->heap()->CreateFillerObjectAt(free_start, static_cast<int>(size),
    330                                       ClearRecordedSlots::kNo,
    331                                       ClearFreedMemoryMode::kClearFreedMemory);
    332     }
    333 
    334     RememberedSet<OLD_TO_NEW>::RemoveRange(p, free_start, p->area_end(),
    335                                            SlotSet::KEEP_EMPTY_BUCKETS);
    336     RememberedSet<OLD_TO_OLD>::RemoveRange(p, free_start, p->area_end(),
    337                                            SlotSet::KEEP_EMPTY_BUCKETS);
    338     if (non_empty_typed_slots) {
    339       free_ranges.insert(std::pair<uint32_t, uint32_t>(
    340           static_cast<uint32_t>(free_start - p->address()),
    341           static_cast<uint32_t>(p->area_end() - p->address())));
    342     }
    343   }
    344 
    345   // Clear invalid typed slots after collection all free ranges.
    346   if (!free_ranges.empty()) {
    347     TypedSlotSet* old_to_new = p->typed_slot_set<OLD_TO_NEW>();
    348     if (old_to_new != nullptr) {
    349       old_to_new->RemoveInvaldSlots(free_ranges);
    350     }
    351     TypedSlotSet* old_to_old = p->typed_slot_set<OLD_TO_OLD>();
    352     if (old_to_old != nullptr) {
    353       old_to_old->RemoveInvaldSlots(free_ranges);
    354     }
    355   }
    356 
    357   marking_state_->bitmap(p)->Clear();
    358   if (free_list_mode == IGNORE_FREE_LIST) {
    359     marking_state_->SetLiveBytes(p, 0);
    360     // We did not free memory, so have to adjust allocated bytes here.
    361     intptr_t freed_bytes = p->area_size() - live_bytes;
    362     p->DecreaseAllocatedBytes(freed_bytes);
    363   } else {
    364     // Keep the old live bytes counter of the page until RefillFreeList, where
    365     // the space size is refined.
    366     // The allocated_bytes() counter is precisely the total size of objects.
    367     DCHECK_EQ(live_bytes, p->allocated_bytes());
    368   }
    369   p->set_concurrent_sweeping_state(Page::kSweepingDone);
    370   if (free_list_mode == IGNORE_FREE_LIST) return 0;
    371   return static_cast<int>(FreeList::GuaranteedAllocatable(max_freed_bytes));
    372 }
    373 
    374 void Sweeper::SweepSpaceFromTask(AllocationSpace identity) {
    375   Page* page = nullptr;
    376   while (!stop_sweeper_tasks_ &&
    377          ((page = GetSweepingPageSafe(identity)) != nullptr)) {
    378     ParallelSweepPage(page, identity);
    379   }
    380 }
    381 
    382 bool Sweeper::SweepSpaceIncrementallyFromTask(AllocationSpace identity) {
    383   if (Page* page = GetSweepingPageSafe(identity)) {
    384     ParallelSweepPage(page, identity);
    385   }
    386   return sweeping_list_[GetSweepSpaceIndex(identity)].empty();
    387 }
    388 
    389 int Sweeper::ParallelSweepSpace(AllocationSpace identity,
    390                                 int required_freed_bytes, int max_pages) {
    391   int max_freed = 0;
    392   int pages_freed = 0;
    393   Page* page = nullptr;
    394   while ((page = GetSweepingPageSafe(identity)) != nullptr) {
    395     int freed = ParallelSweepPage(page, identity);
    396     pages_freed += 1;
    397     DCHECK_GE(freed, 0);
    398     max_freed = Max(max_freed, freed);
    399     if ((required_freed_bytes) > 0 && (max_freed >= required_freed_bytes))
    400       return max_freed;
    401     if ((max_pages > 0) && (pages_freed >= max_pages)) return max_freed;
    402   }
    403   return max_freed;
    404 }
    405 
    406 int Sweeper::ParallelSweepPage(Page* page, AllocationSpace identity) {
    407   // Early bailout for pages that are swept outside of the regular sweeping
    408   // path. This check here avoids taking the lock first, avoiding deadlocks.
    409   if (page->SweepingDone()) return 0;
    410 
    411   DCHECK(IsValidSweepingSpace(identity));
    412   int max_freed = 0;
    413   {
    414     base::LockGuard<base::Mutex> guard(page->mutex());
    415     // If this page was already swept in the meantime, we can return here.
    416     if (page->SweepingDone()) return 0;
    417 
    418     // If the page is a code page, the CodePageMemoryModificationScope changes
    419     // the page protection mode from rx -> rw while sweeping.
    420     CodePageMemoryModificationScope code_page_scope(page);
    421 
    422     DCHECK_EQ(Page::kSweepingPending, page->concurrent_sweeping_state());
    423     page->set_concurrent_sweeping_state(Page::kSweepingInProgress);
    424     const FreeSpaceTreatmentMode free_space_mode =
    425         Heap::ShouldZapGarbage() ? ZAP_FREE_SPACE : IGNORE_FREE_SPACE;
    426     max_freed = RawSweep(page, REBUILD_FREE_LIST, free_space_mode);
    427     DCHECK(page->SweepingDone());
    428 
    429     // After finishing sweeping of a page we clean up its remembered set.
    430     TypedSlotSet* typed_slot_set = page->typed_slot_set<OLD_TO_NEW>();
    431     if (typed_slot_set) {
    432       typed_slot_set->FreeToBeFreedChunks();
    433     }
    434     SlotSet* slot_set = page->slot_set<OLD_TO_NEW>();
    435     if (slot_set) {
    436       slot_set->FreeToBeFreedBuckets();
    437     }
    438   }
    439 
    440   {
    441     base::LockGuard<base::Mutex> guard(&mutex_);
    442     swept_list_[GetSweepSpaceIndex(identity)].push_back(page);
    443   }
    444   return max_freed;
    445 }
    446 
    447 void Sweeper::ScheduleIncrementalSweepingTask() {
    448   if (!incremental_sweeper_pending_) {
    449     incremental_sweeper_pending_ = true;
    450     IncrementalSweeperTask* task =
    451         new IncrementalSweeperTask(heap_->isolate(), this);
    452     v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(heap_->isolate());
    453     V8::GetCurrentPlatform()->CallOnForegroundThread(isolate, task);
    454   }
    455 }
    456 
    457 void Sweeper::AddPage(AllocationSpace space, Page* page,
    458                       Sweeper::AddPageMode mode) {
    459   base::LockGuard<base::Mutex> guard(&mutex_);
    460   DCHECK(IsValidSweepingSpace(space));
    461   DCHECK(!FLAG_concurrent_sweeping || !AreSweeperTasksRunning());
    462   if (mode == Sweeper::REGULAR) {
    463     PrepareToBeSweptPage(space, page);
    464   } else {
    465     // Page has been temporarily removed from the sweeper. Accounting already
    466     // happened when the page was initially added, so it is skipped here.
    467     DCHECK_EQ(Sweeper::READD_TEMPORARY_REMOVED_PAGE, mode);
    468   }
    469   DCHECK_EQ(Page::kSweepingPending, page->concurrent_sweeping_state());
    470   sweeping_list_[GetSweepSpaceIndex(space)].push_back(page);
    471 }
    472 
    473 void Sweeper::PrepareToBeSweptPage(AllocationSpace space, Page* page) {
    474   DCHECK_GE(page->area_size(),
    475             static_cast<size_t>(marking_state_->live_bytes(page)));
    476   DCHECK_EQ(Page::kSweepingDone, page->concurrent_sweeping_state());
    477   page->ForAllFreeListCategories(
    478       [](FreeListCategory* category) { DCHECK(!category->is_linked()); });
    479   page->set_concurrent_sweeping_state(Page::kSweepingPending);
    480   heap_->paged_space(space)->IncreaseAllocatedBytes(
    481       marking_state_->live_bytes(page), page);
    482 }
    483 
    484 Page* Sweeper::GetSweepingPageSafe(AllocationSpace space) {
    485   base::LockGuard<base::Mutex> guard(&mutex_);
    486   DCHECK(IsValidSweepingSpace(space));
    487   int space_index = GetSweepSpaceIndex(space);
    488   Page* page = nullptr;
    489   if (!sweeping_list_[space_index].empty()) {
    490     page = sweeping_list_[space_index].front();
    491     sweeping_list_[space_index].pop_front();
    492   }
    493   return page;
    494 }
    495 
    496 void Sweeper::EnsurePageIsIterable(Page* page) {
    497   AllocationSpace space = page->owner()->identity();
    498   if (IsValidSweepingSpace(space)) {
    499     SweepOrWaitUntilSweepingCompleted(page);
    500   } else {
    501     DCHECK(IsValidIterabilitySpace(space));
    502     EnsureIterabilityCompleted();
    503   }
    504 }
    505 
    506 void Sweeper::EnsureIterabilityCompleted() {
    507   if (!iterability_in_progress_) return;
    508 
    509   if (FLAG_concurrent_sweeping && iterability_task_started_) {
    510     if (heap_->isolate()->cancelable_task_manager()->TryAbort(
    511             iterability_task_id_) != CancelableTaskManager::kTaskAborted) {
    512       iterability_task_semaphore_.Wait();
    513     }
    514     iterability_task_started_ = false;
    515   }
    516 
    517   for (Page* page : iterability_list_) {
    518     MakeIterable(page);
    519   }
    520   iterability_list_.clear();
    521   iterability_in_progress_ = false;
    522 }
    523 
    524 class Sweeper::IterabilityTask final : public CancelableTask {
    525  public:
    526   IterabilityTask(Isolate* isolate, Sweeper* sweeper,
    527                   base::Semaphore* pending_iterability_task)
    528       : CancelableTask(isolate),
    529         sweeper_(sweeper),
    530         pending_iterability_task_(pending_iterability_task),
    531         tracer_(isolate->heap()->tracer()) {}
    532 
    533   virtual ~IterabilityTask() {}
    534 
    535  private:
    536   void RunInternal() final {
    537     TRACE_BACKGROUND_GC(tracer_,
    538                         GCTracer::BackgroundScope::MC_BACKGROUND_SWEEPING);
    539     for (Page* page : sweeper_->iterability_list_) {
    540       sweeper_->MakeIterable(page);
    541     }
    542     sweeper_->iterability_list_.clear();
    543     pending_iterability_task_->Signal();
    544   }
    545 
    546   Sweeper* const sweeper_;
    547   base::Semaphore* const pending_iterability_task_;
    548   GCTracer* const tracer_;
    549 
    550   DISALLOW_COPY_AND_ASSIGN(IterabilityTask);
    551 };
    552 
    553 void Sweeper::StartIterabilityTasks() {
    554   if (!iterability_in_progress_) return;
    555 
    556   DCHECK(!iterability_task_started_);
    557   if (FLAG_concurrent_sweeping && !iterability_list_.empty()) {
    558     auto task = base::make_unique<IterabilityTask>(
    559         heap_->isolate(), this, &iterability_task_semaphore_);
    560     iterability_task_id_ = task->id();
    561     iterability_task_started_ = true;
    562     V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
    563   }
    564 }
    565 
    566 void Sweeper::AddPageForIterability(Page* page) {
    567   DCHECK(sweeping_in_progress_);
    568   DCHECK(iterability_in_progress_);
    569   DCHECK(!iterability_task_started_);
    570   DCHECK(IsValidIterabilitySpace(page->owner()->identity()));
    571   DCHECK_EQ(Page::kSweepingDone, page->concurrent_sweeping_state());
    572 
    573   iterability_list_.push_back(page);
    574   page->set_concurrent_sweeping_state(Page::kSweepingPending);
    575 }
    576 
    577 void Sweeper::MakeIterable(Page* page) {
    578   DCHECK(IsValidIterabilitySpace(page->owner()->identity()));
    579   const FreeSpaceTreatmentMode free_space_mode =
    580       Heap::ShouldZapGarbage() ? ZAP_FREE_SPACE : IGNORE_FREE_SPACE;
    581   RawSweep(page, IGNORE_FREE_LIST, free_space_mode);
    582 }
    583 
    584 }  // namespace internal
    585 }  // namespace v8
    586