Commit f244f0c5 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

Implement managed objects with phantom handles

For each Managed<T> (which is a Foreign), we create a weak global handle
with a finalizer which deletes the referenced C++ object once the
Foreign is dead.
Before calling this finalizer, the garbage collector needs to mark the
referenced object black (i.e. live), because the finalizer might
resurrect it.
Since this is never done for managed objects, we can use the more
lightweight phantom handle semantics, which allows the referenced
object to be garbage collected right away.

However, we can't access the global handle via the WeakCallbackInfo,
because the global handle will already be garbage collected. So we need
to store it explicitly. This is solved by storing the global handle
together with the finalizer.
In order to implement this, ownership of the ManagedObjectFinalizer
is moved from the isolate to the managed object.

R=ulan@chromium.org, mtrofin@chromium.org
BUG=v8:6505, chromium:734345

Change-Id: I94a245df601f70e19355d82439d30099e159231b
Reviewed-on: https://chromium-review.googlesource.com/539578
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46036}
parent fade4a46
...@@ -2091,42 +2091,36 @@ void Isolate::ReleaseManagedObjects() { ...@@ -2091,42 +2091,36 @@ void Isolate::ReleaseManagedObjects() {
while (current != nullptr) { while (current != nullptr) {
Isolate::ManagedObjectFinalizer* next = current->next_; Isolate::ManagedObjectFinalizer* next = current->next_;
current->Dispose(); current->Dispose();
delete current;
current = next; current = next;
} }
// No new managed objects should pop up during finalization. // No new managed objects should pop up during finalization.
DCHECK_NULL(managed_object_finalizers_list_.next_); DCHECK_NULL(managed_object_finalizers_list_.next_);
} }
Isolate::ManagedObjectFinalizer* Isolate::RegisterForReleaseAtTeardown( void Isolate::RegisterForReleaseAtTeardown(
void* value, Isolate::ManagedObjectFinalizer::Deleter deleter) { Isolate::ManagedObjectFinalizer* finalizer) {
DCHECK_NOT_NULL(value); DCHECK_NOT_NULL(finalizer->value_);
DCHECK_NOT_NULL(deleter); DCHECK_NOT_NULL(finalizer->deleter_);
DCHECK_NULL(finalizer->prev_);
DCHECK_NULL(finalizer->next_);
Isolate::ManagedObjectFinalizer* ret = new Isolate::ManagedObjectFinalizer();
ret->value_ = value;
ret->deleter_ = deleter;
// Insert at head. We keep the head alive for the lifetime of the Isolate // Insert at head. We keep the head alive for the lifetime of the Isolate
// because otherwise we can't reset the head, should we delete it before // because otherwise we can't reset the head, should we delete it before
// the isolate expires // the isolate expires
Isolate::ManagedObjectFinalizer* next = managed_object_finalizers_list_.next_; Isolate::ManagedObjectFinalizer* next = managed_object_finalizers_list_.next_;
managed_object_finalizers_list_.next_ = ret; managed_object_finalizers_list_.next_ = finalizer;
ret->prev_ = &managed_object_finalizers_list_; finalizer->prev_ = &managed_object_finalizers_list_;
ret->next_ = next; finalizer->next_ = next;
if (next != nullptr) next->prev_ = ret; if (next != nullptr) next->prev_ = finalizer;
return ret;
} }
void Isolate::UnregisterFromReleaseAtTeardown( void Isolate::UnregisterFromReleaseAtTeardown(
Isolate::ManagedObjectFinalizer** finalizer_ptr) { Isolate::ManagedObjectFinalizer* finalizer) {
DCHECK_NOT_NULL(finalizer_ptr); DCHECK_NOT_NULL(finalizer);
Isolate::ManagedObjectFinalizer* finalizer = *finalizer_ptr;
DCHECK_NOT_NULL(finalizer->prev_); DCHECK_NOT_NULL(finalizer->prev_);
finalizer->prev_->next_ = finalizer->next_; finalizer->prev_->next_ = finalizer->next_;
if (finalizer->next_ != nullptr) finalizer->next_->prev_ = finalizer->prev_; if (finalizer->next_ != nullptr) finalizer->next_->prev_ = finalizer->prev_;
delete finalizer;
*finalizer_ptr = nullptr;
} }
Isolate::PerIsolateThreadData::~PerIsolateThreadData() { Isolate::PerIsolateThreadData::~PerIsolateThreadData() {
......
...@@ -1273,36 +1273,39 @@ class Isolate { ...@@ -1273,36 +1273,39 @@ class Isolate {
// List of native heap values allocated by the runtime as part of its // List of native heap values allocated by the runtime as part of its
// implementation that must be freed at isolate deinit. // implementation that must be freed at isolate deinit.
class ManagedObjectFinalizer final { class ManagedObjectFinalizer {
public: public:
typedef void (*Deleter)(void*); using Deleter = void (*)(ManagedObjectFinalizer*);
void Dispose() { deleter_(value_); }
private: ManagedObjectFinalizer(void* value, Deleter deleter)
friend class Isolate; : value_(value), deleter_(deleter) {
ManagedObjectFinalizer() {
DCHECK_EQ(reinterpret_cast<void*>(this), DCHECK_EQ(reinterpret_cast<void*>(this),
reinterpret_cast<void*>(&value_)); reinterpret_cast<void*>(&value_));
} }
// value_ must be the first member void Dispose() { deleter_(this); }
void* value() const { return value_; }
private:
friend class Isolate;
ManagedObjectFinalizer() = default;
void* value_ = nullptr; void* value_ = nullptr;
Deleter deleter_ = nullptr; Deleter deleter_ = nullptr;
ManagedObjectFinalizer* prev_ = nullptr; ManagedObjectFinalizer* prev_ = nullptr;
ManagedObjectFinalizer* next_ = nullptr; ManagedObjectFinalizer* next_ = nullptr;
}; };
// Register a native value for destruction at isolate teardown. // Register a finalizer to be called at isolate teardown.
ManagedObjectFinalizer* RegisterForReleaseAtTeardown( void RegisterForReleaseAtTeardown(ManagedObjectFinalizer*);
void* value, ManagedObjectFinalizer::Deleter deleter);
// Unregister a previously registered value from release at // Unregister a previously registered value from release at
// isolate teardown, deleting the ManagedObjectFinalizer. // isolate teardown.
// This transfers the responsibility of the previously managed value's // This transfers the responsibility of the previously managed value's
// deletion to the caller. Pass by pointer, because *finalizer_ptr gets // deletion to the caller.
// reset to nullptr. void UnregisterFromReleaseAtTeardown(ManagedObjectFinalizer*);
void UnregisterFromReleaseAtTeardown(ManagedObjectFinalizer** finalizer_ptr);
size_t elements_deletion_counter() { return elements_deletion_counter_; } size_t elements_deletion_counter() { return elements_deletion_counter_; }
void set_elements_deletion_counter(size_t value) { void set_elements_deletion_counter(size_t value) {
......
...@@ -20,11 +20,21 @@ namespace internal { ...@@ -20,11 +20,21 @@ namespace internal {
// address is typed as CppType**. The double indirection is due to the // address is typed as CppType**. The double indirection is due to the
// use, by Managed, of Isolate::ManagedObjectFinalizer, which has a CppType* // use, by Managed, of Isolate::ManagedObjectFinalizer, which has a CppType*
// first field. // first field.
// Calling Foreign::set_foreign_address is not allowed on a Managed object.
template <class CppType> template <class CppType>
class Managed : public Foreign { class Managed : public Foreign {
class FinalizerWithHandle : public Isolate::ManagedObjectFinalizer {
public:
FinalizerWithHandle(void* value,
Isolate::ManagedObjectFinalizer::Deleter deleter)
: Isolate::ManagedObjectFinalizer(value, deleter) {}
Object** global_handle_location;
};
public: public:
V8_INLINE CppType* get() { V8_INLINE CppType* get() {
return *(reinterpret_cast<CppType**>(foreign_address())); return reinterpret_cast<CppType*>(GetFinalizer()->value());
} }
static Managed<CppType>* cast(Object* obj) { static Managed<CppType>* cast(Object* obj) {
...@@ -33,46 +43,42 @@ class Managed : public Foreign { ...@@ -33,46 +43,42 @@ class Managed : public Foreign {
} }
static Handle<Managed<CppType>> New(Isolate* isolate, CppType* ptr) { static Handle<Managed<CppType>> New(Isolate* isolate, CppType* ptr) {
Isolate::ManagedObjectFinalizer* node = FinalizerWithHandle* finalizer =
isolate->RegisterForReleaseAtTeardown(ptr, new FinalizerWithHandle(ptr, &NativeDelete);
Managed<CppType>::NativeDelete); isolate->RegisterForReleaseAtTeardown(finalizer);
Handle<Managed<CppType>> handle = Handle<Managed<CppType>>::cast( Handle<Managed<CppType>> handle = Handle<Managed<CppType>>::cast(
isolate->factory()->NewForeign(reinterpret_cast<Address>(node))); isolate->factory()->NewForeign(reinterpret_cast<Address>(finalizer)));
RegisterWeakCallbackForDelete(isolate, handle); Handle<Object> global_handle = isolate->global_handles()->Create(*handle);
finalizer->global_handle_location = global_handle.location();
GlobalHandles::MakeWeak(finalizer->global_handle_location,
handle->GetFinalizer(), &Managed<CppType>::GCDelete,
v8::WeakCallbackType::kParameter);
return handle; return handle;
} }
private: private:
static void RegisterWeakCallbackForDelete(Isolate* isolate,
Handle<Managed<CppType>> handle) {
Handle<Object> global_handle = isolate->global_handles()->Create(*handle);
GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(),
&Managed<CppType>::GCDelete,
v8::WeakCallbackType::kFinalizer);
}
static void GCDelete(const v8::WeakCallbackInfo<void>& data) { static void GCDelete(const v8::WeakCallbackInfo<void>& data) {
Managed<CppType>** p = FinalizerWithHandle* finalizer =
reinterpret_cast<Managed<CppType>**>(data.GetParameter()); reinterpret_cast<FinalizerWithHandle*>(data.GetParameter());
Isolate::ManagedObjectFinalizer* finalizer = (*p)->GetFinalizer();
Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate()); Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
finalizer->Dispose(); isolate->UnregisterFromReleaseAtTeardown(finalizer);
isolate->UnregisterFromReleaseAtTeardown(&finalizer);
(*p)->set_foreign_address(static_cast<Address>(nullptr)); GlobalHandles::Destroy(finalizer->global_handle_location);
GlobalHandles::Destroy(reinterpret_cast<Object**>(p)); NativeDelete(finalizer);
} }
static void NativeDelete(void* value) { static void NativeDelete(Isolate::ManagedObjectFinalizer* finalizer) {
CppType* typed_value = reinterpret_cast<CppType*>(value); CppType* typed_value = reinterpret_cast<CppType*>(finalizer->value());
delete typed_value; delete typed_value;
FinalizerWithHandle* finalizer_with_handle =
static_cast<FinalizerWithHandle*>(finalizer);
delete finalizer_with_handle;
} }
Isolate::ManagedObjectFinalizer* GetFinalizer() { FinalizerWithHandle* GetFinalizer() {
return reinterpret_cast<Isolate::ManagedObjectFinalizer*>( return reinterpret_cast<FinalizerWithHandle*>(foreign_address());
foreign_address());
} }
}; };
} // namespace internal } // namespace internal
......
...@@ -20,8 +20,8 @@ class DeleteRecorder { ...@@ -20,8 +20,8 @@ class DeleteRecorder {
*deleted_ = false; *deleted_ = false;
} }
~DeleteRecorder() { *deleted_ = true; } ~DeleteRecorder() { *deleted_ = true; }
static void Deleter(void* value) { static void Deleter(Isolate::ManagedObjectFinalizer* finalizer) {
delete reinterpret_cast<DeleteRecorder*>(value); delete *reinterpret_cast<DeleteRecorder**>(finalizer);
} }
private: private:
...@@ -34,8 +34,8 @@ TEST(ManagedCollect) { ...@@ -34,8 +34,8 @@ TEST(ManagedCollect) {
bool deleted2 = false; bool deleted2 = false;
DeleteRecorder* d1 = new DeleteRecorder(&deleted1); DeleteRecorder* d1 = new DeleteRecorder(&deleted1);
DeleteRecorder* d2 = new DeleteRecorder(&deleted2); DeleteRecorder* d2 = new DeleteRecorder(&deleted2);
Isolate::ManagedObjectFinalizer* finalizer = Isolate::ManagedObjectFinalizer finalizer(d2, DeleteRecorder::Deleter);
isolate->RegisterForReleaseAtTeardown(d2, DeleteRecorder::Deleter); isolate->RegisterForReleaseAtTeardown(&finalizer);
{ {
HandleScope scope(isolate); HandleScope scope(isolate);
auto handle = Managed<DeleteRecorder>::New(isolate, d1); auto handle = Managed<DeleteRecorder>::New(isolate, d1);
...@@ -47,7 +47,6 @@ TEST(ManagedCollect) { ...@@ -47,7 +47,6 @@ TEST(ManagedCollect) {
CHECK(deleted1); CHECK(deleted1);
CHECK(!deleted2); CHECK(!deleted2);
isolate->UnregisterFromReleaseAtTeardown(&finalizer); isolate->UnregisterFromReleaseAtTeardown(&finalizer);
CHECK_NULL(finalizer);
delete d2; delete d2;
CHECK(deleted2); CHECK(deleted2);
} }
...@@ -69,7 +68,8 @@ TEST(DisposeCollect) { ...@@ -69,7 +68,8 @@ TEST(DisposeCollect) {
auto handle = Managed<DeleteRecorder>::New(i_isolate, d1); auto handle = Managed<DeleteRecorder>::New(i_isolate, d1);
USE(handle); USE(handle);
} }
i_isolate->RegisterForReleaseAtTeardown(d2, DeleteRecorder::Deleter); Isolate::ManagedObjectFinalizer finalizer(d2, DeleteRecorder::Deleter);
i_isolate->RegisterForReleaseAtTeardown(&finalizer);
isolate->Exit(); isolate->Exit();
isolate->Dispose(); isolate->Dispose();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment