// 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; DECLARE_FLAG(bool, verify_handles); template 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(); } 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: 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(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(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 ThreadState; 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 { public: static const int kOffsetOfRawPtrInHandle = kOffsetOfRawPtr; VMHandles() : Handles() { if (FLAG_trace_handles) { OS::PrintErr("*** Starting a new VM handle block 0x%" Px "\n", reinterpret_cast(this)); } } ~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(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_