| Index: runtime/vm/dart_api_state.h
|
| diff --git a/runtime/vm/dart_api_state.h b/runtime/vm/dart_api_state.h
|
| index b510377352cdc294d017c4922a86bd2e052e0c5f..f687fb2a914f01f1323af97c0aa713f5d6a7f525 100644
|
| --- a/runtime/vm/dart_api_state.h
|
| +++ b/runtime/vm/dart_api_state.h
|
| @@ -15,8 +15,9 @@
|
| #include "vm/handles.h"
|
| #include "vm/object.h"
|
| #include "vm/os.h"
|
| -#include "vm/raw_object.h"
|
| #include "vm/os_thread.h"
|
| +#include "vm/raw_object.h"
|
| +#include "vm/thread_pool.h"
|
| #include "vm/visitor.h"
|
| #include "vm/weak_table.h"
|
|
|
| @@ -24,6 +25,25 @@
|
|
|
| namespace dart {
|
|
|
| +class FinalizablePersistentHandle;
|
| +typedef MallocGrowableArray<FinalizablePersistentHandle*> FinalizationQueue;
|
| +
|
| +
|
| +class BackgroundFinalizer : public ThreadPool::Task {
|
| + public:
|
| + BackgroundFinalizer(Isolate* isolate, FinalizationQueue* queue);
|
| + virtual ~BackgroundFinalizer() { }
|
| +
|
| + void Run();
|
| +
|
| + private:
|
| + Isolate* isolate_;
|
| + FinalizationQueue* queue_;
|
| +
|
| + DISALLOW_IMPLICIT_CONSTRUCTORS(BackgroundFinalizer);
|
| +};
|
| +
|
| +
|
| // 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
|
| @@ -214,12 +234,12 @@ class FinalizablePersistentHandle {
|
| }
|
|
|
| intptr_t external_size() const {
|
| - return ExternalSizeBits::decode(external_data_);
|
| + return ExternalSizeInWordsBits::decode(external_data_) * kWordSize;
|
| }
|
|
|
| void SetExternalSize(intptr_t size, Isolate* isolate) {
|
| ASSERT(size >= 0);
|
| - set_external_size(Utils::RoundUp(size, kObjectAlignment));
|
| + set_external_size(size);
|
| if (SpaceForExternal() == Heap::kNew) {
|
| SetExternalNewSpaceBit();
|
| }
|
| @@ -227,9 +247,18 @@ class FinalizablePersistentHandle {
|
| }
|
|
|
| // Called when the referent becomes unreachable.
|
| - void UpdateUnreachable(Isolate* isolate) {
|
| + void UpdateUnreachable(Isolate* isolate, FinalizationQueue* queue) {
|
| + if (is_queued_for_finalization()) {
|
| + return;
|
| + }
|
| EnsureFreeExternal(isolate);
|
| - Finalize(isolate, this);
|
| + if (queue == NULL) {
|
| + Finalize(isolate, this);
|
| + } else {
|
| + MarkForFinalization();
|
| + queue->Add(this);
|
| + set_is_queued_for_finalization(true);
|
| + }
|
| }
|
|
|
| // Called when the referent has moved, potentially between generations.
|
| @@ -252,20 +281,22 @@ class FinalizablePersistentHandle {
|
| private:
|
| enum {
|
| kExternalNewSpaceBit = 0,
|
| - kExternalSizeBits = 1,
|
| - kExternalSizeBitsSize = (kBitsPerWord - 1),
|
| + kQueuedForFinalizationBit = 1,
|
| + kExternalSizeBits = 2,
|
| + kExternalSizeBitsSize = (kBitsPerWord - 2),
|
| };
|
|
|
| // This part of external_data_ is the number of externally allocated bytes.
|
| - // TODO(koda): Measure size in words instead.
|
| - class ExternalSizeBits : public BitField<uword,
|
| - intptr_t,
|
| - kExternalSizeBits,
|
| - kExternalSizeBitsSize> {};
|
| + 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> {};
|
| + class QueuedForFinalizationBit :
|
| + public BitField<uword, bool, kQueuedForFinalizationBit, 1> {};
|
|
|
| friend class FinalizablePersistentHandles;
|
|
|
| @@ -299,6 +330,11 @@ class FinalizablePersistentHandle {
|
| callback_ = NULL;
|
| }
|
|
|
| + void MarkForFinalization() {
|
| + raw_ = Object::null();
|
| + ASSERT(callback_ != NULL);
|
| + }
|
| +
|
| void set_raw(RawObject* raw) { raw_ = raw; }
|
| void set_raw(const LocalHandle& ref) { raw_ = ref.raw(); }
|
| void set_raw(const Object& object) { raw_ = object.raw(); }
|
| @@ -310,8 +346,17 @@ class FinalizablePersistentHandle {
|
| }
|
|
|
| void set_external_size(intptr_t size) {
|
| - ASSERT(ExternalSizeBits::is_valid(size));
|
| - external_data_ = ExternalSizeBits::update(size, external_data_);
|
| + 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 is_queued_for_finalization() const {
|
| + return QueuedForFinalizationBit::decode(external_data_);
|
| + }
|
| + void set_is_queued_for_finalization(bool value) {
|
| + external_data_ = QueuedForFinalizationBit::update(value, external_data_);
|
| }
|
|
|
| bool IsSetNewSpaceBit() const {
|
| @@ -333,10 +378,13 @@ class FinalizablePersistentHandle {
|
| Heap::kNew : Heap::kOld;
|
| }
|
|
|
| + friend class BackgroundFinalizer;
|
| +
|
| RawObject* raw_;
|
| void* peer_;
|
| uword external_data_;
|
| Dart_WeakPersistentHandleFinalizer callback_;
|
| +
|
| DISALLOW_ALLOCATION(); // Allocated through AllocateHandle methods.
|
| DISALLOW_COPY_AND_ASSIGN(FinalizablePersistentHandle);
|
| };
|
| @@ -501,9 +549,11 @@ class FinalizablePersistentHandles
|
| : Handles<kFinalizablePersistentHandleSizeInWords,
|
| kFinalizablePersistentHandlesPerChunk,
|
| kOffsetOfRawPtrInFinalizablePersistentHandle>(),
|
| - free_list_(NULL) { }
|
| + free_list_(NULL), mutex_(new Mutex()) { }
|
| ~FinalizablePersistentHandles() {
|
| free_list_ = NULL;
|
| + delete mutex_;
|
| + mutex_ = NULL;
|
| }
|
|
|
| // Accessors.
|
| @@ -530,25 +580,31 @@ class FinalizablePersistentHandles
|
| // by calling FreeHandle.
|
| FinalizablePersistentHandle* AllocateHandle() {
|
| FinalizablePersistentHandle* handle;
|
| - if (free_list_ != NULL) {
|
| - handle = free_list_;
|
| - free_list_ = handle->Next();
|
| - handle->set_raw(Object::null());
|
| - } else {
|
| - handle = reinterpret_cast<FinalizablePersistentHandle*>(
|
| - AllocateScopedHandle());
|
| - handle->Clear();
|
| + {
|
| + MutexLocker ml(mutex_);
|
| + if (free_list_ != NULL) {
|
| + handle = free_list_;
|
| + free_list_ = handle->Next();
|
| + handle->set_raw(Object::null());
|
| + return handle;
|
| + }
|
| }
|
| +
|
| + handle = reinterpret_cast<FinalizablePersistentHandle*>(
|
| + AllocateScopedHandle());
|
| + handle->Clear();
|
| return handle;
|
| }
|
|
|
| void FreeHandle(FinalizablePersistentHandle* handle) {
|
| + MutexLocker ml(mutex_);
|
| handle->FreeHandle(free_list());
|
| set_free_list(handle);
|
| }
|
|
|
| // Validate if passed in handle is a Persistent Handle.
|
| bool IsValidHandle(Dart_WeakPersistentHandle object) const {
|
| + MutexLocker ml(mutex_);
|
| return IsValidScopedHandle(reinterpret_cast<uword>(object));
|
| }
|
|
|
| @@ -559,6 +615,7 @@ class FinalizablePersistentHandles
|
|
|
| private:
|
| FinalizablePersistentHandle* free_list_;
|
| + Mutex* mutex_;
|
| DISALLOW_COPY_AND_ASSIGN(FinalizablePersistentHandles);
|
| };
|
|
|
| @@ -761,6 +818,7 @@ inline FinalizablePersistentHandle* FinalizablePersistentHandle::New(
|
| ref->set_raw(object);
|
| ref->set_peer(peer);
|
| ref->set_callback(callback);
|
| + ref->set_is_queued_for_finalization(false);
|
| // This may trigger GC, so it must be called last.
|
| ref->SetExternalSize(external_size, isolate);
|
| return ref;
|
|
|