mirror of
https://github.com/rubyjs/therubyracer
synced 2023-03-27 23:21:42 -04:00
implement weak reference callbacks in handles.
This commit is contained in:
parent
d2f1dcd042
commit
fe90ba39b7
3 changed files with 115 additions and 7 deletions
|
@ -4,15 +4,29 @@
|
||||||
|
|
||||||
using namespace v8;
|
using namespace v8;
|
||||||
|
|
||||||
v8_handle::v8_handle(Handle<void> handle) : handle(Persistent<void>::New(handle)) {}
|
v8_handle::v8_handle(Handle<void> handle) : handle(Persistent<void>::New(handle)) {
|
||||||
|
this->references = rb_hash_new();
|
||||||
|
this->dead = false;
|
||||||
|
}
|
||||||
|
|
||||||
v8_handle::~v8_handle() {
|
v8_handle::~v8_handle() {
|
||||||
handle.Dispose();
|
handle.Dispose();
|
||||||
handle.Clear();
|
handle.Clear();
|
||||||
|
dead = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void v8_handle::set_internal(const char* name, VALUE value) {
|
||||||
|
rb_hash_aset(this->references, rb_str_new2(name), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE v8_handle::get_internal(const char* name) {
|
||||||
|
return rb_funcall(this->references, rb_intern("[]"), 1, rb_str_new2(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void gc_mark(v8_handle* handle) {}
|
void gc_mark(v8_handle* handle) {
|
||||||
|
rb_gc_mark(handle->references);
|
||||||
|
}
|
||||||
|
|
||||||
void gc_free(v8_handle* handle) {
|
void gc_free(v8_handle* handle) {
|
||||||
delete handle;
|
delete handle;
|
||||||
|
@ -41,10 +55,22 @@ namespace {
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoopWeakReferenceCallback(Persistent<Value> object, void* parameter) {}
|
void RubyWeakReferenceCallback(Persistent<Value> value, void* parameter) {
|
||||||
|
value.Dispose();
|
||||||
|
v8_handle* handle = rr_v8_handle_raw((VALUE)parameter);
|
||||||
|
handle->handle.Dispose();
|
||||||
|
handle->handle.Clear();
|
||||||
|
handle->dead = true;
|
||||||
|
VALUE callback = rr_v8_handle_get_internal((VALUE)parameter, "weakref_callback");
|
||||||
|
if (RTEST(callback)) {
|
||||||
|
rb_funcall(callback, rb_intern("call"), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
VALUE MakeWeak(VALUE self) {
|
VALUE MakeWeak(VALUE self) {
|
||||||
rr_v8_handle<void>(self).MakeWeak(0, NoopWeakReferenceCallback);
|
rr_v8_handle_set_internal(self,"weakref_callback", rb_block_proc());
|
||||||
|
rr_v8_handle<void>(self).MakeWeak((void*)self, RubyWeakReferenceCallback);
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +83,10 @@ namespace {
|
||||||
return rr_v82rb(rr_v8_handle<void>(self).IsNearDeath());
|
return rr_v82rb(rr_v8_handle<void>(self).IsNearDeath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VALUE IsDead(VALUE self) {
|
||||||
|
return rr_v82rb(rr_v8_handle_raw(self)->dead);
|
||||||
|
}
|
||||||
|
|
||||||
VALUE IsWeak(VALUE self) {
|
VALUE IsWeak(VALUE self) {
|
||||||
return rr_v82rb(rr_v8_handle<void>(self).IsWeak());
|
return rr_v82rb(rr_v8_handle<void>(self).IsWeak());
|
||||||
}
|
}
|
||||||
|
@ -71,6 +101,7 @@ void rr_init_handle() {
|
||||||
rr_define_method(HandleClass, "MakeWeak", MakeWeak, 0);
|
rr_define_method(HandleClass, "MakeWeak", MakeWeak, 0);
|
||||||
rr_define_method(HandleClass, "ClearWeak", ClearWeak, 0);
|
rr_define_method(HandleClass, "ClearWeak", ClearWeak, 0);
|
||||||
rr_define_method(HandleClass, "IsNearDeath", IsNearDeath, 0);
|
rr_define_method(HandleClass, "IsNearDeath", IsNearDeath, 0);
|
||||||
|
rr_define_method(HandleClass, "IsDead", IsDead, 0);
|
||||||
rr_define_method(HandleClass, "IsWeak", IsWeak, 0);
|
rr_define_method(HandleClass, "IsWeak", IsWeak, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,3 +113,17 @@ VALUE rr_v8_handle_class() {
|
||||||
return rr_define_class("Handle");
|
return rr_define_class("Handle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rr_v8_handle_set_internal(VALUE handle, const char* name, VALUE value) {
|
||||||
|
rr_v8_handle_raw(handle)->set_internal(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE rr_v8_handle_get_internal(VALUE handle, const char* name) {
|
||||||
|
return rr_v8_handle_raw(handle)->get_internal(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
v8_handle* rr_v8_handle_raw(VALUE value) {
|
||||||
|
v8_handle* handle = 0;
|
||||||
|
Data_Get_Struct(value, struct v8_handle, handle);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,17 +7,26 @@
|
||||||
struct v8_handle {
|
struct v8_handle {
|
||||||
v8_handle(v8::Handle<void> object);
|
v8_handle(v8::Handle<void> object);
|
||||||
virtual ~v8_handle();
|
virtual ~v8_handle();
|
||||||
|
VALUE get_internal(const char* name);
|
||||||
|
void set_internal(const char* name, VALUE value);
|
||||||
|
|
||||||
v8::Persistent<void> handle;
|
v8::Persistent<void> handle;
|
||||||
|
bool dead;
|
||||||
|
VALUE references;
|
||||||
};
|
};
|
||||||
|
|
||||||
void rr_init_handle();
|
void rr_init_handle();
|
||||||
|
|
||||||
|
v8_handle* rr_v8_handle_raw(VALUE value);
|
||||||
|
|
||||||
template <class T> v8::Persistent<T>& rr_v8_handle(VALUE value) {
|
template <class T> v8::Persistent<T>& rr_v8_handle(VALUE value) {
|
||||||
v8_handle* handle = 0;
|
return (v8::Persistent<T>&)(rr_v8_handle_raw(value)->handle);
|
||||||
Data_Get_Struct(value, struct v8_handle, handle);
|
|
||||||
return (v8::Persistent<T>&)handle->handle;
|
|
||||||
}
|
}
|
||||||
VALUE rr_v8_handle_new(VALUE rbclass, v8::Handle<void> handle);
|
VALUE rr_v8_handle_new(VALUE rbclass, v8::Handle<void> handle);
|
||||||
VALUE rr_v8_handle_class();
|
VALUE rr_v8_handle_class();
|
||||||
|
|
||||||
|
void rr_v8_handle_set_internal(VALUE handle, const char* name, VALUE value);
|
||||||
|
VALUE rr_v8_handle_get_internal(VALUE handle, const char* name);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe "Memory:" do
|
||||||
|
context "A JavaScript Object reflected into ruby" do
|
||||||
|
before(:all) {c::V8::SetFlagsFromString("--expose-gc")}
|
||||||
|
before do
|
||||||
|
@cxt = c::Context::New()
|
||||||
|
@cxt.Enter()
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
@cxt.Exit()
|
||||||
|
end
|
||||||
|
|
||||||
|
it "has a strong reference from the ruby side" do
|
||||||
|
handle = c::Handle::New(object = c::Object::New())
|
||||||
|
collected = false
|
||||||
|
handle.MakeWeak() {collected = true; puts "yo"}
|
||||||
|
v8_gc
|
||||||
|
collected.should_not be_true
|
||||||
|
handle.IsDead().should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "will be garbarge collected if there are no more ruby references and is weakly reachable from V8" do
|
||||||
|
handle = c::Handle::New(object = c::Object::New())
|
||||||
|
object = nil
|
||||||
|
collected = false
|
||||||
|
handle.MakeWeak() {collected = true}
|
||||||
|
ruby_gc do
|
||||||
|
v8_gc
|
||||||
|
collected.should be_true
|
||||||
|
handle.IsDead().should be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def v8_gc
|
||||||
|
c::Script::New(c::String::New("gc()"), c::String::New("gc.js")).Run()
|
||||||
|
end
|
||||||
|
|
||||||
|
def ruby_gc
|
||||||
|
GC.stress = true
|
||||||
|
yield if block_given?
|
||||||
|
ensure
|
||||||
|
GC.stress = false
|
||||||
|
end
|
||||||
|
|
||||||
|
def c
|
||||||
|
V8::C
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue