blob: 5ef6dc2bd64ad43f749657f6854f7de3cf8fd10e [file] [log] [blame]
// 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_