blob: d8145217709874aecefcda03a786bd26d2b59d30 [file] [log] [blame]
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/heap/sweeper.h"
#include "vm/globals.h"
#include "vm/heap/freelist.h"
#include "vm/heap/heap.h"
#include "vm/heap/pages.h"
#include "vm/heap/safepoint.h"
#include "vm/lockers.h"
#include "vm/thread_pool.h"
#include "vm/timeline.h"
namespace dart {
bool GCSweeper::SweepPage(OldPage* page, FreeList* freelist, bool locked) {
ASSERT(!page->is_image_page());
// Keep track whether this page is still in use.
intptr_t used_in_bytes = 0;
bool is_executable = (page->type() == OldPage::kExecutable);
uword start = page->object_start();
uword end = page->object_end();
uword current = start;
while (current < end) {
ObjectPtr raw_obj = UntaggedObject::FromAddr(current);
ASSERT(OldPage::Of(raw_obj) == page);
// These acquire operations balance release operations in array
// truncaton, ensuring the writes creating the filler object are ordered
// before the writes inserting the filler object into the freelist.
uword tags = raw_obj->untag()->tags_.load(std::memory_order_acquire);
intptr_t obj_size = raw_obj->untag()->HeapSize(tags);
if (UntaggedObject::IsMarked(tags)) {
// Found marked object. Clear the mark bit and update swept bytes.
raw_obj->untag()->ClearMarkBit();
used_in_bytes += obj_size;
} else {
uword free_end = current + obj_size;
while (free_end < end) {
ObjectPtr next_obj = UntaggedObject::FromAddr(free_end);
tags = next_obj->untag()->tags_.load(std::memory_order_acquire);
if (UntaggedObject::IsMarked(tags)) {
// Reached the end of the free block.
break;
}
// Expand the free block by the size of this object.
free_end += next_obj->untag()->HeapSize(tags);
}
obj_size = free_end - current;
if (is_executable) {
uword cursor = current;
uword end = current + obj_size;
while (cursor < end) {
*reinterpret_cast<uword*>(cursor) = kBreakInstructionFiller;
cursor += kWordSize;
}
} else {
#if defined(DEBUG)
memset(reinterpret_cast<void*>(current), Heap::kZapByte, obj_size);
#endif // DEBUG
}
if ((current != start) || (free_end != end)) {
// Only add to the free list if not covering the whole page.
if (locked) {
freelist->FreeLocked(current, obj_size);
} else {
freelist->Free(current, obj_size);
}
}
}
current += obj_size;
}
ASSERT(current == end);
page->set_used_in_bytes(used_in_bytes);
return used_in_bytes != 0; // In use.
}
intptr_t GCSweeper::SweepLargePage(OldPage* page) {
ASSERT(!page->is_image_page());
intptr_t words_to_end = 0;
ObjectPtr raw_obj = UntaggedObject::FromAddr(page->object_start());
ASSERT(OldPage::Of(raw_obj) == page);
if (raw_obj->untag()->IsMarked()) {
raw_obj->untag()->ClearMarkBit();
words_to_end = (raw_obj->untag()->HeapSize() >> kWordSizeLog2);
}
#ifdef DEBUG
// Array::MakeFixedLength creates trailing filler objects,
// but they are always unreachable. Verify that they are not marked.
uword current =
UntaggedObject::ToAddr(raw_obj) + raw_obj->untag()->HeapSize();
uword end = page->object_end();
while (current < end) {
ObjectPtr cur_obj = UntaggedObject::FromAddr(current);
ASSERT(!cur_obj->untag()->IsMarked());
intptr_t obj_size = cur_obj->untag()->HeapSize();
memset(reinterpret_cast<void*>(current), Heap::kZapByte, obj_size);
current += obj_size;
}
#endif // DEBUG
return words_to_end;
}
class ConcurrentSweeperTask : public ThreadPool::Task {
public:
ConcurrentSweeperTask(IsolateGroup* isolate_group,
PageSpace* old_space,
OldPage* first,
OldPage* last,
OldPage* large_first,
OldPage* large_last)
: task_isolate_group_(isolate_group),
old_space_(old_space),
first_(first),
last_(last),
large_first_(large_first),
large_last_(large_last) {
ASSERT(task_isolate_group_ != NULL);
ASSERT(first_ != NULL);
ASSERT(old_space_ != NULL);
ASSERT(last_ != NULL);
MonitorLocker ml(old_space_->tasks_lock());
old_space_->set_tasks(old_space_->tasks() + 1);
old_space_->set_phase(PageSpace::kSweepingLarge);
}
virtual void Run() {
bool result = Thread::EnterIsolateGroupAsHelper(
task_isolate_group_, Thread::kSweeperTask, /*bypass_safepoint=*/true);
ASSERT(result);
{
Thread* thread = Thread::Current();
ASSERT(thread->BypassSafepoints()); // Or we should be checking in.
TIMELINE_FUNCTION_GC_DURATION(thread, "ConcurrentSweep");
GCSweeper sweeper;
OldPage* page = large_first_;
OldPage* prev_page = NULL;
while (page != NULL) {
OldPage* next_page;
if (page == large_last_) {
// Don't access page->next(), which would be a race with mutator
// allocating new pages.
next_page = NULL;
} else {
next_page = page->next();
}
ASSERT(page->type() == OldPage::kData);
const intptr_t words_to_end = sweeper.SweepLargePage(page);
if (words_to_end == 0) {
old_space_->FreeLargePage(page, prev_page);
} else {
old_space_->TruncateLargePage(page, words_to_end << kWordSizeLog2);
prev_page = page;
}
page = next_page;
}
{
MonitorLocker ml(old_space_->tasks_lock());
ASSERT(old_space_->phase() == PageSpace::kSweepingLarge);
old_space_->set_phase(PageSpace::kSweepingRegular);
ml.NotifyAll();
}
intptr_t shard = 0;
const intptr_t num_shards = Utils::Maximum(FLAG_scavenger_tasks, 1);
page = first_;
prev_page = NULL;
while (page != NULL) {
OldPage* next_page;
if (page == last_) {
// Don't access page->next(), which would be a race with mutator
// allocating new pages.
next_page = NULL;
} else {
next_page = page->next();
}
ASSERT(page->type() == OldPage::kData);
shard = (shard + 1) % num_shards;
bool page_in_use =
sweeper.SweepPage(page, old_space_->DataFreeList(shard), false);
if (page_in_use) {
prev_page = page;
} else {
old_space_->FreePage(page, prev_page);
}
{
// Notify the mutator thread that we have added elements to the free
// list or that more capacity is available.
MonitorLocker ml(old_space_->tasks_lock());
ml.Notify();
}
page = next_page;
}
}
// Exit isolate cleanly *before* notifying it, to avoid shutdown race.
Thread::ExitIsolateGroupAsHelper(/*bypass_safepoint=*/true);
// This sweeper task is done. Notify the original isolate.
{
MonitorLocker ml(old_space_->tasks_lock());
old_space_->set_tasks(old_space_->tasks() - 1);
ASSERT(old_space_->phase() == PageSpace::kSweepingRegular);
old_space_->set_phase(PageSpace::kDone);
ml.NotifyAll();
}
}
private:
IsolateGroup* task_isolate_group_;
PageSpace* old_space_;
OldPage* first_;
OldPage* last_;
OldPage* large_first_;
OldPage* large_last_;
};
void GCSweeper::SweepConcurrent(IsolateGroup* isolate_group,
OldPage* first,
OldPage* last,
OldPage* large_first,
OldPage* large_last,
FreeList* freelist) {
bool result = Dart::thread_pool()->Run<ConcurrentSweeperTask>(
isolate_group, isolate_group->heap()->old_space(), first, last,
large_first, large_last);
ASSERT(result);
}
} // namespace dart