| // 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 VM_HANDLES_H_ |
| #define 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(isolate). |
| // Handles allocated in this manner are destroyed when the HandleScope |
| // object is destroyed. |
| // Code that uses scoped handles typically looks as follows: |
| // { |
| // HANDLESCOPE(isolate); |
| // 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. |
| // |
| // There are some critical regions of the Dart VM were we may need to manipulate |
| // raw dart objects directly. We use NOHANDLESCOPE to assert that we do not |
| // add code that will allocate new handles during this critical area. |
| // { |
| // NOHANDLESCOPE(isolate); |
| // .... |
| // .... |
| // } |
| |
| |
| // Forward declarations. |
| class ObjectPointerVisitor; |
| class Thread; |
| |
| DECLARE_FLAG(bool, verify_handles); |
| DECLARE_DEBUG_FLAG(bool, trace_handles); |
| |
| class HandleVisitor { |
| public: |
| explicit HandleVisitor(Thread* thread) : thread_(thread) {} |
| virtual ~HandleVisitor() {} |
| |
| Thread* thread() const { return thread_; } |
| |
| virtual void VisitHandle(uword addr) = 0; |
| |
| private: |
| Thread* thread_; |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(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); |
| |
| protected: |
| // Allocates space for a scoped handle. |
| uword AllocateScopedHandle() { |
| if (scoped_blocks_->IsFull()) { |
| SetupNextScopeBlock(); |
| } |
| return scoped_blocks_->AllocateHandle(); |
| } |
| |
| // 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: |
| explicit HandlesBlock(HandlesBlock* next) |
| : next_handle_slot_(0), |
| next_block_(next) { } |
| ~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: |
| uword data_[kHandleSizeInWords * kHandlesPerChunk]; // Handles area. |
| intptr_t next_handle_slot_; // Next slot for allocation in current block. |
| HandlesBlock* next_block_; // Link to next block of handles. |
| |
| 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 ObjectStore; |
| friend class Thread; |
| DISALLOW_ALLOCATION(); |
| DISALLOW_COPY_AND_ASSIGN(Handles); |
| }; |
| |
| |
| static const int kVMHandleSizeInWords = 2; |
| static const int kVMHandlesPerChunk = 64; |
| static const int kOffsetOfRawPtr = kWordSize; |
| class VMHandles : public Handles<kVMHandleSizeInWords, |
| kVMHandlesPerChunk, |
| kOffsetOfRawPtr> { |
| public: |
| static const int kOffsetOfRawPtrInHandle = kOffsetOfRawPtr; |
| |
| VMHandles() : Handles<kVMHandleSizeInWords, |
| kVMHandlesPerChunk, |
| kOffsetOfRawPtr>() { |
| #ifdef DEBUG |
| if (FLAG_trace_handles) { |
| OS::PrintErr("*** Starting a new VM handle block 0x%" Px "\n", |
| reinterpret_cast<intptr_t>(this)); |
| } |
| #endif |
| } |
| ~VMHandles(); |
| |
| // 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(Thread* 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(isolate_or_thread) \ |
| dart::HandleScope vm_internal_handles_scope_(isolate_or_thread); |
| |
| |
| // The class NoHandleScope is used in critical regions of the virtual machine |
| // code where raw dart object pointers are directly manipulated. |
| // This class asserts that we do not add code that will allocate new handles |
| // during this critical area. |
| // It is used as follows: |
| // { |
| // NOHANDLESCOPE(thread); |
| // .... |
| // ..... |
| // critical code that manipulates dart objects directly. |
| // .... |
| // } |
| #if defined(DEBUG) |
| class NoHandleScope : public StackResource { |
| public: |
| explicit NoHandleScope(Thread* thread); |
| ~NoHandleScope(); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(NoHandleScope); |
| }; |
| #else // defined(DEBUG) |
| class NoHandleScope : public ValueObject { |
| public: |
| explicit NoHandleScope(Thread* thread) { } |
| explicit NoHandleScope(Isolate* isolate) { } |
| NoHandleScope() { } |
| ~NoHandleScope() { } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(NoHandleScope); |
| }; |
| #endif // defined(DEBUG) |
| |
| // Macro to start a no handles scope in the code. |
| #define NOHANDLESCOPE(isolate_or_thread) \ |
| dart::NoHandleScope no_vm_internal_handles_scope_(isolate_or_thread); |
| |
| } // namespace dart |
| |
| #endif // VM_HANDLES_H_ |