blob: cdb1058edb0b40fbdfe0e7ddb03ab8debb7c581f [file] [log] [blame]
// 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/sweeper.h"
#include "src/execution/vm-state-inl.h"
#include "src/heap/code-object-registry.h"
#include "src/heap/free-list-inl.h"
#include "src/heap/gc-tracer.h"
#include "src/heap/invalidated-slots-inl.h"
#include "src/heap/mark-compact-inl.h"
#include "src/heap/remembered-set.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
Sweeper::Sweeper(Heap* heap, MajorNonAtomicMarkingState* marking_state)
: heap_(heap),
marking_state_(marking_state),
num_tasks_(0),
pending_sweeper_tasks_semaphore_(0),
incremental_sweeper_pending_(false),
sweeping_in_progress_(false),
num_sweeping_tasks_(0),
stop_sweeper_tasks_(false),
iterability_task_semaphore_(0),
iterability_in_progress_(false),
iterability_task_started_(false),
should_reduce_memory_(false) {}
Sweeper::PauseOrCompleteScope::PauseOrCompleteScope(Sweeper* sweeper)
: sweeper_(sweeper) {
sweeper_->stop_sweeper_tasks_ = true;
if (!sweeper_->sweeping_in_progress()) return;
sweeper_->AbortAndWaitForTasks();
// Complete sweeping if there's nothing more to do.
if (sweeper_->IsDoneSweeping()) {
sweeper_->heap_->mark_compact_collector()->EnsureSweepingCompleted();
DCHECK(!sweeper_->sweeping_in_progress());
} else {
// Unless sweeping is complete the flag still indicates that the sweeper
// is enabled. It just cannot use tasks anymore.
DCHECK(sweeper_->sweeping_in_progress());
}
}
Sweeper::PauseOrCompleteScope::~PauseOrCompleteScope() {
sweeper_->stop_sweeper_tasks_ = false;
if (!sweeper_->sweeping_in_progress()) return;
sweeper_->StartSweeperTasks();
}
Sweeper::FilterSweepingPagesScope::FilterSweepingPagesScope(
Sweeper* sweeper, const PauseOrCompleteScope& pause_or_complete_scope)
: sweeper_(sweeper),
pause_or_complete_scope_(pause_or_complete_scope),
sweeping_in_progress_(sweeper_->sweeping_in_progress()) {
USE(pause_or_complete_scope_);
if (!sweeping_in_progress_) return;
int old_space_index = GetSweepSpaceIndex(OLD_SPACE);
old_space_sweeping_list_ =
std::move(sweeper_->sweeping_list_[old_space_index]);
sweeper_->sweeping_list_[old_space_index].clear();
}
Sweeper::FilterSweepingPagesScope::~FilterSweepingPagesScope() {
DCHECK_EQ(sweeping_in_progress_, sweeper_->sweeping_in_progress());
if (!sweeping_in_progress_) return;
sweeper_->sweeping_list_[GetSweepSpaceIndex(OLD_SPACE)] =
std::move(old_space_sweeping_list_);
// old_space_sweeping_list_ does not need to be cleared as we don't use it.
}
class Sweeper::SweeperTask final : public CancelableTask {
public:
SweeperTask(Isolate* isolate, Sweeper* sweeper,
base::Semaphore* pending_sweeper_tasks,
std::atomic<intptr_t>* num_sweeping_tasks,
AllocationSpace space_to_start)
: CancelableTask(isolate),
sweeper_(sweeper),
pending_sweeper_tasks_(pending_sweeper_tasks),
num_sweeping_tasks_(num_sweeping_tasks),
space_to_start_(space_to_start),
tracer_(isolate->heap()->tracer()) {}
~SweeperTask() override = default;
private:
void RunInternal() final {
TRACE_BACKGROUND_GC(tracer_,
GCTracer::BackgroundScope::MC_BACKGROUND_SWEEPING);
DCHECK(IsValidSweepingSpace(space_to_start_));
const int offset = space_to_start_ - FIRST_GROWABLE_PAGED_SPACE;
for (int i = 0; i < kNumberOfSweepingSpaces; i++) {
const AllocationSpace space_id = static_cast<AllocationSpace>(
FIRST_GROWABLE_PAGED_SPACE +
((i + offset) % kNumberOfSweepingSpaces));
// Do not sweep code space concurrently.
if (space_id == CODE_SPACE) continue;
DCHECK(IsValidSweepingSpace(space_id));
sweeper_->SweepSpaceFromTask(space_id);
}
(*num_sweeping_tasks_)--;
pending_sweeper_tasks_->Signal();
}
Sweeper* const sweeper_;
base::Semaphore* const pending_sweeper_tasks_;
std::atomic<intptr_t>* const num_sweeping_tasks_;
AllocationSpace space_to_start_;
GCTracer* const tracer_;
DISALLOW_COPY_AND_ASSIGN(SweeperTask);
};
class Sweeper::IncrementalSweeperTask final : public CancelableTask {
public:
IncrementalSweeperTask(Isolate* isolate, Sweeper* sweeper)
: CancelableTask(isolate), isolate_(isolate), sweeper_(sweeper) {}
~IncrementalSweeperTask() override = default;
private:
void RunInternal() final {
VMState<GC> state(isolate_);
TRACE_EVENT_CALL_STATS_SCOPED(isolate_, "v8", "V8.Task");
sweeper_->incremental_sweeper_pending_ = false;
if (sweeper_->sweeping_in_progress()) {
if (!sweeper_->SweepSpaceIncrementallyFromTask(CODE_SPACE)) {
sweeper_->ScheduleIncrementalSweepingTask();
}
}
}
Isolate* const isolate_;
Sweeper* const sweeper_;
DISALLOW_COPY_AND_ASSIGN(IncrementalSweeperTask);
};
void Sweeper::StartSweeping() {
CHECK(!stop_sweeper_tasks_);
sweeping_in_progress_ = true;
iterability_in_progress_ = true;
should_reduce_memory_ = heap_->ShouldReduceMemory();
MajorNonAtomicMarkingState* marking_state =
heap_->mark_compact_collector()->non_atomic_marking_state();
ForAllSweepingSpaces([this, marking_state](AllocationSpace space) {
// Sorting is done in order to make compaction more efficient: by sweeping
// pages with the most free bytes first, we make it more likely that when
// evacuating a page, already swept pages will have enough free bytes to
// hold the objects to move (and therefore, we won't need to wait for more
// pages to be swept in order to move those objects).
// Since maps don't move, there is no need to sort the pages from MAP_SPACE
// before sweeping them.
if (space != MAP_SPACE) {
int space_index = GetSweepSpaceIndex(space);
std::sort(
sweeping_list_[space_index].begin(),
sweeping_list_[space_index].end(), [marking_state](Page* a, Page* b) {
return marking_state->live_bytes(a) > marking_state->live_bytes(b);
});
}
});
}
void Sweeper::StartSweeperTasks() {
DCHECK_EQ(0, num_tasks_);
DCHECK_EQ(0, num_sweeping_tasks_);
if (FLAG_concurrent_sweeping && sweeping_in_progress_ &&
!heap_->delay_sweeper_tasks_for_testing_) {
ForAllSweepingSpaces([this](AllocationSpace space) {
DCHECK(IsValidSweepingSpace(space));
num_sweeping_tasks_++;
auto task = std::make_unique<SweeperTask>(
heap_->isolate(), this, &pending_sweeper_tasks_semaphore_,
&num_sweeping_tasks_, space);
DCHECK_LT(num_tasks_, kMaxSweeperTasks);
task_ids_[num_tasks_++] = task->id();
V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
});
ScheduleIncrementalSweepingTask();
}
}
Page* Sweeper::GetSweptPageSafe(PagedSpace* space) {
base::MutexGuard guard(&mutex_);
SweptList& list = swept_list_[GetSweepSpaceIndex(space->identity())];
if (!list.empty()) {
auto last_page = list.back();
list.pop_back();
return last_page;
}
return nullptr;
}
void Sweeper::MergeOldToNewRememberedSetsForSweptPages() {
base::MutexGuard guard(&mutex_);
ForAllSweepingSpaces([this](AllocationSpace space) {
SweptList& swept_list = swept_list_[GetSweepSpaceIndex(space)];
for (Page* p : swept_list) p->MergeOldToNewRememberedSets();
});
}
void Sweeper::AbortAndWaitForTasks() {
if (!FLAG_concurrent_sweeping) return;
for (int i = 0; i < num_tasks_; i++) {
if (heap_->isolate()->cancelable_task_manager()->TryAbort(task_ids_[i]) !=
TryAbortResult::kTaskAborted) {
pending_sweeper_tasks_semaphore_.Wait();
} else {
// Aborted case.
num_sweeping_tasks_--;
}
}
num_tasks_ = 0;
DCHECK_EQ(0, num_sweeping_tasks_);
}
void Sweeper::EnsureCompleted() {
if (!sweeping_in_progress_) return;
EnsureIterabilityCompleted();
// If sweeping is not completed or not running at all, we try to complete it
// here.
ForAllSweepingSpaces(
[this](AllocationSpace space) { ParallelSweepSpace(space, 0); });
AbortAndWaitForTasks();
ForAllSweepingSpaces([this](AllocationSpace space) {
CHECK(sweeping_list_[GetSweepSpaceIndex(space)].empty());
});
sweeping_in_progress_ = false;
}
void Sweeper::DrainSweepingWorklists() {
if (!sweeping_in_progress_) return;
ForAllSweepingSpaces(
[this](AllocationSpace space) { DrainSweepingWorklistForSpace(space); });
}
void Sweeper::DrainSweepingWorklistForSpace(AllocationSpace space) {
if (!sweeping_in_progress_) return;
ParallelSweepSpace(space, 0);
}
void Sweeper::SupportConcurrentSweeping() {
ForAllSweepingSpaces([this](AllocationSpace space) {
const int kMaxPagesToSweepPerSpace = 1;
ParallelSweepSpace(space, 0, kMaxPagesToSweepPerSpace);
});
}
bool Sweeper::AreSweeperTasksRunning() { return num_sweeping_tasks_ != 0; }
V8_INLINE size_t Sweeper::FreeAndProcessFreedMemory(
Address free_start, Address free_end, Page* page, Space* space,
bool non_empty_typed_slots, FreeListRebuildingMode free_list_mode,
FreeSpaceTreatmentMode free_space_mode) {
CHECK_GT(free_end, free_start);
size_t freed_bytes = 0;
size_t size = static_cast<size_t>(free_end - free_start);
if (free_space_mode == ZAP_FREE_SPACE) {
ZapCode(free_start, size);
}
ClearFreedMemoryMode clear_memory_mode =
(free_list_mode == REBUILD_FREE_LIST)
? ClearFreedMemoryMode::kDontClearFreedMemory
: ClearFreedMemoryMode::kClearFreedMemory;
page->heap()->CreateFillerObjectAtBackground(
free_start, static_cast<int>(size), clear_memory_mode);
if (free_list_mode == REBUILD_FREE_LIST) {
freed_bytes =
reinterpret_cast<PagedSpace*>(space)->UnaccountedFree(free_start, size);
}
if (should_reduce_memory_) page->DiscardUnusedMemory(free_start, size);
return freed_bytes;
}
V8_INLINE void Sweeper::CleanupRememberedSetEntriesForFreedMemory(
Address free_start, Address free_end, Page* page,
bool non_empty_typed_slots, FreeRangesMap* free_ranges_map,
InvalidatedSlotsCleanup* old_to_new_cleanup) {
DCHECK_LE(free_start, free_end);
RememberedSetSweeping::RemoveRange(page, free_start, free_end,
SlotSet::KEEP_EMPTY_BUCKETS);
RememberedSet<OLD_TO_OLD>::RemoveRange(page, free_start, free_end,
SlotSet::KEEP_EMPTY_BUCKETS);
if (non_empty_typed_slots) {
free_ranges_map->insert(std::pair<uint32_t, uint32_t>(
static_cast<uint32_t>(free_start - page->address()),
static_cast<uint32_t>(free_end - page->address())));
}
old_to_new_cleanup->Free(free_start, free_end);
}
void Sweeper::CleanupInvalidTypedSlotsOfFreeRanges(
Page* page, const FreeRangesMap& free_ranges_map) {
if (!free_ranges_map.empty()) {
TypedSlotSet* old_to_new = page->typed_slot_set<OLD_TO_NEW>();
if (old_to_new != nullptr) {
old_to_new->ClearInvalidSlots(free_ranges_map);
}
TypedSlotSet* old_to_old = page->typed_slot_set<OLD_TO_OLD>();
if (old_to_old != nullptr) {
old_to_old->ClearInvalidSlots(free_ranges_map);
}
}
}
void Sweeper::ClearMarkBitsAndHandleLivenessStatistics(
Page* page, size_t live_bytes, FreeListRebuildingMode free_list_mode) {
marking_state_->bitmap(page)->Clear();
if (free_list_mode == IGNORE_FREE_LIST) {
marking_state_->SetLiveBytes(page, 0);
// We did not free memory, so have to adjust allocated bytes here.
intptr_t freed_bytes = page->area_size() - live_bytes;
page->DecreaseAllocatedBytes(freed_bytes);
} else {
// Keep the old live bytes counter of the page until RefillFreeList, where
// the space size is refined.
// The allocated_bytes() counter is precisely the total size of objects.
DCHECK_EQ(live_bytes, page->allocated_bytes());
}
}
int Sweeper::RawSweep(
Page* p, FreeListRebuildingMode free_list_mode,
FreeSpaceTreatmentMode free_space_mode,
FreeSpaceMayContainInvalidatedSlots invalidated_slots_in_free_space,
const base::MutexGuard& page_guard) {
Space* space = p->owner();
DCHECK_NOT_NULL(space);
DCHECK(free_list_mode == IGNORE_FREE_LIST || space->identity() == OLD_SPACE ||
space->identity() == CODE_SPACE || space->identity() == MAP_SPACE);
DCHECK(!p->IsEvacuationCandidate() && !p->SweepingDone());
// Phase 1: Prepare the page for sweeping.
// Set the allocated_bytes_ counter to area_size and clear the wasted_memory_
// counter. The free operations below will decrease allocated_bytes_ to actual
// live bytes and keep track of wasted_memory_.
p->ResetAllocationStatistics();
CodeObjectRegistry* code_object_registry = p->GetCodeObjectRegistry();
if (code_object_registry) code_object_registry->Clear();
// Phase 2: Free the non-live memory and clean-up the regular remembered set
// entires.
// Liveness and freeing statistics.
size_t live_bytes = 0;
size_t max_freed_bytes = 0;
// TODO(ulan): we don't have to clear type old-to-old slots in code space
// because the concurrent marker doesn't mark code objects. This requires
// the write barrier for code objects to check the color of the code object.
bool non_empty_typed_slots = p->typed_slot_set<OLD_TO_NEW>() != nullptr ||
p->typed_slot_set<OLD_TO_OLD>() != nullptr;
// Clean invalidated slots during the final atomic pause. After resuming
// execution this isn't necessary, invalid old-to-new refs were already
// removed by mark compact's update pointers phase.
InvalidatedSlotsCleanup old_to_new_cleanup =
InvalidatedSlotsCleanup::NoCleanup(p);
if (invalidated_slots_in_free_space ==
FreeSpaceMayContainInvalidatedSlots::kYes)
old_to_new_cleanup = InvalidatedSlotsCleanup::OldToNew(p);
// The free ranges map is used for filtering typed slots.
FreeRangesMap free_ranges_map;
#ifdef V8_ENABLE_CONSERVATIVE_STACK_SCANNING
p->object_start_bitmap()->Clear();
#endif
// Iterate over the page using the live objects and free the memory before
// the given live object.
Address free_start = p->area_start();
for (auto object_and_size :
LiveObjectRange<kBlackObjects>(p, marking_state_->bitmap(p))) {
HeapObject const object = object_and_size.first;
if (code_object_registry)
code_object_registry->RegisterAlreadyExistingCodeObject(object.address());
DCHECK(marking_state_->IsBlack(object));
Address free_end = object.address();
if (free_end != free_start) {
max_freed_bytes =
Max(max_freed_bytes,
FreeAndProcessFreedMemory(free_start, free_end, p, space,
non_empty_typed_slots, free_list_mode,
free_space_mode));
CleanupRememberedSetEntriesForFreedMemory(
free_start, free_end, p, non_empty_typed_slots, &free_ranges_map,
&old_to_new_cleanup);
}
Map map = object.synchronized_map();
int size = object.SizeFromMap(map);
live_bytes += size;
free_start = free_end + size;
#ifdef V8_ENABLE_CONSERVATIVE_STACK_SCANNING
p->object_start_bitmap()->SetBit(object.address());
#endif
}
// If there is free memory after the last live object also free that.
Address free_end = p->area_end();
if (free_end != free_start) {
max_freed_bytes =
Max(max_freed_bytes,
FreeAndProcessFreedMemory(free_start, free_end, p, space,
non_empty_typed_slots, free_list_mode,
free_space_mode));
CleanupRememberedSetEntriesForFreedMemory(
free_start, free_end, p, non_empty_typed_slots, &free_ranges_map,
&old_to_new_cleanup);
}
// Phase 3: Post process the page.
CleanupInvalidTypedSlotsOfFreeRanges(p, free_ranges_map);
ClearMarkBitsAndHandleLivenessStatistics(p, live_bytes, free_list_mode);
p->set_concurrent_sweeping_state(Page::ConcurrentSweepingState::kDone);
if (code_object_registry) code_object_registry->Finalize();
if (free_list_mode == IGNORE_FREE_LIST) return 0;
return static_cast<int>(
p->owner()->free_list()->GuaranteedAllocatable(max_freed_bytes));
}
void Sweeper::SweepSpaceFromTask(AllocationSpace identity) {
Page* page = nullptr;
while (!stop_sweeper_tasks_ &&
((page = GetSweepingPageSafe(identity)) != nullptr)) {
// Typed slot sets are only recorded on code pages. Code pages
// are not swept concurrently to the application to ensure W^X.
DCHECK(!page->typed_slot_set<OLD_TO_NEW>() &&
!page->typed_slot_set<OLD_TO_OLD>());
ParallelSweepPage(page, identity);
}
}
bool Sweeper::SweepSpaceIncrementallyFromTask(AllocationSpace identity) {
if (Page* page = GetSweepingPageSafe(identity)) {
ParallelSweepPage(page, identity);
}
return sweeping_list_[GetSweepSpaceIndex(identity)].empty();
}
int Sweeper::ParallelSweepSpace(
AllocationSpace identity, int required_freed_bytes, int max_pages,
FreeSpaceMayContainInvalidatedSlots invalidated_slots_in_free_space) {
int max_freed = 0;
int pages_freed = 0;
Page* page = nullptr;
while ((page = GetSweepingPageSafe(identity)) != nullptr) {
int freed =
ParallelSweepPage(page, identity, invalidated_slots_in_free_space);
++pages_freed;
if (page->IsFlagSet(Page::NEVER_ALLOCATE_ON_PAGE)) {
// Free list of a never-allocate page will be dropped later on.
continue;
}
DCHECK_GE(freed, 0);
max_freed = Max(max_freed, freed);
if ((required_freed_bytes) > 0 && (max_freed >= required_freed_bytes))
return max_freed;
if ((max_pages > 0) && (pages_freed >= max_pages)) return max_freed;
}
return max_freed;
}
int Sweeper::ParallelSweepPage(
Page* page, AllocationSpace identity,
FreeSpaceMayContainInvalidatedSlots invalidated_slots_in_free_space) {
DCHECK(IsValidSweepingSpace(identity));
// The Scavenger may add already swept pages back.
if (page->SweepingDone()) return 0;
int max_freed = 0;
{
base::MutexGuard guard(page->mutex());
DCHECK(!page->SweepingDone());
// If the page is a code page, the CodePageMemoryModificationScope changes
// the page protection mode from rx -> rw while sweeping.
CodePageMemoryModificationScope code_page_scope(page);
DCHECK_EQ(Page::ConcurrentSweepingState::kPending,
page->concurrent_sweeping_state());
page->set_concurrent_sweeping_state(
Page::ConcurrentSweepingState::kInProgress);
const FreeSpaceTreatmentMode free_space_mode =
Heap::ShouldZapGarbage() ? ZAP_FREE_SPACE : IGNORE_FREE_SPACE;
max_freed = RawSweep(page, REBUILD_FREE_LIST, free_space_mode,
invalidated_slots_in_free_space, guard);
DCHECK(page->SweepingDone());
}
{
base::MutexGuard guard(&mutex_);
swept_list_[GetSweepSpaceIndex(identity)].push_back(page);
}
return max_freed;
}
void Sweeper::ScheduleIncrementalSweepingTask() {
if (!incremental_sweeper_pending_) {
incremental_sweeper_pending_ = true;
v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(heap_->isolate());
auto taskrunner =
V8::GetCurrentPlatform()->GetForegroundTaskRunner(isolate);
taskrunner->PostTask(
std::make_unique<IncrementalSweeperTask>(heap_->isolate(), this));
}
}
void Sweeper::AddPage(AllocationSpace space, Page* page,
Sweeper::AddPageMode mode) {
base::MutexGuard guard(&mutex_);
DCHECK(IsValidSweepingSpace(space));
DCHECK(!FLAG_concurrent_sweeping || !AreSweeperTasksRunning());
if (mode == Sweeper::REGULAR) {
PrepareToBeSweptPage(space, page);
} else {
// Page has been temporarily removed from the sweeper. Accounting already
// happened when the page was initially added, so it is skipped here.
DCHECK_EQ(Sweeper::READD_TEMPORARY_REMOVED_PAGE, mode);
}
DCHECK_EQ(Page::ConcurrentSweepingState::kPending,
page->concurrent_sweeping_state());
sweeping_list_[GetSweepSpaceIndex(space)].push_back(page);
}
void Sweeper::PrepareToBeSweptPage(AllocationSpace space, Page* page) {
#ifdef DEBUG
DCHECK_GE(page->area_size(),
static_cast<size_t>(marking_state_->live_bytes(page)));
DCHECK_EQ(Page::ConcurrentSweepingState::kDone,
page->concurrent_sweeping_state());
page->ForAllFreeListCategories([page](FreeListCategory* category) {
DCHECK(!category->is_linked(page->owner()->free_list()));
});
#endif // DEBUG
page->MoveOldToNewRememberedSetForSweeping();
page->set_concurrent_sweeping_state(Page::ConcurrentSweepingState::kPending);
heap_->paged_space(space)->IncreaseAllocatedBytes(
marking_state_->live_bytes(page), page);
}
Page* Sweeper::GetSweepingPageSafe(AllocationSpace space) {
base::MutexGuard guard(&mutex_);
DCHECK(IsValidSweepingSpace(space));
int space_index = GetSweepSpaceIndex(space);
Page* page = nullptr;
if (!sweeping_list_[space_index].empty()) {
page = sweeping_list_[space_index].back();
sweeping_list_[space_index].pop_back();
}
return page;
}
void Sweeper::EnsureIterabilityCompleted() {
if (!iterability_in_progress_) return;
if (FLAG_concurrent_sweeping && iterability_task_started_) {
if (heap_->isolate()->cancelable_task_manager()->TryAbort(
iterability_task_id_) != TryAbortResult::kTaskAborted) {
iterability_task_semaphore_.Wait();
}
iterability_task_started_ = false;
}
for (Page* page : iterability_list_) {
MakeIterable(page);
}
iterability_list_.clear();
iterability_in_progress_ = false;
}
class Sweeper::IterabilityTask final : public CancelableTask {
public:
IterabilityTask(Isolate* isolate, Sweeper* sweeper,
base::Semaphore* pending_iterability_task)
: CancelableTask(isolate),
sweeper_(sweeper),
pending_iterability_task_(pending_iterability_task),
tracer_(isolate->heap()->tracer()) {}
~IterabilityTask() override = default;
private:
void RunInternal() final {
TRACE_BACKGROUND_GC(tracer_,
GCTracer::BackgroundScope::MC_BACKGROUND_SWEEPING);
for (Page* page : sweeper_->iterability_list_) {
sweeper_->MakeIterable(page);
}
sweeper_->iterability_list_.clear();
pending_iterability_task_->Signal();
}
Sweeper* const sweeper_;
base::Semaphore* const pending_iterability_task_;
GCTracer* const tracer_;
DISALLOW_COPY_AND_ASSIGN(IterabilityTask);
};
void Sweeper::StartIterabilityTasks() {
if (!iterability_in_progress_) return;
DCHECK(!iterability_task_started_);
if (FLAG_concurrent_sweeping && !iterability_list_.empty()) {
auto task = std::make_unique<IterabilityTask>(heap_->isolate(), this,
&iterability_task_semaphore_);
iterability_task_id_ = task->id();
iterability_task_started_ = true;
V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
}
}
void Sweeper::AddPageForIterability(Page* page) {
DCHECK(sweeping_in_progress_);
DCHECK(iterability_in_progress_);
DCHECK(!iterability_task_started_);
DCHECK(IsValidIterabilitySpace(page->owner_identity()));
DCHECK_EQ(Page::ConcurrentSweepingState::kDone,
page->concurrent_sweeping_state());
iterability_list_.push_back(page);
page->set_concurrent_sweeping_state(Page::ConcurrentSweepingState::kPending);
}
void Sweeper::MakeIterable(Page* page) {
base::MutexGuard guard(page->mutex());
DCHECK(IsValidIterabilitySpace(page->owner_identity()));
const FreeSpaceTreatmentMode free_space_mode =
Heap::ShouldZapGarbage() ? ZAP_FREE_SPACE : IGNORE_FREE_SPACE;
RawSweep(page, IGNORE_FREE_LIST, free_space_mode,
FreeSpaceMayContainInvalidatedSlots::kNo, guard);
}
} // namespace internal
} // namespace v8