mirror of
https://github.com/rubyjs/therubyracer
synced 2023-03-27 23:21:42 -04:00
retain and release ruby objects
This commit is contained in:
parent
369b0d323d
commit
a91f59d90e
8 changed files with 148 additions and 3 deletions
37
ext/v8/external.cc
Normal file
37
ext/v8/external.cc
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#include "rr.h"
|
||||||
|
#include "external.h"
|
||||||
|
|
||||||
|
namespace rr {
|
||||||
|
void External::Init() {
|
||||||
|
ClassBuilder("External").
|
||||||
|
defineSingletonMethod("New", &New).
|
||||||
|
|
||||||
|
defineMethod("Value", &Value).
|
||||||
|
|
||||||
|
store(&Class);
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE External::New(VALUE self, VALUE r_isolate, VALUE object) {
|
||||||
|
Isolate isolate(r_isolate);
|
||||||
|
isolate.retainObject(object);
|
||||||
|
|
||||||
|
Locker lock(isolate);
|
||||||
|
|
||||||
|
Container* container = new Container(object);
|
||||||
|
v8::Local<v8::External> external(v8::External::New(isolate, (void*)container));
|
||||||
|
|
||||||
|
v8::Global<v8::External>* global(new v8::Global<v8::External>(isolate, external));
|
||||||
|
container->global = global;
|
||||||
|
|
||||||
|
global->SetWeak<Container>(container, &release, v8::WeakCallbackType::kParameter);
|
||||||
|
|
||||||
|
return External(isolate, external);
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE External::Value(VALUE self) {
|
||||||
|
External external(self);
|
||||||
|
Locker lock(external);
|
||||||
|
Container* container((Container*)external->Value());
|
||||||
|
return container->object;
|
||||||
|
}
|
||||||
|
}
|
38
ext/v8/external.h
Normal file
38
ext/v8/external.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// -*- mode: c++ -*-
|
||||||
|
#ifndef EXTERNAL_H
|
||||||
|
#define EXTERNAL_H
|
||||||
|
|
||||||
|
namespace rr {
|
||||||
|
class External : Ref<v8::External> {
|
||||||
|
public:
|
||||||
|
|
||||||
|
static void Init();
|
||||||
|
static VALUE New(VALUE self, VALUE isolate, VALUE object);
|
||||||
|
static VALUE Value(VALUE self);
|
||||||
|
|
||||||
|
inline External(VALUE value) : Ref<v8::External>(value) {}
|
||||||
|
inline External(v8::Isolate* isolate, v8::Handle<v8::External> handle) :
|
||||||
|
Ref<v8::External>(isolate, handle) {}
|
||||||
|
|
||||||
|
struct Container {
|
||||||
|
Container(VALUE v) : object(v) {}
|
||||||
|
|
||||||
|
v8::Global<v8::External>* global;
|
||||||
|
VALUE object;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void release(const v8::WeakCallbackInfo<Container>& info) {
|
||||||
|
Container* container(info.GetParameter());
|
||||||
|
if (info.IsFirstPass()) {
|
||||||
|
container->global->Reset();
|
||||||
|
info.SetSecondPassCallback(&release);
|
||||||
|
} else {
|
||||||
|
Isolate isolate(info.GetIsolate());
|
||||||
|
isolate.scheduleReleaseObject(container->object);
|
||||||
|
delete container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* EXTERNAL_H */
|
|
@ -20,6 +20,7 @@ extern "C" {
|
||||||
Function::Init();
|
Function::Init();
|
||||||
Script::Init();
|
Script::Init();
|
||||||
Array::Init();
|
Array::Init();
|
||||||
|
External::Init();
|
||||||
|
|
||||||
// Accessor::Init();
|
// Accessor::Init();
|
||||||
// Invocation::Init();
|
// Invocation::Init();
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
namespace rr {
|
namespace rr {
|
||||||
|
|
||||||
void Isolate::Init() {
|
void Isolate::Init() {
|
||||||
|
rb_eval_string("require 'v8/retained_objects'");
|
||||||
ClassBuilder("Isolate").
|
ClassBuilder("Isolate").
|
||||||
defineSingletonMethod("New", &New).
|
defineSingletonMethod("New", &New).
|
||||||
|
|
||||||
|
@ -17,11 +18,16 @@ namespace rr {
|
||||||
|
|
||||||
VALUE Isolate::New(VALUE self) {
|
VALUE Isolate::New(VALUE self) {
|
||||||
Isolate::IsolateData* data = new IsolateData();
|
Isolate::IsolateData* data = new IsolateData();
|
||||||
|
VALUE rb_cRetainedObjects = rb_eval_string("V8::RetainedObjects");
|
||||||
|
data->retained_objects = rb_funcall(rb_cRetainedObjects, rb_intern("new"), 0);
|
||||||
|
|
||||||
v8::Isolate::CreateParams create_params;
|
v8::Isolate::CreateParams create_params;
|
||||||
create_params.array_buffer_allocator = &data->array_buffer_allocator;
|
create_params.array_buffer_allocator = &data->array_buffer_allocator;
|
||||||
|
|
||||||
Isolate isolate(v8::Isolate::New(create_params));
|
Isolate isolate(v8::Isolate::New(create_params));
|
||||||
isolate->SetData(0, new IsolateData());
|
isolate->SetData(0, data);
|
||||||
isolate->AddGCPrologueCallback(&clearReferences);
|
isolate->AddGCPrologueCallback(&clearReferences);
|
||||||
|
|
||||||
return isolate;
|
return isolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,8 @@ namespace rr {
|
||||||
* its book keeping data. E.g.
|
* its book keeping data. E.g.
|
||||||
* VALUE rubyObject = Isolate(v8::Isolate::New());
|
* VALUE rubyObject = Isolate(v8::Isolate::New());
|
||||||
*/
|
*/
|
||||||
inline operator VALUE() {
|
virtual operator VALUE() {
|
||||||
return Data_Wrap_Struct(Class, 0, 0, pointer);
|
return Data_Wrap_Struct(Class, &releaseAndMarkRetainedObjects, 0, pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,6 +60,28 @@ namespace rr {
|
||||||
data()->queue.enqueue((v8::Persistent<void>*)cell);
|
data()->queue.enqueue((v8::Persistent<void>*)cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void retainObject(VALUE object) {
|
||||||
|
rb_funcall(data()->retained_objects, rb_intern("add"), 1, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void releaseObject(VALUE object) {
|
||||||
|
rb_funcall(data()->retained_objects, rb_intern("remove"), 1, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void scheduleReleaseObject(VALUE object) {
|
||||||
|
data()->release_queue.enqueue(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void releaseAndMarkRetainedObjects(v8::Isolate* isolate_) {
|
||||||
|
Isolate isolate(isolate_);
|
||||||
|
IsolateData* data = isolate.data();
|
||||||
|
VALUE object;
|
||||||
|
while (data->release_queue.try_dequeue(object)) {
|
||||||
|
isolate.releaseObject(object);
|
||||||
|
}
|
||||||
|
rb_gc_mark(data->retained_objects);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instance of v8::GCPrologueCallback, this will run in the v8
|
* An instance of v8::GCPrologueCallback, this will run in the v8
|
||||||
* GC thread, and clear out all the references that have been
|
* GC thread, and clear out all the references that have been
|
||||||
|
@ -97,6 +119,14 @@ namespace rr {
|
||||||
* as the isolate.
|
* as the isolate.
|
||||||
*/
|
*/
|
||||||
struct IsolateData {
|
struct IsolateData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instance of `V8::RetainedObjects` that contains all
|
||||||
|
* references held from from V8. As long as they are in this
|
||||||
|
* list, they won't be gc'd by Ruby.
|
||||||
|
*/
|
||||||
|
VALUE retained_objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom ArrayBufferAllocator for this isolate. Why? because
|
* A custom ArrayBufferAllocator for this isolate. Why? because
|
||||||
* if you don't, it will segfault when you try and create an
|
* if you don't, it will segfault when you try and create an
|
||||||
|
@ -110,6 +140,11 @@ namespace rr {
|
||||||
* can be released by the v8 garbarge collector later.
|
* can be released by the v8 garbarge collector later.
|
||||||
*/
|
*/
|
||||||
ConcurrentQueue<v8::Persistent<void>*> queue;
|
ConcurrentQueue<v8::Persistent<void>*> queue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue to hold
|
||||||
|
*/
|
||||||
|
ConcurrentQueue<VALUE> release_queue;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,10 @@ namespace rr {
|
||||||
return isolate;
|
return isolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline operator v8::Isolate*() const {
|
||||||
|
return isolate;
|
||||||
|
}
|
||||||
|
|
||||||
static void destroy(Holder* holder) {
|
static void destroy(Holder* holder) {
|
||||||
delete holder;
|
delete holder;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ inline VALUE not_implemented(const char* message) {
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
#include "array.h"
|
#include "array.h"
|
||||||
#include "primitive.h"
|
#include "primitive.h"
|
||||||
|
#include "external.h"
|
||||||
// This one is named v8_string to avoid name collisions with C's string.h
|
// This one is named v8_string to avoid name collisions with C's string.h
|
||||||
#include "rr_string.h"
|
#include "rr_string.h"
|
||||||
|
|
||||||
|
|
23
spec/c/external_spec.rb
Normal file
23
spec/c/external_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
require 'c_spec_helper'
|
||||||
|
|
||||||
|
describe V8::C::External do
|
||||||
|
let(:isolate) { V8::C::Isolate::New() }
|
||||||
|
let(:value) { @external::Value() }
|
||||||
|
around { |example| V8::C::HandleScope(isolate) { example.run } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
Object.new.tap do |object|
|
||||||
|
@object_id = object.object_id
|
||||||
|
@external = V8::C::External::New(isolate, object)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "exists" do
|
||||||
|
expect(@external).to be
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can retrieve the ruby object out from V8 land" do
|
||||||
|
expect(value).to be
|
||||||
|
expect(value.object_id).to eql @object_id
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue