mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 20:51:50 +00:00
b1c09ecd8f
Currently we have things called XPtr which are not what you get from ptr().
Old world:
handle->raw() returns RawObject* (tagged)
raw_obj->ptr() returns RawObject* (untagged)
After 6fe15f6df9
:
handle->raw() returns ObjectPtr
obj_ptr->ptr() returns ObjectLayout*
New world:
handle->ptr() returns ObjectPtr
obj_ptr->untag() returns UntaggedObject*
TEST=ci
Change-Id: I6c7f34014cf20737607caaf84979838300d12df2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/149367
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
889 lines
29 KiB
C++
889 lines
29 KiB
C++
// Copyright (c) 2012, 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_DART_API_STATE_H_
|
|
#define RUNTIME_VM_DART_API_STATE_H_
|
|
|
|
#include "include/dart_api.h"
|
|
|
|
#include "platform/utils.h"
|
|
#include "vm/bitfield.h"
|
|
#include "vm/dart_api_impl.h"
|
|
#include "vm/flags.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/handles.h"
|
|
#include "vm/heap/weak_table.h"
|
|
#include "vm/object.h"
|
|
#include "vm/os.h"
|
|
#include "vm/os_thread.h"
|
|
#include "vm/raw_object.h"
|
|
#include "vm/thread_pool.h"
|
|
#include "vm/visitor.h"
|
|
|
|
#include "vm/handles_impl.h"
|
|
|
|
namespace dart {
|
|
|
|
// Implementation of Zone support for very fast allocation of small chunks
|
|
// of memory. The chunks cannot be deallocated individually, but instead
|
|
// zones support deallocating all chunks in one fast operation when the
|
|
// scope is exited.
|
|
class ApiZone {
|
|
public:
|
|
// Create an empty zone.
|
|
ApiZone() : zone_() {
|
|
Thread* thread = Thread::Current();
|
|
Zone* zone = thread != NULL ? thread->zone() : NULL;
|
|
zone_.Link(zone);
|
|
if (thread != NULL) {
|
|
thread->set_zone(&zone_);
|
|
}
|
|
if (FLAG_trace_zones) {
|
|
OS::PrintErr("*** Starting a new Api zone 0x%" Px "(0x%" Px ")\n",
|
|
reinterpret_cast<intptr_t>(this),
|
|
reinterpret_cast<intptr_t>(&zone_));
|
|
}
|
|
}
|
|
|
|
// Delete all memory associated with the zone.
|
|
~ApiZone() {
|
|
Thread* thread = Thread::Current();
|
|
#if defined(DEBUG)
|
|
if (thread == NULL) {
|
|
ASSERT(zone_.handles()->CountScopedHandles() == 0);
|
|
ASSERT(zone_.handles()->CountZoneHandles() == 0);
|
|
}
|
|
#endif
|
|
if ((thread != NULL) && (thread->zone() == &zone_)) {
|
|
thread->set_zone(zone_.previous_);
|
|
}
|
|
if (FLAG_trace_zones) {
|
|
OS::PrintErr("*** Deleting Api zone 0x%" Px "(0x%" Px ")\n",
|
|
reinterpret_cast<intptr_t>(this),
|
|
reinterpret_cast<intptr_t>(&zone_));
|
|
}
|
|
}
|
|
|
|
// Allocates an array sized to hold 'len' elements of type
|
|
// 'ElementType'. Checks for integer overflow when performing the
|
|
// size computation.
|
|
template <class ElementType>
|
|
ElementType* Alloc(intptr_t len) {
|
|
return zone_.Alloc<ElementType>(len);
|
|
}
|
|
|
|
// Allocates an array sized to hold 'len' elements of type
|
|
// 'ElementType'. The new array is initialized from the memory of
|
|
// 'old_array' up to 'old_len'.
|
|
template <class ElementType>
|
|
ElementType* Realloc(ElementType* old_array,
|
|
intptr_t old_len,
|
|
intptr_t new_len) {
|
|
return zone_.Realloc<ElementType>(old_array, old_len, new_len);
|
|
}
|
|
|
|
// Allocates 'size' bytes of memory in the zone; expands the zone by
|
|
// allocating new segments of memory on demand using 'new'.
|
|
//
|
|
// It is preferred to use Alloc<T>() instead, as that function can
|
|
// check for integer overflow. If you use AllocUnsafe, you are
|
|
// responsible for avoiding integer overflow yourself.
|
|
uword AllocUnsafe(intptr_t size) { return zone_.AllocUnsafe(size); }
|
|
|
|
// Compute the total size of this zone. This includes wasted space that is
|
|
// due to internal fragmentation in the segments.
|
|
intptr_t SizeInBytes() const { return zone_.SizeInBytes(); }
|
|
|
|
Zone* GetZone() { return &zone_; }
|
|
|
|
void Reinit(Thread* thread) {
|
|
if (thread == NULL) {
|
|
zone_.Link(NULL);
|
|
} else {
|
|
zone_.Link(thread->zone());
|
|
thread->set_zone(&zone_);
|
|
}
|
|
}
|
|
|
|
void Reset(Thread* thread) {
|
|
if ((thread != NULL) && (thread->zone() == &zone_)) {
|
|
thread->set_zone(zone_.previous_);
|
|
}
|
|
zone_.DeleteAll();
|
|
}
|
|
|
|
private:
|
|
Zone zone_;
|
|
|
|
template <typename T>
|
|
friend class ApiGrowableArray;
|
|
DISALLOW_COPY_AND_ASSIGN(ApiZone);
|
|
};
|
|
|
|
// Implementation of local handles which are handed out from every
|
|
// dart API call, these handles are valid only in the present scope
|
|
// and are destroyed when a Dart_ExitScope() is called.
|
|
class LocalHandle {
|
|
public:
|
|
// Accessors.
|
|
ObjectPtr ptr() const { return ptr_; }
|
|
void set_ptr(ObjectPtr ptr) { ptr_ = ptr; }
|
|
static intptr_t ptr_offset() { return OFFSET_OF(LocalHandle, ptr_); }
|
|
|
|
Dart_Handle apiHandle() { return reinterpret_cast<Dart_Handle>(this); }
|
|
|
|
private:
|
|
LocalHandle() {}
|
|
~LocalHandle() {}
|
|
|
|
ObjectPtr ptr_;
|
|
DISALLOW_ALLOCATION(); // Allocated through AllocateHandle methods.
|
|
DISALLOW_COPY_AND_ASSIGN(LocalHandle);
|
|
};
|
|
|
|
// A distinguished callback which indicates that a persistent handle
|
|
// should not be deleted from the dart api.
|
|
void ProtectedHandleCallback(void* peer);
|
|
|
|
// Implementation of persistent handles which are handed out through the
|
|
// dart API.
|
|
class PersistentHandle {
|
|
public:
|
|
// Accessors.
|
|
ObjectPtr ptr() const { return ptr_; }
|
|
void set_ptr(ObjectPtr ref) { ptr_ = ref; }
|
|
void set_ptr(const LocalHandle& ref) { ptr_ = ref.ptr(); }
|
|
void set_ptr(const Object& object) { ptr_ = object.ptr(); }
|
|
ObjectPtr* raw_addr() { return &ptr_; }
|
|
|
|
Dart_PersistentHandle apiHandle() {
|
|
return reinterpret_cast<Dart_PersistentHandle>(this);
|
|
}
|
|
|
|
static intptr_t ptr_offset() { return OFFSET_OF(PersistentHandle, ptr_); }
|
|
|
|
static PersistentHandle* Cast(Dart_PersistentHandle handle);
|
|
|
|
private:
|
|
friend class PersistentHandles;
|
|
|
|
PersistentHandle() {}
|
|
~PersistentHandle() {}
|
|
|
|
// Overload the ptr_ field as a next pointer when adding freed
|
|
// handles to the free list.
|
|
PersistentHandle* Next() {
|
|
return reinterpret_cast<PersistentHandle*>(static_cast<uword>(ptr_));
|
|
}
|
|
void SetNext(PersistentHandle* free_list) {
|
|
ptr_ = static_cast<ObjectPtr>(reinterpret_cast<uword>(free_list));
|
|
ASSERT(!ptr_->IsHeapObject());
|
|
}
|
|
void FreeHandle(PersistentHandle* free_list) { SetNext(free_list); }
|
|
|
|
ObjectPtr ptr_;
|
|
DISALLOW_ALLOCATION(); // Allocated through AllocateHandle methods.
|
|
DISALLOW_COPY_AND_ASSIGN(PersistentHandle);
|
|
};
|
|
|
|
// Implementation of persistent handles which are handed out through the
|
|
// dart API.
|
|
class FinalizablePersistentHandle {
|
|
public:
|
|
static FinalizablePersistentHandle* New(IsolateGroup* isolate_group,
|
|
const Object& object,
|
|
void* peer,
|
|
Dart_HandleFinalizer callback,
|
|
intptr_t external_size,
|
|
bool auto_delete);
|
|
|
|
// Accessors.
|
|
ObjectPtr ptr() const { return ptr_; }
|
|
ObjectPtr* ptr_addr() { return &ptr_; }
|
|
static intptr_t ptr_offset() {
|
|
return OFFSET_OF(FinalizablePersistentHandle, ptr_);
|
|
}
|
|
void* peer() const { return peer_; }
|
|
Dart_HandleFinalizer callback() const { return callback_; }
|
|
Dart_WeakPersistentHandle ApiWeakPersistentHandle() {
|
|
return reinterpret_cast<Dart_WeakPersistentHandle>(this);
|
|
}
|
|
Dart_FinalizableHandle ApiFinalizableHandle() {
|
|
return reinterpret_cast<Dart_FinalizableHandle>(this);
|
|
}
|
|
|
|
bool auto_delete() const { return auto_delete_; }
|
|
|
|
bool IsFinalizedNotFreed() const {
|
|
return ptr_ == static_cast<ObjectPtr>(reinterpret_cast<uword>(this));
|
|
}
|
|
|
|
intptr_t external_size() const {
|
|
return ExternalSizeInWordsBits::decode(external_data_) * kWordSize;
|
|
}
|
|
|
|
void SetExternalSize(intptr_t size, IsolateGroup* isolate_group) {
|
|
ASSERT(size >= 0);
|
|
set_external_size(size);
|
|
if (SpaceForExternal() == Heap::kNew) {
|
|
SetExternalNewSpaceBit();
|
|
}
|
|
isolate_group->heap()->AllocatedExternal(external_size(),
|
|
SpaceForExternal());
|
|
}
|
|
void UpdateExternalSize(intptr_t size, IsolateGroup* isolate_group) {
|
|
ASSERT(size >= 0);
|
|
intptr_t old_size = external_size();
|
|
set_external_size(size);
|
|
if (size > old_size) {
|
|
isolate_group->heap()->AllocatedExternal(size - old_size,
|
|
SpaceForExternal());
|
|
} else {
|
|
isolate_group->heap()->FreedExternal(old_size - size, SpaceForExternal());
|
|
}
|
|
}
|
|
|
|
// Called when the referent becomes unreachable.
|
|
void UpdateUnreachable(IsolateGroup* isolate_group) {
|
|
EnsureFreedExternal(isolate_group);
|
|
Finalize(isolate_group, this);
|
|
}
|
|
|
|
// Called when the referent has moved, potentially between generations.
|
|
void UpdateRelocated(IsolateGroup* isolate_group) {
|
|
if (IsSetNewSpaceBit() && (SpaceForExternal() == Heap::kOld)) {
|
|
isolate_group->heap()->PromotedExternal(external_size());
|
|
ClearExternalNewSpaceBit();
|
|
}
|
|
}
|
|
|
|
// Idempotent. Called when the handle is explicitly deleted or the
|
|
// referent becomes unreachable.
|
|
void EnsureFreedExternal(IsolateGroup* isolate_group) {
|
|
isolate_group->heap()->FreedExternal(external_size(), SpaceForExternal());
|
|
set_external_size(0);
|
|
}
|
|
|
|
static FinalizablePersistentHandle* Cast(Dart_WeakPersistentHandle handle);
|
|
static FinalizablePersistentHandle* Cast(Dart_FinalizableHandle handle);
|
|
|
|
private:
|
|
enum {
|
|
kExternalNewSpaceBit = 0,
|
|
kExternalSizeBits = 1,
|
|
kExternalSizeBitsSize = (kBitsPerWord - 1),
|
|
};
|
|
|
|
// This part of external_data_ is the number of externally allocated bytes.
|
|
class ExternalSizeInWordsBits : public BitField<uword,
|
|
intptr_t,
|
|
kExternalSizeBits,
|
|
kExternalSizeBitsSize> {};
|
|
// This bit of external_data_ is true if the referent was created in new
|
|
// space and UpdateRelocated has not yet detected any promotion.
|
|
class ExternalNewSpaceBit
|
|
: public BitField<uword, bool, kExternalNewSpaceBit, 1> {};
|
|
|
|
friend class FinalizablePersistentHandles;
|
|
|
|
FinalizablePersistentHandle()
|
|
: ptr_(nullptr), peer_(NULL), external_data_(0), callback_(NULL) {}
|
|
~FinalizablePersistentHandle() {}
|
|
|
|
static void Finalize(IsolateGroup* isolate_group,
|
|
FinalizablePersistentHandle* handle);
|
|
|
|
// Overload the ptr_ field as a next pointer when adding freed
|
|
// handles to the free list.
|
|
FinalizablePersistentHandle* Next() {
|
|
return reinterpret_cast<FinalizablePersistentHandle*>(
|
|
static_cast<uword>(ptr_));
|
|
}
|
|
void SetNext(FinalizablePersistentHandle* free_list) {
|
|
ptr_ = static_cast<ObjectPtr>(reinterpret_cast<uword>(free_list));
|
|
ASSERT(!ptr_->IsHeapObject());
|
|
}
|
|
|
|
void SetFinalizedNotFreed() {
|
|
// `handle->raw_ != Object::null()` or the GC will finalize again.
|
|
SetNext(this);
|
|
}
|
|
|
|
void FreeHandle(FinalizablePersistentHandle* free_list) {
|
|
Clear();
|
|
SetNext(free_list);
|
|
}
|
|
|
|
void Clear() {
|
|
ptr_ = Object::null();
|
|
peer_ = nullptr;
|
|
external_data_ = 0;
|
|
callback_ = nullptr;
|
|
auto_delete_ = false;
|
|
}
|
|
|
|
void set_ptr(ObjectPtr raw) { ptr_ = raw; }
|
|
void set_ptr(const LocalHandle& ref) { ptr_ = ref.ptr(); }
|
|
void set_ptr(const Object& object) { ptr_ = object.ptr(); }
|
|
|
|
void set_peer(void* peer) { peer_ = peer; }
|
|
|
|
void set_callback(Dart_HandleFinalizer callback) { callback_ = callback; }
|
|
|
|
void set_auto_delete(bool auto_delete) { auto_delete_ = auto_delete; }
|
|
|
|
void set_external_size(intptr_t size) {
|
|
intptr_t size_in_words = Utils::RoundUp(size, kObjectAlignment) / kWordSize;
|
|
ASSERT(ExternalSizeInWordsBits::is_valid(size_in_words));
|
|
external_data_ =
|
|
ExternalSizeInWordsBits::update(size_in_words, external_data_);
|
|
}
|
|
|
|
bool IsSetNewSpaceBit() const {
|
|
return ExternalNewSpaceBit::decode(external_data_);
|
|
}
|
|
|
|
void SetExternalNewSpaceBit() {
|
|
external_data_ = ExternalNewSpaceBit::update(true, external_data_);
|
|
}
|
|
|
|
void ClearExternalNewSpaceBit() {
|
|
external_data_ = ExternalNewSpaceBit::update(false, external_data_);
|
|
}
|
|
|
|
// Returns the space to charge for the external size.
|
|
Heap::Space SpaceForExternal() const {
|
|
// Non-heap and VM-heap objects count as old space here.
|
|
return ptr_->IsSmiOrOldObject() ? Heap::kOld : Heap::kNew;
|
|
}
|
|
|
|
ObjectPtr ptr_;
|
|
void* peer_;
|
|
uword external_data_;
|
|
Dart_HandleFinalizer callback_;
|
|
bool auto_delete_;
|
|
|
|
DISALLOW_ALLOCATION(); // Allocated through AllocateHandle methods.
|
|
DISALLOW_COPY_AND_ASSIGN(FinalizablePersistentHandle);
|
|
};
|
|
|
|
// Local handles repository structure.
|
|
static const int kLocalHandleSizeInWords = sizeof(LocalHandle) / kWordSize;
|
|
static const int kLocalHandlesPerChunk = 64;
|
|
static const int kOffsetOfRawPtrInLocalHandle = 0;
|
|
class LocalHandles : Handles<kLocalHandleSizeInWords,
|
|
kLocalHandlesPerChunk,
|
|
kOffsetOfRawPtrInLocalHandle> {
|
|
public:
|
|
LocalHandles()
|
|
: Handles<kLocalHandleSizeInWords,
|
|
kLocalHandlesPerChunk,
|
|
kOffsetOfRawPtrInLocalHandle>() {
|
|
if (FLAG_trace_handles) {
|
|
OS::PrintErr("*** Starting a new Local handle block 0x%" Px "\n",
|
|
reinterpret_cast<intptr_t>(this));
|
|
}
|
|
}
|
|
~LocalHandles() {
|
|
if (FLAG_trace_handles) {
|
|
OS::PrintErr("*** Handle Counts for 0x(%" Px "):Scoped = %d\n",
|
|
reinterpret_cast<intptr_t>(this), CountHandles());
|
|
OS::PrintErr("*** Deleting Local handle block 0x%" Px "\n",
|
|
reinterpret_cast<intptr_t>(this));
|
|
}
|
|
}
|
|
|
|
// Visit all object pointers stored in the various handles.
|
|
void VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
|
visitor->set_gc_root_type("local handle");
|
|
Handles<kLocalHandleSizeInWords, kLocalHandlesPerChunk,
|
|
kOffsetOfRawPtrInLocalHandle>::VisitObjectPointers(visitor);
|
|
visitor->clear_gc_root_type();
|
|
}
|
|
|
|
// Reset the local handles block for reuse.
|
|
void Reset() {
|
|
Handles<kLocalHandleSizeInWords, kLocalHandlesPerChunk,
|
|
kOffsetOfRawPtrInLocalHandle>::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.
|
|
LocalHandle* AllocateHandle() {
|
|
return reinterpret_cast<LocalHandle*>(AllocateScopedHandle());
|
|
}
|
|
|
|
// Validate if passed in handle is a Local Handle.
|
|
bool IsValidHandle(Dart_Handle object) const {
|
|
return IsValidScopedHandle(reinterpret_cast<uword>(object));
|
|
}
|
|
|
|
// Returns a count of active handles (used for testing purposes).
|
|
int CountHandles() const { return CountScopedHandles(); }
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(LocalHandles);
|
|
};
|
|
|
|
// Persistent handles repository structure.
|
|
static const int kPersistentHandleSizeInWords =
|
|
sizeof(PersistentHandle) / kWordSize;
|
|
static const int kPersistentHandlesPerChunk = 64;
|
|
static const int kOffsetOfRawPtrInPersistentHandle = 0;
|
|
class PersistentHandles : Handles<kPersistentHandleSizeInWords,
|
|
kPersistentHandlesPerChunk,
|
|
kOffsetOfRawPtrInPersistentHandle> {
|
|
public:
|
|
PersistentHandles()
|
|
: Handles<kPersistentHandleSizeInWords,
|
|
kPersistentHandlesPerChunk,
|
|
kOffsetOfRawPtrInPersistentHandle>(),
|
|
free_list_(NULL) {
|
|
if (FLAG_trace_handles) {
|
|
OS::PrintErr("*** Starting a new Persistent handle block 0x%" Px "\n",
|
|
reinterpret_cast<intptr_t>(this));
|
|
}
|
|
}
|
|
~PersistentHandles() {
|
|
free_list_ = NULL;
|
|
if (FLAG_trace_handles) {
|
|
OS::PrintErr("*** Handle Counts for 0x(%" Px "):Scoped = %d\n",
|
|
reinterpret_cast<intptr_t>(this), CountHandles());
|
|
OS::PrintErr("*** Deleting Persistent handle block 0x%" Px "\n",
|
|
reinterpret_cast<intptr_t>(this));
|
|
}
|
|
}
|
|
|
|
// Accessors.
|
|
PersistentHandle* free_list() const { return free_list_; }
|
|
void set_free_list(PersistentHandle* value) { free_list_ = value; }
|
|
|
|
// Visit all object pointers stored in the various handles.
|
|
void VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
|
visitor->set_gc_root_type("persistent handle");
|
|
Handles<kPersistentHandleSizeInWords, kPersistentHandlesPerChunk,
|
|
kOffsetOfRawPtrInPersistentHandle>::VisitObjectPointers(visitor);
|
|
visitor->clear_gc_root_type();
|
|
}
|
|
|
|
// Visit all the handles.
|
|
void Visit(HandleVisitor* visitor) {
|
|
Handles<kPersistentHandleSizeInWords, kPersistentHandlesPerChunk,
|
|
kOffsetOfRawPtrInPersistentHandle>::Visit(visitor);
|
|
}
|
|
|
|
// Allocates a persistent handle, these have to be destroyed explicitly
|
|
// by calling FreeHandle.
|
|
PersistentHandle* AllocateHandle() {
|
|
PersistentHandle* handle;
|
|
if (free_list_ != NULL) {
|
|
handle = free_list_;
|
|
free_list_ = handle->Next();
|
|
} else {
|
|
handle = reinterpret_cast<PersistentHandle*>(AllocateScopedHandle());
|
|
}
|
|
handle->set_ptr(Object::null());
|
|
return handle;
|
|
}
|
|
|
|
void FreeHandle(PersistentHandle* handle) {
|
|
handle->FreeHandle(free_list());
|
|
set_free_list(handle);
|
|
}
|
|
|
|
// Validate if passed in handle is a Persistent Handle.
|
|
bool IsValidHandle(Dart_PersistentHandle object) const {
|
|
return IsValidScopedHandle(reinterpret_cast<uword>(object));
|
|
}
|
|
|
|
bool IsFreeHandle(Dart_PersistentHandle object) const {
|
|
PersistentHandle* handle = free_list_;
|
|
while (handle != NULL) {
|
|
if (handle == reinterpret_cast<PersistentHandle*>(object)) {
|
|
return true;
|
|
}
|
|
handle = handle->Next();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Returns a count of active handles (used for testing purposes).
|
|
int CountHandles() const { return CountScopedHandles(); }
|
|
|
|
private:
|
|
PersistentHandle* free_list_;
|
|
DISALLOW_COPY_AND_ASSIGN(PersistentHandles);
|
|
};
|
|
|
|
// Finalizable persistent handles repository structure.
|
|
static const int kFinalizablePersistentHandleSizeInWords =
|
|
sizeof(FinalizablePersistentHandle) / kWordSize;
|
|
static const int kFinalizablePersistentHandlesPerChunk = 64;
|
|
static const int kOffsetOfRawPtrInFinalizablePersistentHandle = 0;
|
|
class FinalizablePersistentHandles
|
|
: Handles<kFinalizablePersistentHandleSizeInWords,
|
|
kFinalizablePersistentHandlesPerChunk,
|
|
kOffsetOfRawPtrInFinalizablePersistentHandle> {
|
|
public:
|
|
FinalizablePersistentHandles()
|
|
: Handles<kFinalizablePersistentHandleSizeInWords,
|
|
kFinalizablePersistentHandlesPerChunk,
|
|
kOffsetOfRawPtrInFinalizablePersistentHandle>(),
|
|
free_list_(NULL) {}
|
|
~FinalizablePersistentHandles() { free_list_ = NULL; }
|
|
|
|
// Accessors.
|
|
FinalizablePersistentHandle* free_list() const { return free_list_; }
|
|
void set_free_list(FinalizablePersistentHandle* value) { free_list_ = value; }
|
|
|
|
// Visit all handles stored in the various handle blocks.
|
|
void VisitHandles(HandleVisitor* visitor) {
|
|
Handles<kFinalizablePersistentHandleSizeInWords,
|
|
kFinalizablePersistentHandlesPerChunk,
|
|
kOffsetOfRawPtrInFinalizablePersistentHandle>::Visit(visitor);
|
|
}
|
|
|
|
// Visit all object pointers stored in the various handles.
|
|
void VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
|
visitor->set_gc_root_type("weak persistent handle");
|
|
Handles<kFinalizablePersistentHandleSizeInWords,
|
|
kFinalizablePersistentHandlesPerChunk,
|
|
kOffsetOfRawPtrInFinalizablePersistentHandle>::
|
|
VisitObjectPointers(visitor);
|
|
visitor->clear_gc_root_type();
|
|
}
|
|
|
|
// Allocates a persistent handle, these have to be destroyed explicitly
|
|
// by calling FreeHandle.
|
|
FinalizablePersistentHandle* AllocateHandle() {
|
|
FinalizablePersistentHandle* handle;
|
|
if (free_list_ != NULL) {
|
|
handle = free_list_;
|
|
free_list_ = handle->Next();
|
|
handle->set_ptr(Object::null());
|
|
return handle;
|
|
}
|
|
|
|
handle =
|
|
reinterpret_cast<FinalizablePersistentHandle*>(AllocateScopedHandle());
|
|
handle->Clear();
|
|
return handle;
|
|
}
|
|
|
|
void ClearHandle(FinalizablePersistentHandle* handle) {
|
|
handle->Clear();
|
|
handle->SetFinalizedNotFreed();
|
|
}
|
|
|
|
void FreeHandle(FinalizablePersistentHandle* handle) {
|
|
handle->FreeHandle(free_list());
|
|
set_free_list(handle);
|
|
}
|
|
|
|
// Validate if passed in handle is a Persistent Handle.
|
|
bool IsValidHandle(Dart_WeakPersistentHandle object) const {
|
|
return IsValidScopedHandle(reinterpret_cast<uword>(object));
|
|
}
|
|
|
|
bool IsValidHandle(Dart_FinalizableHandle object) const {
|
|
return IsValidScopedHandle(reinterpret_cast<uword>(object));
|
|
}
|
|
|
|
bool IsFreeHandle(Dart_WeakPersistentHandle object) const {
|
|
FinalizablePersistentHandle* handle = free_list_;
|
|
while (handle != NULL) {
|
|
if (handle == reinterpret_cast<FinalizablePersistentHandle*>(object)) {
|
|
return true;
|
|
}
|
|
handle = handle->Next();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Returns a count of active handles (used for testing purposes).
|
|
int CountHandles() const { return CountScopedHandles(); }
|
|
|
|
private:
|
|
FinalizablePersistentHandle* free_list_;
|
|
DISALLOW_COPY_AND_ASSIGN(FinalizablePersistentHandles);
|
|
};
|
|
|
|
// Structure used for the implementation of local scopes used in dart_api.
|
|
// These local scopes manage handles and memory allocated in the scope.
|
|
class ApiLocalScope {
|
|
public:
|
|
ApiLocalScope(ApiLocalScope* previous, uword stack_marker)
|
|
: previous_(previous), stack_marker_(stack_marker) {}
|
|
~ApiLocalScope() { previous_ = NULL; }
|
|
|
|
// Reinit the ApiLocalScope to new values.
|
|
void Reinit(Thread* thread, ApiLocalScope* previous, uword stack_marker) {
|
|
previous_ = previous;
|
|
stack_marker_ = stack_marker;
|
|
zone_.Reinit(thread);
|
|
}
|
|
|
|
// Reset the ApiLocalScope so that it can be reused again.
|
|
void Reset(Thread* thread) {
|
|
local_handles_.Reset();
|
|
zone_.Reset(thread);
|
|
previous_ = NULL;
|
|
stack_marker_ = 0;
|
|
}
|
|
|
|
// Accessors.
|
|
ApiLocalScope* previous() const { return previous_; }
|
|
uword stack_marker() const { return stack_marker_; }
|
|
void set_previous(ApiLocalScope* value) { previous_ = value; }
|
|
LocalHandles* local_handles() { return &local_handles_; }
|
|
Zone* zone() { return zone_.GetZone(); }
|
|
|
|
private:
|
|
ApiLocalScope* previous_;
|
|
uword stack_marker_;
|
|
LocalHandles local_handles_;
|
|
ApiZone zone_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ApiLocalScope);
|
|
};
|
|
|
|
class ApiNativeScope {
|
|
public:
|
|
ApiNativeScope() {
|
|
// Currently no support for nesting native scopes.
|
|
ASSERT(Current() == NULL);
|
|
OSThread::SetThreadLocal(Api::api_native_key_,
|
|
reinterpret_cast<uword>(this));
|
|
// We manually increment the memory usage counter since there is memory
|
|
// initially allocated within the zone on creation.
|
|
IncrementNativeScopeMemoryCapacity(zone_.GetZone()->CapacityInBytes());
|
|
}
|
|
|
|
~ApiNativeScope() {
|
|
ASSERT(Current() == this);
|
|
OSThread::SetThreadLocal(Api::api_native_key_, 0);
|
|
// We must also manually decrement the memory usage counter since the native
|
|
// is still holding it's initial memory and ~Zone() won't be able to
|
|
// determine which memory usage counter to decrement.
|
|
DecrementNativeScopeMemoryCapacity(zone_.GetZone()->CapacityInBytes());
|
|
}
|
|
|
|
static inline ApiNativeScope* Current() {
|
|
return reinterpret_cast<ApiNativeScope*>(
|
|
OSThread::GetThreadLocal(Api::api_native_key_));
|
|
}
|
|
|
|
static uintptr_t current_memory_usage() { return current_memory_usage_; }
|
|
|
|
static void IncrementNativeScopeMemoryCapacity(intptr_t size) {
|
|
current_memory_usage_.fetch_add(size);
|
|
}
|
|
|
|
static void DecrementNativeScopeMemoryCapacity(intptr_t size) {
|
|
current_memory_usage_.fetch_sub(size);
|
|
}
|
|
|
|
Zone* zone() {
|
|
Zone* result = zone_.GetZone();
|
|
ASSERT(result->handles()->CountScopedHandles() == 0);
|
|
ASSERT(result->handles()->CountZoneHandles() == 0);
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
// The current total memory usage within ApiNativeScopes.
|
|
static RelaxedAtomic<intptr_t> current_memory_usage_;
|
|
|
|
ApiZone zone_;
|
|
};
|
|
|
|
// Api growable arrays use a zone for allocation. The constructor
|
|
// picks the zone from the current isolate if in an isolate
|
|
// environment. When outside an isolate environment it picks the zone
|
|
// from the current native scope.
|
|
template <typename T>
|
|
class ApiGrowableArray : public BaseGrowableArray<T, ValueObject, Zone> {
|
|
public:
|
|
explicit ApiGrowableArray(int initial_capacity)
|
|
: BaseGrowableArray<T, ValueObject, Zone>(
|
|
initial_capacity,
|
|
ApiNativeScope::Current()->zone()) {}
|
|
ApiGrowableArray()
|
|
: BaseGrowableArray<T, ValueObject, Zone>(
|
|
ApiNativeScope::Current()->zone()) {}
|
|
ApiGrowableArray(intptr_t initial_capacity, Zone* zone)
|
|
: BaseGrowableArray<T, ValueObject, Zone>(initial_capacity, zone) {}
|
|
};
|
|
|
|
// Implementation of the API State used in dart api for maintaining
|
|
// local scopes, persistent handles etc. These are setup on a per isolate
|
|
// group basis and destroyed when the isolate group is shutdown.
|
|
class ApiState {
|
|
public:
|
|
ApiState()
|
|
: persistent_handles_(),
|
|
weak_persistent_handles_(),
|
|
null_(NULL),
|
|
true_(NULL),
|
|
false_(NULL),
|
|
acquired_error_(NULL) {}
|
|
~ApiState() {
|
|
if (null_ != NULL) {
|
|
persistent_handles_.FreeHandle(null_);
|
|
null_ = NULL;
|
|
}
|
|
if (true_ != NULL) {
|
|
persistent_handles_.FreeHandle(true_);
|
|
true_ = NULL;
|
|
}
|
|
if (false_ != NULL) {
|
|
persistent_handles_.FreeHandle(false_);
|
|
false_ = NULL;
|
|
}
|
|
if (acquired_error_ != NULL) {
|
|
persistent_handles_.FreeHandle(acquired_error_);
|
|
acquired_error_ = NULL;
|
|
}
|
|
}
|
|
|
|
void MergeOtherApiState(ApiState* api_state);
|
|
|
|
void VisitObjectPointersUnlocked(ObjectPointerVisitor* visitor) {
|
|
persistent_handles_.VisitObjectPointers(visitor);
|
|
if (visitor->visit_weak_persistent_handles()) {
|
|
weak_persistent_handles_.VisitObjectPointers(visitor);
|
|
}
|
|
}
|
|
|
|
void VisitWeakHandlesUnlocked(HandleVisitor* visitor) {
|
|
weak_persistent_handles_.VisitHandles(visitor);
|
|
}
|
|
|
|
PersistentHandle* AllocatePersistentHandle() {
|
|
MutexLocker ml(&mutex_);
|
|
return persistent_handles_.AllocateHandle();
|
|
}
|
|
void FreePersistentHandle(PersistentHandle* ref) {
|
|
MutexLocker ml(&mutex_);
|
|
persistent_handles_.FreeHandle(ref);
|
|
}
|
|
|
|
FinalizablePersistentHandle* AllocateWeakPersistentHandle() {
|
|
MutexLocker ml(&mutex_);
|
|
return weak_persistent_handles_.AllocateHandle();
|
|
}
|
|
void ClearWeakPersistentHandle(FinalizablePersistentHandle* weak_ref) {
|
|
MutexLocker ml(&mutex_);
|
|
weak_persistent_handles_.ClearHandle(weak_ref);
|
|
}
|
|
void FreeWeakPersistentHandle(FinalizablePersistentHandle* weak_ref) {
|
|
MutexLocker ml(&mutex_);
|
|
weak_persistent_handles_.FreeHandle(weak_ref);
|
|
}
|
|
|
|
bool IsValidPersistentHandle(Dart_PersistentHandle object) {
|
|
MutexLocker ml(&mutex_);
|
|
return persistent_handles_.IsValidHandle(object);
|
|
}
|
|
|
|
bool IsActivePersistentHandle(Dart_PersistentHandle object) {
|
|
MutexLocker ml(&mutex_);
|
|
return persistent_handles_.IsValidHandle(object) &&
|
|
!persistent_handles_.IsFreeHandle(object);
|
|
}
|
|
|
|
bool IsValidWeakPersistentHandle(Dart_WeakPersistentHandle object) {
|
|
MutexLocker ml(&mutex_);
|
|
return weak_persistent_handles_.IsValidHandle(object);
|
|
}
|
|
|
|
bool IsValidFinalizableHandle(Dart_FinalizableHandle object) {
|
|
MutexLocker ml(&mutex_);
|
|
return weak_persistent_handles_.IsValidHandle(object);
|
|
}
|
|
|
|
bool IsActiveWeakPersistentHandle(Dart_WeakPersistentHandle object) {
|
|
MutexLocker ml(&mutex_);
|
|
return weak_persistent_handles_.IsValidHandle(object) &&
|
|
!weak_persistent_handles_.IsFreeHandle(object);
|
|
}
|
|
|
|
bool IsProtectedHandle(PersistentHandle* object) {
|
|
MutexLocker ml(&mutex_);
|
|
if (object == NULL) return false;
|
|
return object == null_ || object == true_ || object == false_;
|
|
}
|
|
|
|
int CountPersistentHandles() {
|
|
MutexLocker ml(&mutex_);
|
|
return persistent_handles_.CountHandles();
|
|
}
|
|
|
|
PersistentHandle* AcquiredError() {
|
|
// The ApiError pre-allocated in the "vm-isolate" since we will not be able
|
|
// to allocate it when the error actually occurs.
|
|
// When the error occurs there will be outstanding acquires to internal
|
|
// data pointers making it unsafe to allocate objects on the dart heap.
|
|
MutexLocker ml(&mutex_);
|
|
if (acquired_error_ == nullptr) {
|
|
acquired_error_ = persistent_handles_.AllocateHandle();
|
|
acquired_error_->set_ptr(ApiError::typed_data_acquire_error());
|
|
}
|
|
return acquired_error_;
|
|
}
|
|
|
|
void RunWithLockedPersistentHandles(
|
|
std::function<void(PersistentHandles&)> fun) {
|
|
MutexLocker ml(&mutex_);
|
|
fun(persistent_handles_);
|
|
}
|
|
|
|
void RunWithLockedWeakPersistentHandles(
|
|
std::function<void(FinalizablePersistentHandles&)> fun) {
|
|
MutexLocker ml(&mutex_);
|
|
fun(weak_persistent_handles_);
|
|
}
|
|
|
|
WeakTable* acquired_table() { return &acquired_table_; }
|
|
|
|
private:
|
|
Mutex mutex_;
|
|
|
|
PersistentHandles persistent_handles_;
|
|
FinalizablePersistentHandles weak_persistent_handles_;
|
|
WeakTable acquired_table_;
|
|
|
|
// Persistent handles to important objects.
|
|
PersistentHandle* null_;
|
|
PersistentHandle* true_;
|
|
PersistentHandle* false_;
|
|
PersistentHandle* acquired_error_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ApiState);
|
|
};
|
|
|
|
inline FinalizablePersistentHandle* FinalizablePersistentHandle::New(
|
|
IsolateGroup* isolate_group,
|
|
const Object& object,
|
|
void* peer,
|
|
Dart_HandleFinalizer callback,
|
|
intptr_t external_size,
|
|
bool auto_delete) {
|
|
ApiState* state = isolate_group->api_state();
|
|
ASSERT(state != NULL);
|
|
FinalizablePersistentHandle* ref = state->AllocateWeakPersistentHandle();
|
|
ref->set_ptr(object);
|
|
ref->set_peer(peer);
|
|
ref->set_callback(callback);
|
|
ref->set_auto_delete(auto_delete);
|
|
// This may trigger GC, so it must be called last.
|
|
ref->SetExternalSize(external_size, isolate_group);
|
|
return ref;
|
|
}
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_DART_API_STATE_H_
|