blob: d59a1ba22b0869d44eb9309ecca2df6bd27e827e [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.
#ifndef RUNTIME_VM_HANDLES_H_
#define RUNTIME_VM_HANDLES_H_
#include "vm/allocation.h"
#include "vm/flags.h"
#include "vm/os.h"
namespace dart {
// Handles are used in the Dart Virtual Machine to ensure that access
// to dart objects in the virtual machine code is done in a
// Garbage Collection safe manner.
//
// The class Handles is the basic type that implements creation of handles and
// manages their life cycle (allocated either in the current zone or
// current handle scope).
// The two forms of handle allocation are:
// - allocation of handles in the current zone (Handle::AllocateZoneHandle).
// Handles allocated in this manner are destroyed when the zone is destroyed.
// - allocation of handles in a scoped manner (Handle::AllocateHandle).
// A new scope can be started using HANDLESCOPE(thread).
// Handles allocated in this manner are destroyed when the HandleScope
// object is destroyed.
// Code that uses scoped handles typically looks as follows:
// {
// HANDLESCOPE(thread);
// const String& str = String::Handle(String::New("abc"));
// .....
// .....
// }
// Code that uses zone handles typically looks as follows:
// const String& str = String::ZoneHandle(String::New("abc"));
// .....
// .....
//
// The Handle function for each object type internally uses the
// Handles::AllocateHandle() function for creating handles. The Handle
// function of the object type is the only way to create scoped handles
// in the dart VM.
// The ZoneHandle function for each object type internally uses the
// Handles::AllocateZoneHandle() function for creating zone handles.
// The ZoneHandle function of the object type is the only way to create
// zone handles in the dart VM.
// Forward declarations.
class ObjectPointerVisitor;
class HandleVisitor;
template <int kHandleSizeInWords, int kHandlesPerChunk, int kOffsetOfRawPtr>
class Handles {
public:
Handles()
: zone_blocks_(NULL),
first_scoped_block_(NULL),
scoped_blocks_(&first_scoped_block_) {}
~Handles() { DeleteAll(); }
// Visit all object pointers stored in the various handles.
void VisitObjectPointers(ObjectPointerVisitor* visitor);
// Visit all the scoped handles.
void VisitScopedHandles(ObjectPointerVisitor* visitor);
// Visit all blocks that have been added since the last time
// this method was called.
// Be careful with this, since multiple users of this method could
// interfere with eachother.
// Currently only used by GC trace facility.
void VisitUnvisitedScopedHandles(ObjectPointerVisitor* visitor);
// Visit all of the various handles.
void Visit(HandleVisitor* visitor);
// Reset the handles so that we can reuse.
void Reset();
// Allocates a handle in the current handle scope. This handle is valid only
// in the current handle scope and is destroyed when the current handle
// scope ends.
static uword AllocateHandle(Zone* zone);
// Allocates a handle in the current zone. This handle will be destroyed
// when the current zone is destroyed.
static uword AllocateZoneHandle(Zone* zone);
// Returns true if specified handle is a zone handle.
static bool IsZoneHandle(uword handle);
// Allocates space for a scoped handle.
uword AllocateScopedHandle() {
if (scoped_blocks_->IsFull()) {
SetupNextScopeBlock();
}
return scoped_blocks_->AllocateHandle();
}
bool IsEmpty() const {
if (zone_blocks_ != nullptr) return false;
if (first_scoped_block_.HandleCount() != 0) return false;
if (scoped_blocks_ != &first_scoped_block_) return false;
return true;
}
intptr_t ZoneHandlesCapacityInBytes() const {
intptr_t capacity = 0;
for (HandlesBlock* block = zone_blocks_; block != nullptr;
block = block->next_block()) {
capacity += sizeof(*block);
}
return capacity;
}
intptr_t ScopedHandlesCapacityInBytes() const {
intptr_t capacity = 0;
for (HandlesBlock* block = scoped_blocks_; block != nullptr;
block = block->next_block()) {
capacity += sizeof(*block);
}
return capacity;
}
protected:
// Returns a count of active handles (used for testing purposes).
int CountScopedHandles() const;
int CountZoneHandles() const;
// Returns true if passed in handle is a valid zone handle.
bool IsValidScopedHandle(uword handle) const;
bool IsValidZoneHandle(uword handle) const;
private:
// Base structure for managing blocks of handles.
// Handles are allocated in Chunks (each chunk holds kHandlesPerChunk
// handles). The chunk is uninitialized, subsequent requests for handles
// is allocated from the chunk until we run out space in the chunk,
// at this point another chunk is allocated. These chunks are chained
// together.
class HandlesBlock : public MallocAllocated {
public:
explicit HandlesBlock(HandlesBlock* next)
: next_block_(next), next_handle_slot_(0) {}
~HandlesBlock();
// Reinitializes handle block for reuse.
void ReInit();
// Returns true if the handle block is full.
bool IsFull() const {
return next_handle_slot_ >= (kHandleSizeInWords * kHandlesPerChunk);
}
// Returns true if passed in handle belongs to this block.
bool IsValidHandle(uword handle) const {
uword start = reinterpret_cast<uword>(data_);
uword end = start + (kHandleSizeInWords * kWordSize * kHandlesPerChunk);
return (start <= handle && handle < end);
}
// Allocates space for a handle in the data area.
uword AllocateHandle() {
ASSERT(!IsFull());
uword handle_address = reinterpret_cast<uword>(data_ + next_handle_slot_);
next_handle_slot_ += kHandleSizeInWords;
return handle_address;
}
// Visit all object pointers in the handle block.
void VisitObjectPointers(ObjectPointerVisitor* visitor);
// Visit all of the handles in the handle block.
void Visit(HandleVisitor* visitor);
#if defined(DEBUG)
// Zaps the free handle area to an uninitialized value.
void ZapFreeHandles();
#endif
// Returns number of active handles in the handle block.
int HandleCount() const;
// Accessors.
intptr_t next_handle_slot() const { return next_handle_slot_; }
void set_next_handle_slot(intptr_t next_handle_slot) {
next_handle_slot_ = next_handle_slot;
}
HandlesBlock* next_block() const { return next_block_; }
void set_next_block(HandlesBlock* next) { next_block_ = next; }
private:
HandlesBlock* next_block_; // Link to next block of handles.
intptr_t next_handle_slot_; // Next slot for allocation in current block.
uword data_[kHandleSizeInWords * kHandlesPerChunk]; // Handles area.
DISALLOW_COPY_AND_ASSIGN(HandlesBlock);
};
// Deletes all the allocated handle blocks.
void DeleteAll();
void DeleteHandleBlocks(HandlesBlock* blocks);
// Sets up the next handle block (allocates a new one if needed).
void SetupNextScopeBlock();
// Allocates space for a zone handle.
uword AllocateHandleInZone() {
if (zone_blocks_ == NULL || zone_blocks_->IsFull()) {
SetupNextZoneBlock();
}
return zone_blocks_->AllocateHandle();
}
// Allocates a new handle block and links it up.
void SetupNextZoneBlock();
#if defined(DEBUG)
// Verifies consistency of handle blocks after a scope is destroyed.
void VerifyScopedHandleState();
// Zaps the free scoped handles to an uninitialized value.
void ZapFreeScopedHandles();
#endif
HandlesBlock* zone_blocks_; // List of zone handles.
HandlesBlock first_scoped_block_; // First block of scoped handles.
HandlesBlock* scoped_blocks_; // List of scoped handles.
friend class HandleScope;
friend class Dart;
friend class IsolateObjectStore;
friend class ObjectStore;
friend class ThreadState;
DISALLOW_ALLOCATION();
DISALLOW_COPY_AND_ASSIGN(Handles);
};
static const int kVMHandleSizeInWords = 2;
static const int kVMHandlesPerChunk = 63;
static const int kOffsetOfRawPtr = kWordSize;
class VMHandles : public Handles<kVMHandleSizeInWords,
kVMHandlesPerChunk,
kOffsetOfRawPtr> {
public:
static const int kOffsetOfRawPtrInHandle = kOffsetOfRawPtr;
VMHandles()
: Handles<kVMHandleSizeInWords, kVMHandlesPerChunk, kOffsetOfRawPtr>() {
#if defined(DEBUG)
if (FLAG_trace_handles) {
OS::PrintErr("*** Starting a new VM handle block 0x%" Px "\n",
reinterpret_cast<intptr_t>(this));
}
#endif
}
~VMHandles() {
#if defined(DEBUG)
if (FLAG_trace_handles) {
OS::PrintErr("*** Handle Counts for 0x(%" Px
"):Zone = %d,Scoped = %d\n",
reinterpret_cast<intptr_t>(this), CountZoneHandles(),
CountScopedHandles());
OS::PrintErr("*** Deleting VM handle block 0x%" Px "\n",
reinterpret_cast<intptr_t>(this));
}
#endif
}
// Visit all object pointers stored in the various handles.
void VisitObjectPointers(ObjectPointerVisitor* visitor);
// Allocates a handle in the current handle scope of 'zone', which must be
// the current zone. This handle is valid only in the current handle scope
// and is destroyed when the current handle scope ends.
static uword AllocateHandle(Zone* zone);
// Allocates a handle in 'zone', which must be the current zone. This handle
// will be destroyed when the current zone is destroyed.
static uword AllocateZoneHandle(Zone* zone);
// Returns true if specified handle is a zone handle.
static bool IsZoneHandle(uword handle);
// Returns number of handles, these functions are used for testing purposes.
static int ScopedHandleCount();
static int ZoneHandleCount();
friend class ApiZone;
friend class ApiNativeScope;
};
// The class HandleScope is used to start a new handles scope in the code.
// It is used as follows:
// {
// HANDLESCOPE(thread);
// ....
// .....
// code that creates some scoped handles.
// ....
// }
class HandleScope : public StackResource {
public:
explicit HandleScope(ThreadState* thread);
~HandleScope();
private:
void Initialize();
VMHandles::HandlesBlock* saved_handle_block_; // Handle block at prev scope.
uword saved_handle_slot_; // Next available handle slot at previous scope.
#if defined(DEBUG)
HandleScope* link_; // Link to previous scope.
#endif
DISALLOW_IMPLICIT_CONSTRUCTORS(HandleScope);
};
// Macro to start a new Handle scope.
#define HANDLESCOPE(thread) \
dart::HandleScope vm_internal_handles_scope_(thread);
} // namespace dart
#endif // RUNTIME_VM_HANDLES_H_