mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 23:19:47 +00:00
2c72ea6e03
Remove change to Scavenger::UsedInWords. TEST=--verbose_gc Change-Id: I2e4d7f369e7d7c34e488a5c0994a7599a2e78be9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/241244 Reviewed-by: Siva Annamalai <asiva@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
257 lines
6.6 KiB
C++
257 lines
6.6 KiB
C++
// Copyright (c) 2012, 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/pointer_block.h"
|
|
|
|
#include "platform/assert.h"
|
|
#include "vm/lockers.h"
|
|
#include "vm/runtime_entry.h"
|
|
|
|
namespace dart {
|
|
|
|
DEFINE_LEAF_RUNTIME_ENTRY(void, StoreBufferBlockProcess, 1, Thread* thread) {
|
|
thread->StoreBufferBlockProcess(StoreBuffer::kCheckThreshold);
|
|
}
|
|
END_LEAF_RUNTIME_ENTRY
|
|
|
|
DEFINE_LEAF_RUNTIME_ENTRY(void, MarkingStackBlockProcess, 1, Thread* thread) {
|
|
thread->MarkingStackBlockProcess();
|
|
}
|
|
END_LEAF_RUNTIME_ENTRY
|
|
|
|
template <int BlockSize>
|
|
typename BlockStack<BlockSize>::List* BlockStack<BlockSize>::global_empty_ =
|
|
NULL;
|
|
template <int BlockSize>
|
|
Mutex* BlockStack<BlockSize>::global_mutex_ = NULL;
|
|
|
|
template <int BlockSize>
|
|
void BlockStack<BlockSize>::Init() {
|
|
global_empty_ = new List();
|
|
if (global_mutex_ == NULL) {
|
|
global_mutex_ = new Mutex();
|
|
}
|
|
}
|
|
|
|
template <int BlockSize>
|
|
void BlockStack<BlockSize>::Cleanup() {
|
|
delete global_empty_;
|
|
global_empty_ = NULL;
|
|
}
|
|
|
|
template <int BlockSize>
|
|
BlockStack<BlockSize>::BlockStack() : monitor_() {}
|
|
|
|
template <int BlockSize>
|
|
BlockStack<BlockSize>::~BlockStack() {
|
|
Reset();
|
|
}
|
|
|
|
template <int BlockSize>
|
|
void BlockStack<BlockSize>::Reset() {
|
|
MonitorLocker local_mutex_locker(&monitor_);
|
|
{
|
|
// Empty all blocks and move them to the global cache.
|
|
MutexLocker global_mutex_locker(global_mutex_);
|
|
while (!full_.IsEmpty()) {
|
|
Block* block = full_.Pop();
|
|
block->Reset();
|
|
global_empty_->Push(block);
|
|
}
|
|
while (!partial_.IsEmpty()) {
|
|
Block* block = partial_.Pop();
|
|
block->Reset();
|
|
global_empty_->Push(block);
|
|
}
|
|
TrimGlobalEmpty();
|
|
}
|
|
}
|
|
|
|
template <int BlockSize>
|
|
typename BlockStack<BlockSize>::Block* BlockStack<BlockSize>::TakeBlocks() {
|
|
MonitorLocker ml(&monitor_);
|
|
while (!partial_.IsEmpty()) {
|
|
full_.Push(partial_.Pop());
|
|
}
|
|
return full_.PopAll();
|
|
}
|
|
|
|
template <int BlockSize>
|
|
void BlockStack<BlockSize>::PushBlockImpl(Block* block) {
|
|
ASSERT(block->next() == NULL); // Should be just a single block.
|
|
if (block->IsFull()) {
|
|
MonitorLocker ml(&monitor_);
|
|
bool was_empty = IsEmptyLocked();
|
|
full_.Push(block);
|
|
if (was_empty) ml.Notify();
|
|
} else if (block->IsEmpty()) {
|
|
MutexLocker ml(global_mutex_);
|
|
global_empty_->Push(block);
|
|
TrimGlobalEmpty();
|
|
} else {
|
|
MonitorLocker ml(&monitor_);
|
|
bool was_empty = IsEmptyLocked();
|
|
partial_.Push(block);
|
|
if (was_empty) ml.Notify();
|
|
}
|
|
}
|
|
|
|
template <int BlockSize>
|
|
typename BlockStack<BlockSize>::Block* BlockStack<BlockSize>::WaitForWork(
|
|
RelaxedAtomic<uintptr_t>* num_busy) {
|
|
MonitorLocker ml(&monitor_);
|
|
if (num_busy->fetch_sub(1u) == 1 /* 1 is before subtraction */) {
|
|
// This is the last worker, wake the others now that we know no further work
|
|
// will come.
|
|
ml.NotifyAll();
|
|
return NULL;
|
|
}
|
|
for (;;) {
|
|
if (!full_.IsEmpty()) {
|
|
num_busy->fetch_add(1u);
|
|
return full_.Pop();
|
|
}
|
|
if (!partial_.IsEmpty()) {
|
|
num_busy->fetch_add(1u);
|
|
return partial_.Pop();
|
|
}
|
|
ml.Wait();
|
|
if (num_busy->load() == 0) {
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <int Size>
|
|
void PointerBlock<Size>::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
|
// Generated code appends to store buffers; tell MemorySanitizer.
|
|
MSAN_UNPOISON(this, sizeof(*this));
|
|
visitor->VisitPointers(&pointers_[0], top_);
|
|
}
|
|
|
|
void StoreBuffer::PushBlock(Block* block, ThresholdPolicy policy) {
|
|
BlockStack<Block::kSize>::PushBlockImpl(block);
|
|
if ((policy == kCheckThreshold) && Overflowed()) {
|
|
MonitorLocker ml(&monitor_);
|
|
Thread* thread = Thread::Current();
|
|
// Sanity check: it makes no sense to schedule the GC in another isolate
|
|
// group.
|
|
// (If Isolate ever gets multiple store buffers, we should avoid this
|
|
// coupling by passing in an explicit callback+parameter at construction.)
|
|
ASSERT(thread->isolate_group()->store_buffer() == this);
|
|
thread->ScheduleInterrupts(Thread::kVMInterrupt);
|
|
}
|
|
}
|
|
|
|
template <int BlockSize>
|
|
typename BlockStack<BlockSize>::Block*
|
|
BlockStack<BlockSize>::PopNonFullBlock() {
|
|
{
|
|
MonitorLocker ml(&monitor_);
|
|
if (!partial_.IsEmpty()) {
|
|
return partial_.Pop();
|
|
}
|
|
}
|
|
return PopEmptyBlock();
|
|
}
|
|
|
|
template <int BlockSize>
|
|
typename BlockStack<BlockSize>::Block* BlockStack<BlockSize>::PopEmptyBlock() {
|
|
{
|
|
MutexLocker ml(global_mutex_);
|
|
if (!global_empty_->IsEmpty()) {
|
|
return global_empty_->Pop();
|
|
}
|
|
}
|
|
return new Block();
|
|
}
|
|
|
|
template <int BlockSize>
|
|
typename BlockStack<BlockSize>::Block*
|
|
BlockStack<BlockSize>::PopNonEmptyBlock() {
|
|
MonitorLocker ml(&monitor_);
|
|
if (!full_.IsEmpty()) {
|
|
return full_.Pop();
|
|
} else if (!partial_.IsEmpty()) {
|
|
return partial_.Pop();
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
template <int BlockSize>
|
|
bool BlockStack<BlockSize>::IsEmpty() {
|
|
MonitorLocker ml(&monitor_);
|
|
return IsEmptyLocked();
|
|
}
|
|
|
|
template <int BlockSize>
|
|
bool BlockStack<BlockSize>::IsEmptyLocked() {
|
|
return full_.IsEmpty() && partial_.IsEmpty();
|
|
}
|
|
|
|
template <int BlockSize>
|
|
BlockStack<BlockSize>::List::~List() {
|
|
while (!IsEmpty()) {
|
|
delete Pop();
|
|
}
|
|
}
|
|
|
|
template <int BlockSize>
|
|
typename BlockStack<BlockSize>::Block* BlockStack<BlockSize>::List::Pop() {
|
|
Block* result = head_;
|
|
head_ = head_->next_;
|
|
--length_;
|
|
result->next_ = NULL;
|
|
return result;
|
|
}
|
|
|
|
template <int BlockSize>
|
|
typename BlockStack<BlockSize>::Block* BlockStack<BlockSize>::List::PopAll() {
|
|
Block* result = head_;
|
|
head_ = NULL;
|
|
length_ = 0;
|
|
return result;
|
|
}
|
|
|
|
template <int BlockSize>
|
|
void BlockStack<BlockSize>::List::Push(Block* block) {
|
|
ASSERT(block->next_ == NULL);
|
|
block->next_ = head_;
|
|
head_ = block;
|
|
++length_;
|
|
}
|
|
|
|
bool StoreBuffer::Overflowed() {
|
|
MonitorLocker ml(&monitor_);
|
|
return (full_.length() + partial_.length()) > kMaxNonEmpty;
|
|
}
|
|
|
|
intptr_t StoreBuffer::Size() {
|
|
ASSERT(Thread::Current()->IsAtSafepoint()); // No lock needed.
|
|
return full_.length() + partial_.length();
|
|
}
|
|
|
|
void StoreBuffer::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
|
for (Block* block = full_.Peek(); block != NULL; block = block->next()) {
|
|
block->VisitObjectPointers(visitor);
|
|
}
|
|
for (Block* block = partial_.Peek(); block != NULL; block = block->next()) {
|
|
block->VisitObjectPointers(visitor);
|
|
}
|
|
}
|
|
|
|
template <int BlockSize>
|
|
void BlockStack<BlockSize>::TrimGlobalEmpty() {
|
|
DEBUG_ASSERT(global_mutex_->IsOwnedByCurrentThread());
|
|
while (global_empty_->length() > kMaxGlobalEmpty) {
|
|
delete global_empty_->Pop();
|
|
}
|
|
}
|
|
|
|
template class BlockStack<kStoreBufferBlockSize>;
|
|
template class BlockStack<kMarkingStackBlockSize>;
|
|
|
|
} // namespace dart
|