From 05e4c5766f66a301238c1da49691c8ef8a037152 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Wed, 15 Jun 2011 08:37:58 -0500 Subject: [PATCH] Never access V8 from within Ruby GC. --- ext/v8/v8_handle.cpp | 29 +++++++++++++++++++++++------ ext/v8/v8_locker.cpp | 7 +++---- lib/v8.rb | 1 + lib/v8/portal/proxies.rb | 4 ---- specthread/spec_helper.rb | 2 ++ specthread/threading_spec.rb | 13 +++++++++++++ 6 files changed, 42 insertions(+), 14 deletions(-) create mode 100644 specthread/spec_helper.rb create mode 100644 specthread/threading_spec.rb diff --git a/ext/v8/v8_handle.cpp b/ext/v8/v8_handle.cpp index e8aa9ef..c254e2e 100644 --- a/ext/v8/v8_handle.cpp +++ b/ext/v8/v8_handle.cpp @@ -10,13 +10,11 @@ v8_handle::v8_handle(Handle handle) : handle(Persistent::New(handle) this->dead = false; } -v8_handle::~v8_handle() { - handle.Dispose(); - handle.Clear(); - dead = true; -} +v8_handle::~v8_handle() {} namespace { + VALUE handle_queue; + void v8_handle_mark(v8_handle* handle) { rb_gc_mark(handle->weakref_callback); rb_gc_mark(handle->weakref_callback_parameters); @@ -26,6 +24,21 @@ namespace { delete handle; } + void v8_handle_enqueue(v8_handle* handle) { + handle->dead = true; + VALUE zombie = Data_Wrap_Struct(rr_v8_handle_class(), 0, v8_handle_free, handle); + rb_ary_unshift(handle_queue, zombie); + } + + void v8_handle_dequeue(GCType type, GCCallbackFlags flags) { + for (VALUE handle = rb_ary_pop(handle_queue); RTEST(handle); handle = rb_ary_pop(handle_queue)) { + v8_handle* dead = NULL; + Data_Get_Struct(handle, struct v8_handle, dead); + dead->handle.Dispose(); + dead->handle.Clear(); + } + } + VALUE New(VALUE self, VALUE handle) { if (RTEST(handle)) { Persistent that = rr_v8_handle(handle); @@ -101,11 +114,15 @@ void rr_init_handle() { rr_define_method(HandleClass, "ClearWeak", ClearWeak, 0); rr_define_method(HandleClass, "IsNearDeath", IsNearDeath, 0); rr_define_method(HandleClass, "IsWeak", IsWeak, 0); + + handle_queue = rb_ary_new(); + rb_gc_register_address(&handle_queue); + V8::AddGCPrologueCallback(v8_handle_dequeue); } VALUE rr_v8_handle_new(VALUE klass, v8::Handle handle) { v8_handle* new_handle = new v8_handle(handle); - return Data_Wrap_Struct(klass, v8_handle_mark, v8_handle_free, new_handle); + return Data_Wrap_Struct(klass, v8_handle_mark, v8_handle_enqueue, new_handle); } VALUE rr_v8_handle_class() { diff --git a/ext/v8/v8_locker.cpp b/ext/v8/v8_locker.cpp index aa37049..3d7db17 100644 --- a/ext/v8/v8_locker.cpp +++ b/ext/v8/v8_locker.cpp @@ -66,8 +66,8 @@ namespace { * For details on V8 locking semantics, see the locking {API http://izs.me/v8-docs/classv8_1_1Unlocker.html} * @return [V8::C::Unocker] the new locker */ - VALUE New(VALUE UnockerClass) { - Unlocker unlocker = new Unlocker(); + VALUE New(VALUE UnlockerClass) { + Unlocker* unlocker = new Unlocker(); return Data_Wrap_Struct(UnlockerClass, 0, 0, (void*)unlocker); } @@ -80,7 +80,7 @@ namespace { */ VALUE Delete(VALUE self) { Unlocker* unlocker; - Data_Get_Struct(self, class Locker, locker); + Data_Get_Struct(self, class Unlocker, unlocker); delete unlocker; } } @@ -126,7 +126,6 @@ namespace { } void rr_init_v8_locker() { - VALUE V8 = rb_define_module("V8"); VALUE LockerClass = rr_define_class("Locker"); VALUE UnlockerClass = rr_define_class("Unlocker"); rr_define_singleton_method(LockerClass, "new", Lock::New, 0); diff --git a/lib/v8.rb b/lib/v8.rb index ee9dded..8391672 100644 --- a/lib/v8.rb +++ b/lib/v8.rb @@ -4,6 +4,7 @@ $:.unshift(File.dirname(__FILE__)) unless module V8 require 'v8/version' require 'v8/v8' #native glue + require 'v8/c/locker' require 'v8/portal' require 'v8/portal/caller' require 'v8/portal/proxies' diff --git a/lib/v8/portal/proxies.rb b/lib/v8/portal/proxies.rb index 84b52f6..16a1c71 100644 --- a/lib/v8/portal/proxies.rb +++ b/lib/v8/portal/proxies.rb @@ -43,7 +43,6 @@ module V8 @js_proxies_js2rb[proxy] = target @js_proxies_rb2js[target] = proxy proxy.MakeWeak(nil, @clear_js_proxy) - V8::C::V8::AdjustAmountOfExternalAllocatedMemory(16 * 1024) end # Lookup the JavaScript proxy for a natively Ruby object @@ -66,7 +65,6 @@ module V8 @rb_proxies_rb2js[proxy.object_id] = target @rb_proxies_js2rb[target] = proxy.object_id ObjectSpace.define_finalizer(proxy, @clear_rb_proxy) - V8::C::V8::AdjustAmountOfExternalAllocatedMemory(8 * 1024) end # Looks up the Ruby proxy for an object that is natively JavaScript @@ -115,7 +113,6 @@ module V8 rb = @js2rb[proxy] @js2rb.delete(proxy) @rb2js.delete(rb) - V8::C::V8::AdjustAmountOfExternalAllocatedMemory(-16 * 1024) end end @@ -146,7 +143,6 @@ module V8 if js = @rb2js[proxy_id] @rb2js.delete(proxy_id) @js2rb.delete(js) - V8::C::V8::AdjustAmountOfExternalAllocatedMemory(-8 * 1024) end end end diff --git a/specthread/spec_helper.rb b/specthread/spec_helper.rb new file mode 100644 index 0000000..319ce45 --- /dev/null +++ b/specthread/spec_helper.rb @@ -0,0 +1,2 @@ +require Pathname(__FILE__).dirname.join('../spec/spec_helper') + diff --git a/specthread/threading_spec.rb b/specthread/threading_spec.rb new file mode 100644 index 0000000..ee81bfe --- /dev/null +++ b/specthread/threading_spec.rb @@ -0,0 +1,13 @@ + +require 'spec_helper' + +describe "using v8 from multiple threads" do + + it "is possible" do + Thread.new do + require 'v8' + V8::Context.new + end.join + V8::Context.new + end +end \ No newline at end of file