|  | // 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, | 
|  | OldMarkingStackBlockProcess, | 
|  | 1, | 
|  | Thread* thread) { | 
|  | thread->OldMarkingStackBlockProcess(); | 
|  | } | 
|  | END_LEAF_RUNTIME_ENTRY | 
|  |  | 
|  | DEFINE_LEAF_RUNTIME_ENTRY(void, | 
|  | NewMarkingStackBlockProcess, | 
|  | 1, | 
|  | Thread* thread) { | 
|  | thread->NewMarkingStackBlockProcess(); | 
|  | } | 
|  | END_LEAF_RUNTIME_ENTRY | 
|  |  | 
|  | template <int BlockSize> | 
|  | typename BlockStack<BlockSize>::List* BlockStack<BlockSize>::global_empty_ = | 
|  | nullptr; | 
|  | template <int BlockSize> | 
|  | Mutex* BlockStack<BlockSize>::global_mutex_ = nullptr; | 
|  |  | 
|  | template <int BlockSize> | 
|  | void BlockStack<BlockSize>::Init() { | 
|  | global_empty_ = new List(); | 
|  | if (global_mutex_ == nullptr) { | 
|  | global_mutex_ = new Mutex(); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <int BlockSize> | 
|  | void BlockStack<BlockSize>::Cleanup() { | 
|  | delete global_empty_; | 
|  | global_empty_ = nullptr; | 
|  | } | 
|  |  | 
|  | 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>::PopAll() { | 
|  | MonitorLocker ml(&monitor_); | 
|  | while (!partial_.IsEmpty()) { | 
|  | full_.Push(partial_.Pop()); | 
|  | } | 
|  | return full_.PopAll(); | 
|  | } | 
|  |  | 
|  | template <int BlockSize> | 
|  | void BlockStack<BlockSize>::PushAll(Block* block) { | 
|  | while (block != nullptr) { | 
|  | Block* next = block->next(); | 
|  | block->set_next(nullptr); | 
|  | PushBlockImpl(block); | 
|  | block = next; | 
|  | } | 
|  | } | 
|  |  | 
|  | template <int BlockSize> | 
|  | void BlockStack<BlockSize>::PushBlockImpl(Block* block) { | 
|  | ASSERT(block->next() == nullptr);  // 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, | 
|  | bool abort) { | 
|  | 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 nullptr; | 
|  | } | 
|  | if (abort) { | 
|  | return nullptr; | 
|  | } | 
|  | 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 nullptr; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | 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 nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | 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_ = nullptr; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | template <int BlockSize> | 
|  | typename BlockStack<BlockSize>::Block* BlockStack<BlockSize>::List::PopAll() { | 
|  | Block* result = head_; | 
|  | head_ = nullptr; | 
|  | length_ = 0; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | template <int BlockSize> | 
|  | void BlockStack<BlockSize>::List::Push(Block* block) { | 
|  | ASSERT(block->next_ == nullptr); | 
|  | 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()->OwnsGCSafepoint());  // No lock needed. | 
|  | return full_.length() + partial_.length(); | 
|  | } | 
|  |  | 
|  | template <int BlockSize> | 
|  | void BlockStack<BlockSize>::VisitObjectPointers(ObjectPointerVisitor* visitor) { | 
|  | for (Block* block = full_.Peek(); block != nullptr; block = block->next()) { | 
|  | block->VisitObjectPointers(visitor); | 
|  | } | 
|  | for (Block* block = partial_.Peek(); block != nullptr; | 
|  | 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 PointerBlock<kStoreBufferBlockSize>; | 
|  | template class PointerBlock<kMarkingStackBlockSize>; | 
|  |  | 
|  | template class BlockStack<kStoreBufferBlockSize>; | 
|  | template class BlockStack<kMarkingStackBlockSize>; | 
|  |  | 
|  | }  // namespace dart |