diff --git a/ext/v8/init.cc b/ext/v8/init.cc index ca702c8..1e68a87 100644 --- a/ext/v8/init.cc +++ b/ext/v8/init.cc @@ -17,7 +17,8 @@ extern "C" { Object::Init(); Primitive::Init(); String::Init(); - // v8::Locker lock(); + Locker::Init(); + // GC::Init(); // Accessor::Init(); // Invocation::Init(); @@ -34,7 +35,6 @@ extern "C" { // Message::Init(); // TryCatch::Init(); // Exception::Init(); - // Locker::Init(); // ResourceConstraints::Init(); // HeapStatistics::Init(); } diff --git a/ext/v8/locker.cc b/ext/v8/locker.cc new file mode 100644 index 0000000..acebb32 --- /dev/null +++ b/ext/v8/locker.cc @@ -0,0 +1,83 @@ +#include "rr.h" + +namespace rr { + + void Locker::Init() { + ClassBuilder("Locker"). + defineSingletonMethod("IsLocked", &IsLocked). + defineSingletonMethod("IsActive", &IsActive); + + VALUE v8 = rb_define_module("V8"); + VALUE c = rb_define_module_under(v8, "C"); + + rb_define_singleton_method(c, "Locker", (VALUE (*)(...))&Lock, -1); + rb_define_singleton_method(c, "Unlocker",(VALUE (*)(...))&Unlock, -1); + } + + VALUE Locker::IsLocked(VALUE self, VALUE isolate) { + return Bool(v8::Locker::IsLocked(Isolate(isolate))); + } + + VALUE Locker::IsActive(VALUE self) { + return Bool(v8::Locker::IsActive()); + } + + VALUE Locker::Lock(int argc, VALUE* argv, VALUE self) { + if (!rb_block_given_p()) { + return Qnil; + } + + int state = 0; + + VALUE isolate, block; + rb_scan_args(argc, argv, "10&", &isolate, &block); + + VALUE result = setupLockAndCall(Isolate(isolate), &state, block); + + if (state != 0) { + rb_jump_tag(state); + } + + return result; + } + + VALUE Locker::setupLockAndCall(Isolate isolate, int* state, VALUE block) { + v8::Locker locker(isolate); + + return rb_protect(&doLockCall, block, state); + } + + VALUE Locker::doLockCall(VALUE block) { + return rb_funcall(block, rb_intern("call"), 0); + } + + VALUE Locker::Unlock(int argc, VALUE* argv, VALUE self) { + if (!rb_block_given_p()) { + return Qnil; + } + + int state = 0; + + VALUE isolate, block; + rb_scan_args(argc, argv, "10&", &isolate, &block); + + VALUE result = setupUnlockAndCall(Isolate(isolate), &state, block); + + if (state != 0) { + rb_jump_tag(state); + } + + return result; + } + + VALUE Locker::setupUnlockAndCall(Isolate isolate, int* state, VALUE block) { + v8::Unlocker unlocker(isolate); + + return rb_protect(&doUnlockCall, block, state); + } + + VALUE Locker::doUnlockCall(VALUE block) { + return rb_funcall(block, rb_intern("call"), 0); + } + +} diff --git a/ext/v8/locker.h b/ext/v8/locker.h new file mode 100644 index 0000000..a733d72 --- /dev/null +++ b/ext/v8/locker.h @@ -0,0 +1,26 @@ +#ifndef RR_LOCKER +#define RR_LOCKER + +namespace rr { + + class Locker { + public: + static void Init(); + + static VALUE IsLocked(VALUE self, VALUE isolate); + static VALUE IsActive(VALUE self); + + static VALUE Lock(int argc, VALUE* argv, VALUE self); + static VALUE Unlock(int argc, VALUE* argv, VALUE self); + + protected: + static VALUE setupLockAndCall(Isolate isolate, int* state, VALUE code); + static VALUE doLockCall(VALUE code); + + static VALUE setupUnlockAndCall(Isolate isolate, int* state, VALUE code); + static VALUE doUnlockCall(VALUE code); + }; + +} + +#endif diff --git a/ext/v8/rr.h b/ext/v8/rr.h index 2ab5981..ddd478c 100644 --- a/ext/v8/rr.h +++ b/ext/v8/rr.h @@ -19,6 +19,7 @@ #include "v8.h" #include "isolate.h" +#include "locker.h" #include "handles.h" #include "context.h" diff --git a/spec/c/locker_spec.rb b/spec/c/locker_spec.rb new file mode 100644 index 0000000..1f960c5 --- /dev/null +++ b/spec/c/locker_spec.rb @@ -0,0 +1,41 @@ +require 'c_spec_helper' + +describe V8::C::Locker do + let(:isolate) { V8::C::Isolate.New } + + it 'can lock and unlock the VM' do + expect(V8::C::Locker::IsLocked(isolate)).to eq false + + V8::C::Locker(isolate) do + expect(V8::C::Locker::IsLocked(isolate)).to eq true + + V8::C::Unlocker(isolate) do + expect(V8::C::Locker::IsLocked(isolate)).to eq false + end + end + + expect(V8::C::Locker::IsLocked(isolate)).to eq false + end + + it 'properly unlocks if an exception is thrown inside a lock block' do + begin + V8::C::Locker(isolate) do + raise 'boom!' + end + rescue + expect(V8::C::Locker::IsLocked(isolate)).to eq false + end + end + + it 'properly re-locks if an exception is thrown inside an un-lock block' do + V8::C::Locker(isolate) do + begin + V8::C::Unlocker(isolate) do + raise 'boom!' + end + rescue + expect(V8::C::Locker::IsLocked(isolate)).to eq true + end + end + end +end diff --git a/spec/c_spec_helper.rb b/spec/c_spec_helper.rb index f555295..4a45eab 100644 --- a/spec/c_spec_helper.rb +++ b/spec/c_spec_helper.rb @@ -11,24 +11,21 @@ module V8ContextHelpers end def bootstrap_v8_context + cleanup_isolates + isolate = V8::C::Isolate.New - begin - cleanup_isolates + V8::C::Locker(isolate) do isolate.Enter - - # V8::C::Locker() do - V8::C::HandleScope(isolate) do - @cxt = V8::C::Context::New(isolate) - begin - @cxt.Enter - yield - ensure - @cxt.Exit - end + V8::C::HandleScope(isolate) do + @cxt = V8::C::Context::New(isolate) + begin + @cxt.Enter + yield + ensure + @cxt.Exit end - # end - ensure + end isolate.Exit end end