| // 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_(nullptr), | |
| first_scoped_block_(nullptr), | |
| 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); | |
| #if defined(DEBUG) | |
| // Returns true if specified handle is a zone handle. | |
| static bool IsZoneHandle(uword handle); | |
| #endif | |
| // 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_ == nullptr || zone_blocks_->IsFull()) { | |
| SetupNextZoneBlock(); | |
| } | |
| return zone_blocks_->AllocateHandle(); | |
| } | |
| // Allocates a new handle block and links it up. | |
| void SetupNextZoneBlock(); | |
| 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); | |
| }; | |
| #if defined(DEBUG) | |
| static constexpr int kVMHandleSizeInWords = 3; | |
| static constexpr int kOffsetOfIsZoneHandle = 2; | |
| #else | |
| static constexpr int kVMHandleSizeInWords = 2; | |
| #endif | |
| static constexpr int kVMHandlesPerChunk = 63; | |
| static constexpr int kOffsetOfRawPtr = kWordSize; | |
| class VMHandles : public Handles<kVMHandleSizeInWords, | |
| kVMHandlesPerChunk, | |
| kOffsetOfRawPtr> { | |
| public: | |
| static constexpr 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); | |
| #if defined(DEBUG) | |
| // Returns true if specified handle is a zone handle. | |
| static bool IsZoneHandle(uword handle); | |
| #endif | |
| // 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_ |