9c4a322b08
This reverts commit 922ea3e9b6 in patchset 1, fix for assertion triggered in https://ci.chromium.org/b/8883214567628884960 in patchset 2, fix for deadlock around symbols table mutex in patchset 4.
Original commit description:
Speed up is achieved by sharing most of the dart code, object store
and class table between isolates in single isolate group. So
instead of bootstrapping isolate from the snapshot, isolate is
initialized by setting pointers to existing data structures already
set up for first isolate, and only few isolate-specific structures (moved
to newly introducted isolate_object_store) are created.
To allow for safe cross-isolate switchable call site, type test cache
mutations additional synchronization via RunWithStoppedMutators(that
relies on safepoints) was added.
Besides switchable call sites, no other mutation to the dart code is
done in AOT, which allows such sharing.
Bug: https://github.com/dart-lang/sdk/issues/37835
Bug: https://github.com/dart-lang/sdk/issues/36097
Change-Id: I655e337198214c9dfacbe76f7852b941b5a7e910
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/143462
Commit-Queue: Alexander Aprelev <aam@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
290 lines
9.6 KiB
C++
290 lines
9.6 KiB
C++
// 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 <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();
|
|
}
|
|
|
|
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<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 IsolateObjectStore;
|
|
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<kVMHandleSizeInWords,
|
|
kVMHandlesPerChunk,
|
|
kOffsetOfRawPtr> {
|
|
public:
|
|
static const int kOffsetOfRawPtrInHandle = kOffsetOfRawPtr;
|
|
|
|
VMHandles()
|
|
: Handles<kVMHandleSizeInWords, kVMHandlesPerChunk, kOffsetOfRawPtr>() {
|
|
if (FLAG_trace_handles) {
|
|
OS::PrintErr("*** Starting a new VM handle block 0x%" Px "\n",
|
|
reinterpret_cast<intptr_t>(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_
|