| // 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. |
| |
| #ifndef RUNTIME_VM_HEAP_POINTER_BLOCK_H_ |
| #define RUNTIME_VM_HEAP_POINTER_BLOCK_H_ |
| |
| #include "platform/assert.h" |
| #include "vm/globals.h" |
| #include "vm/os_thread.h" |
| #include "vm/tagged_pointer.h" |
| |
| namespace dart { |
| |
| // Forward declarations. |
| class Isolate; |
| class ObjectPointerVisitor; |
| |
| // A set of ObjectPtr. Must be emptied before destruction (using Pop/Reset). |
| template <int Size> |
| class PointerBlock : public MallocAllocated { |
| public: |
| enum { kSize = Size }; |
| |
| void Reset() { |
| top_ = 0; |
| next_ = nullptr; |
| } |
| |
| PointerBlock<Size>* next() const { return next_; } |
| void set_next(PointerBlock<Size>* next) { next_ = next; } |
| |
| intptr_t Count() const { return top_; } |
| bool IsFull() const { return Count() == kSize; } |
| bool IsEmpty() const { return Count() == 0; } |
| |
| void Push(ObjectPtr obj) { |
| ASSERT(!IsFull()); |
| pointers_[top_++] = obj; |
| } |
| |
| ObjectPtr Pop() { |
| ASSERT(!IsEmpty()); |
| return pointers_[--top_]; |
| } |
| |
| #if defined(TESTING) |
| bool Contains(ObjectPtr obj) const { |
| // Generated code appends to store buffers; tell MemorySanitizer. |
| MSAN_UNPOISON(this, sizeof(*this)); |
| for (intptr_t i = 0; i < Count(); i++) { |
| if (pointers_[i] == obj) { |
| return true; |
| } |
| } |
| return false; |
| } |
| #endif // TESTING |
| |
| static intptr_t top_offset() { return OFFSET_OF(PointerBlock<Size>, top_); } |
| static intptr_t pointers_offset() { |
| return OFFSET_OF(PointerBlock<Size>, pointers_); |
| } |
| |
| void VisitObjectPointers(ObjectPointerVisitor* visitor); |
| |
| private: |
| PointerBlock() : next_(nullptr), top_(0) {} |
| ~PointerBlock() { |
| ASSERT(IsEmpty()); // Guard against unintentionally discarding pointers. |
| } |
| |
| PointerBlock<Size>* next_; |
| int32_t top_; |
| ObjectPtr pointers_[kSize]; |
| |
| template <int> |
| friend class BlockStack; |
| |
| DISALLOW_COPY_AND_ASSIGN(PointerBlock); |
| }; |
| |
| // A synchronized collection of pointer blocks of a particular size. |
| // This class is meant to be used as a base (note PushBlockImpl is protected). |
| // The global list of cached empty blocks is currently per-size. |
| template <int BlockSize> |
| class BlockStack { |
| public: |
| typedef PointerBlock<BlockSize> Block; |
| |
| BlockStack(); |
| ~BlockStack(); |
| static void Init(); |
| static void Cleanup(); |
| |
| // Partially filled blocks can be reused, and there is an "inifite" supply |
| // of empty blocks (reused or newly allocated). In any case, the caller |
| // takes ownership of the returned block. |
| Block* PopNonFullBlock(); |
| Block* PopEmptyBlock(); |
| Block* PopNonEmptyBlock(); |
| |
| // Pops and returns all non-empty blocks as a linked list (owned by caller). |
| Block* TakeBlocks(); |
| |
| // Discards the contents of all non-empty blocks. |
| void Reset(); |
| |
| bool IsEmpty(); |
| |
| Block* WaitForWork(RelaxedAtomic<uintptr_t>* num_busy); |
| |
| protected: |
| class List { |
| public: |
| List() : head_(nullptr), length_(0) {} |
| ~List(); |
| void Push(Block* block); |
| Block* Pop(); |
| intptr_t length() const { return length_; } |
| bool IsEmpty() const { return head_ == nullptr; } |
| Block* PopAll(); |
| Block* Peek() { return head_; } |
| |
| private: |
| Block* head_; |
| intptr_t length_; |
| DISALLOW_COPY_AND_ASSIGN(List); |
| }; |
| |
| bool IsEmptyLocked(); |
| |
| // Adds and transfers ownership of the block to the buffer. |
| void PushBlockImpl(Block* block); |
| |
| // If needed, trims the global cache of empty blocks. |
| static void TrimGlobalEmpty(); |
| |
| List full_; |
| List partial_; |
| Monitor monitor_; |
| |
| // Note: This is shared on the basis of block size. |
| static const intptr_t kMaxGlobalEmpty = 100; |
| static List* global_empty_; |
| static Mutex* global_mutex_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(BlockStack); |
| }; |
| |
| template <typename Stack> |
| class BlockWorkList : public ValueObject { |
| public: |
| typedef typename Stack::Block Block; |
| |
| explicit BlockWorkList(Stack* stack) : stack_(stack) { |
| local_output_ = stack_->PopEmptyBlock(); |
| local_input_ = stack_->PopEmptyBlock(); |
| } |
| |
| ~BlockWorkList() { |
| ASSERT(local_output_ == nullptr); |
| ASSERT(local_input_ == nullptr); |
| ASSERT(stack_ == nullptr); |
| } |
| |
| // Returns nullptr if no more work was found. |
| ObjectPtr Pop() { |
| ASSERT(local_input_ != nullptr); |
| if (UNLIKELY(local_input_->IsEmpty())) { |
| if (!local_output_->IsEmpty()) { |
| auto temp = local_output_; |
| local_output_ = local_input_; |
| local_input_ = temp; |
| } else { |
| Block* new_work = stack_->PopNonEmptyBlock(); |
| if (new_work == nullptr) { |
| return nullptr; |
| } |
| stack_->PushBlock(local_input_); |
| local_input_ = new_work; |
| // Generated code appends to marking stacks; tell MemorySanitizer. |
| MSAN_UNPOISON(local_input_, sizeof(*local_input_)); |
| } |
| } |
| return local_input_->Pop(); |
| } |
| |
| void Push(ObjectPtr raw_obj) { |
| if (UNLIKELY(local_output_->IsFull())) { |
| stack_->PushBlock(local_output_); |
| local_output_ = stack_->PopEmptyBlock(); |
| } |
| local_output_->Push(raw_obj); |
| } |
| |
| bool WaitForWork(RelaxedAtomic<uintptr_t>* num_busy) { |
| ASSERT(local_input_->IsEmpty()); |
| Block* new_work = stack_->WaitForWork(num_busy); |
| if (new_work == NULL) { |
| return false; |
| } |
| stack_->PushBlock(local_input_); |
| local_input_ = new_work; |
| return true; |
| } |
| |
| void Finalize() { |
| ASSERT(local_output_->IsEmpty()); |
| stack_->PushBlock(local_output_); |
| local_output_ = nullptr; |
| ASSERT(local_input_->IsEmpty()); |
| stack_->PushBlock(local_input_); |
| local_input_ = nullptr; |
| // Fail fast on attempts to mark after finalizing. |
| stack_ = nullptr; |
| } |
| |
| void AbandonWork() { |
| stack_->PushBlock(local_output_); |
| local_output_ = nullptr; |
| stack_->PushBlock(local_input_); |
| local_input_ = nullptr; |
| stack_ = nullptr; |
| } |
| |
| bool IsEmpty() { |
| if (!local_input_->IsEmpty()) { |
| return false; |
| } |
| if (!local_output_->IsEmpty()) { |
| return false; |
| } |
| return stack_->IsEmpty(); |
| } |
| |
| private: |
| Block* local_output_; |
| Block* local_input_; |
| Stack* stack_; |
| }; |
| |
| static const int kStoreBufferBlockSize = 1024; |
| class StoreBuffer : public BlockStack<kStoreBufferBlockSize> { |
| public: |
| // Interrupt when crossing this threshold of non-empty blocks in the buffer. |
| static const intptr_t kMaxNonEmpty = 100; |
| |
| enum ThresholdPolicy { kCheckThreshold, kIgnoreThreshold }; |
| |
| // Adds and transfers ownership of the block to the buffer. Optionally |
| // checks the number of non-empty blocks for overflow, and schedules an |
| // interrupt on the current isolate if so. |
| void PushBlock(Block* block, ThresholdPolicy policy); |
| |
| // Check whether non-empty blocks have exceeded kMaxNonEmpty (but takes no |
| // action). |
| bool Overflowed(); |
| |
| void VisitObjectPointers(ObjectPointerVisitor* visitor); |
| }; |
| |
| typedef StoreBuffer::Block StoreBufferBlock; |
| |
| static const int kMarkingStackBlockSize = 64; |
| class MarkingStack : public BlockStack<kMarkingStackBlockSize> { |
| public: |
| // Adds and transfers ownership of the block to the buffer. |
| void PushBlock(Block* block) { |
| BlockStack<Block::kSize>::PushBlockImpl(block); |
| } |
| }; |
| |
| typedef MarkingStack::Block MarkingStackBlock; |
| typedef BlockWorkList<MarkingStack> MarkerWorkList; |
| |
| static const int kPromotionStackBlockSize = 64; |
| class PromotionStack : public BlockStack<kPromotionStackBlockSize> { |
| public: |
| // Adds and transfers ownership of the block to the buffer. |
| void PushBlock(Block* block) { |
| BlockStack<Block::kSize>::PushBlockImpl(block); |
| } |
| }; |
| |
| typedef PromotionStack::Block PromotionStackBlock; |
| typedef BlockWorkList<PromotionStack> PromotionWorkList; |
| |
| } // namespace dart |
| |
| #endif // RUNTIME_VM_HEAP_POINTER_BLOCK_H_ |