1
0
Fork 0
mirror of https://github.com/rubyjs/therubyracer synced 2023-03-27 23:21:42 -04:00

Lock inside the C methods and store isolate with every ref

This commit is contained in:
Georgy Angelov 2015-04-04 14:03:06 +00:00
parent 7f48722d72
commit a926860150
22 changed files with 188 additions and 304 deletions

View file

@ -38,12 +38,20 @@ namespace rr {
}
VALUE Context::Enter(VALUE self) {
Context(self)->Enter();
Context context(self);
Locker lock(context.getIsolate());
context->Enter();
return Qnil;
}
VALUE Context::Exit(VALUE self) {
Context(self)->Exit();
Context context(self);
Locker lock(context.getIsolate());
context->Exit();
return Qnil;
}

View file

@ -31,7 +31,7 @@ namespace rr {
// static VALUE IsCodeGenerationFromStringsAllowed(VALUE self);
inline Context(VALUE value) : Ref<v8::Context>(value) {}
inline Context(v8::Handle<v8::Context> cxt) : Ref<v8::Context>(cxt) {}
inline Context(v8::Handle<v8::Context> ctx) : Ref<v8::Context>(ctx->GetIsolate(), ctx) {}
};
}

View file

@ -25,8 +25,13 @@ namespace rr {
}
VALUE Handles::SetupAndCall(Isolate isolate, int* state, VALUE block) {
Locker lock(isolate);
v8::HandleScope handle_scope(isolate);
return rb_protect(&DoCall, block, state);
{
Unlocker unlock(isolate);
return rb_protect(&DoCall, block, state);
}
}
VALUE Handles::DoCall(VALUE block) {

View file

@ -17,7 +17,6 @@ extern "C" {
Object::Init();
Primitive::Init();
String::Init();
Locker::Init();
// Accessor::Init();
// Invocation::Init();

View file

@ -1,83 +0,0 @@
#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);
}
}

View file

@ -1,26 +0,0 @@
#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

31
ext/v8/locks.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef RR_LOCKER
#define RR_LOCKER
namespace rr {
class Locker {
public:
Locker(v8::Isolate* _isolate): isolate(_isolate), locker(_isolate) {
isolate->Enter();
}
~Locker() {
isolate->Exit();
}
protected:
v8::Isolate* isolate;
v8::Locker locker;
};
class Unlocker {
public:
Unlocker(v8::Isolate* isolate): unlocker(isolate) {}
protected:
v8::Unlocker unlocker;
};
}
#endif

View file

@ -12,24 +12,33 @@ namespace rr {
store(&Class);
}
VALUE Object::New(VALUE self) {
return Object(v8::Object::New(v8::Isolate::GetCurrent()));
VALUE Object::New(VALUE self, VALUE rb_isolate) {
Isolate isolate(rb_isolate);
Locker lock(isolate);
return Object(isolate, v8::Object::New(isolate));
}
// TODO: Allow setting of property attributes
VALUE Object::Set(VALUE self, VALUE key, VALUE value) {
Object object(self);
Locker lock(object.getIsolate());
if (rb_obj_is_kind_of(key, rb_cNumeric)) {
return Bool(Object(self)->Set(UInt32(key), Value(value)));
return Bool(object->Set(UInt32(key), Value::rubyObjectToHandle(object.getIsolate(), value)));
} else {
return Bool(Object(self)->Set(*Value(key), Value(value)));
return Bool(object->Set(*Value(key), Value::rubyObjectToHandle(object.getIsolate(), value)));
}
}
VALUE Object::Get(VALUE self, VALUE key) {
Object object(self);
Locker lock(object.getIsolate());
if (rb_obj_is_kind_of(key, rb_cNumeric)) {
return Value(Object(self)->Get(UInt32(key)));
return Value::handleToRubyObject(object.getIsolate(), object->Get(UInt32(key)));
} else {
return Value(Object(self)->Get(*Value(key)));
return Value::handleToRubyObject(object.getIsolate(), object->Get(*Value(key)));
}
}
@ -40,7 +49,7 @@ namespace rr {
Backref* backref;
v8::Local<v8::String> key(v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), "rr::Backref"));
v8::Local<v8::String> key(v8::String::NewFromUtf8(getIsolate(), "rr::Backref"));
v8::Local<v8::Value> external = handle->GetHiddenValue(key);
VALUE value;

View file

@ -6,7 +6,7 @@ namespace rr {
class Object : public Ref<v8::Object> {
public:
static void Init();
static VALUE New(VALUE self);
static VALUE New(VALUE self, VALUE isolate);
static VALUE Set(VALUE self, VALUE key, VALUE value);
// static VALUE ForceSet(VALUE self, VALUE key, VALUE value);
static VALUE Get(VALUE self, VALUE key);
@ -55,7 +55,7 @@ namespace rr {
// static VALUE CallAsConstructor(VALUE self, VALUE argv);
inline Object(VALUE value) : Ref<v8::Object>(value) {}
inline Object(v8::Handle<v8::Object> object) : Ref<v8::Object>(object) {}
inline Object(v8::Isolate* isolate, v8::Handle<v8::Object> object) : Ref<v8::Object>(isolate, object) {}
virtual operator VALUE();

View file

@ -8,7 +8,7 @@ namespace rr {
static void Init();
inline Primitive(VALUE value) : Ref<v8::Primitive>(value) {}
inline Primitive(v8::Handle<v8::Primitive> primitive) : Ref<v8::Primitive>(primitive) {}
inline Primitive(v8::Isolate* isolate, v8::Handle<v8::Primitive> primitive) : Ref<v8::Primitive>(isolate, primitive) {}
};
}

View file

@ -33,9 +33,19 @@ namespace rr {
public:
Ref(VALUE value) {
this->value = value;
Holder* holder = unwrapHolder();
if (holder) {
this->isolate = holder->isolate;
this->handle = v8::Local<T>::New(holder->isolate, *holder->handle);
} else {
this->isolate = NULL;
this->handle = v8::Local<T>();
}
}
Ref(v8::Local<T> handle) {
Ref(v8::Isolate* isolate, v8::Local<T> handle) {
this->isolate = isolate;
this->handle = handle;
}
@ -49,21 +59,18 @@ namespace rr {
return Qnil;
}
return Data_Wrap_Struct(Class, 0, &Holder::destroy, new Holder(handle));
return Data_Wrap_Struct(Class, 0, &Holder::destroy, new Holder(isolate, handle));
}
/*
* Coerce a Ref into a v8::Local.
*/
virtual operator v8::Handle<T>() const {
if (RTEST(this->value)) {
Holder* holder = NULL;
Data_Get_Struct(this->value, class Holder, holder);
return handle;
}
return v8::Local<T>::New(v8::Isolate::GetCurrent(), *holder->handle);
} else {
return v8::Local<T>();
}
v8::Isolate* getIsolate() const {
return isolate;
}
void dispose() {
@ -84,9 +91,10 @@ namespace rr {
class Holder {
friend class Ref;
public:
Holder(v8::Handle<T> handle) {
Holder(v8::Isolate* isolate, v8::Handle<T> handle) {
this->disposed_p = false;
this->handle = new v8::Persistent<T>(v8::Isolate::GetCurrent(), handle);
this->isolate = isolate;
this->handle = new v8::Persistent<T>(isolate, handle);
}
virtual ~Holder() {
@ -102,23 +110,34 @@ namespace rr {
}
protected:
v8::Isolate* isolate;
v8::Persistent<T>* handle;
bool disposed_p;
static void destroy(Holder* holder) {
holder->dispose();
// TODO: This previously enqueued the holder to be disposed of
// in `AddGCPrologueCallback`. Now that `AddGCPrologueCallback` depends
// on an active Isolate (and must be registered for each one) it
// might be better to just dispose of the object on the spot.
// GC::Finalize(holder);
// TODO: Use a nonblocking queue with `AddGCPrologueCallback`
}
};
VALUE value;
v8::Handle<T> handle;
static VALUE Class;
protected:
Holder* unwrapHolder() const {
if (RTEST(this->value)) {
Holder* holder = NULL;
Data_Get_Struct(this->value, class Holder, holder);
return holder;
} else {
return NULL;
}
}
VALUE value;
v8::Isolate* isolate;
v8::Handle<T> handle;
};
template <class T>

View file

@ -15,11 +15,12 @@
#include "equiv.h"
#include "bool.h"
#include "pointer.h"
#include "isolate.h"
#include "ref.h"
#include "v8.h"
#include "isolate.h"
#include "locker.h"
#include "locks.h"
#include "handles.h"
#include "context.h"

View file

@ -12,10 +12,12 @@ namespace rr {
store(&Class);
}
VALUE String::NewFromUtf8(VALUE StringClass, VALUE string) {
v8::Local<v8::String> v8_string = v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), RSTRING_PTR(string), v8::String::kNormalString, (int)RSTRING_LEN(string));
VALUE String::NewFromUtf8(VALUE StringClass, VALUE rb_isolate, VALUE string) {
Isolate isolate(rb_isolate);
return String(v8_string);
v8::Local<v8::String> v8_string = v8::String::NewFromUtf8(isolate, RSTRING_PTR(string), v8::String::kNormalString, (int)RSTRING_LEN(string));
return String(isolate, v8_string);
}
VALUE String::Utf8Value(VALUE self) {
@ -29,20 +31,20 @@ namespace rr {
}
VALUE String::Concat(VALUE self, VALUE left, VALUE right) {
return String(v8::String::Concat(String(left), String(right)));
String left_string(left);
return String(left_string.getIsolate(), v8::String::Concat(left_string, String(right)));
}
String::operator v8::Handle<v8::String>() const {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
switch (TYPE(value)) {
case T_STRING:
return v8::String::NewFromUtf8(isolate, RSTRING_PTR(value), v8::String::kNormalString, (int)RSTRING_LEN(value));
return v8::String::NewFromUtf8(getIsolate(), RSTRING_PTR(value), v8::String::kNormalString, (int)RSTRING_LEN(value));
case T_DATA:
return Ref<v8::String>::operator v8::Handle<v8::String>();
default:
VALUE string = rb_funcall(value, rb_intern("to_s"), 0);
return v8::String::NewFromUtf8(isolate, RSTRING_PTR(string), v8::String::kNormalString, (int)RSTRING_LEN(string));
return v8::String::NewFromUtf8(getIsolate(), RSTRING_PTR(string), v8::String::kNormalString, (int)RSTRING_LEN(string));
}
}

View file

@ -7,12 +7,12 @@ namespace rr {
public:
static void Init();
static VALUE NewFromUtf8(VALUE self, VALUE value);
static VALUE NewFromUtf8(VALUE self, VALUE isolate, VALUE value);
static VALUE Utf8Value(VALUE self);
static VALUE Concat(VALUE self, VALUE left, VALUE right);
inline String(VALUE value) : Ref<v8::String>(value) {}
inline String(v8::Handle<v8::String> string) : Ref<v8::String>(string) {}
inline String(v8::Isolate* isolate, v8::Handle<v8::String> string) : Ref<v8::String>(isolate, string) {}
virtual operator v8::Handle<v8::String>() const;
};

View file

@ -40,6 +40,9 @@ namespace rr {
// defineMethod("Equals", &Equals).
// defineMethod("StrictEquals", &StrictEquals).
defineMethod("ToRubyObject", &ToRubyObject).
defineSingletonMethod("FromRubyObject", &FromRubyObject).
store(&Class);
rb_gc_register_address(&Empty);
@ -82,7 +85,9 @@ namespace rr {
}
VALUE Value::ToString(VALUE self) {
return String(Value(self)->ToString());
Value value(self);
return String(value.getIsolate(), value->ToString());
}
VALUE Value::Equals(VALUE self, VALUE other) {
@ -93,7 +98,21 @@ namespace rr {
return Bool(Value(self)->StrictEquals(Value(other)));
}
Value::operator VALUE() {
VALUE Value::ToRubyObject(VALUE self) {
Value value(self);
Locker lock(value.getIsolate());
return handleToRubyObject(value.getIsolate(), value);
}
VALUE Value::FromRubyObject(VALUE selfClass, VALUE rb_isolate, VALUE value) {
Isolate isolate(rb_isolate);
Locker lock(isolate);
return Value(isolate, rubyObjectToHandle(isolate, value));
}
VALUE Value::handleToRubyObject(v8::Isolate* isolate, v8::Handle<v8::Value> handle) {
if (handle.IsEmpty() || handle->IsUndefined() || handle->IsNull()) {
return Qnil;
}
@ -129,7 +148,7 @@ namespace rr {
// }
if (handle->IsString()) {
return String(handle->ToString());
return String(isolate, handle->ToString());
}
// TODO
@ -138,19 +157,19 @@ namespace rr {
// }
if (handle->IsObject()) {
return Object(handle->ToObject());
return Object(isolate, handle->ToObject());
}
return Ref<v8::Value>::operator VALUE();
return Value(isolate, handle);
}
Value::operator v8::Handle<v8::Value>() const {
v8::Handle<v8::Value> Value::rubyObjectToHandle(v8::Isolate* isolate, VALUE value) {
Locker lock(isolate);
if (rb_equal(value, Empty)) {
return v8::Handle<v8::Value>();
}
v8::Isolate* isolate = v8::Isolate::GetCurrent();
switch (TYPE(value)) {
case T_FIXNUM:
return v8::Integer::New(isolate, NUM2INT(value));
@ -165,7 +184,7 @@ namespace rr {
case T_FALSE:
return v8::False(isolate);
case T_DATA:
return Ref<v8::Value>::operator v8::Handle<v8::Value>();
return Value(value);
case T_OBJECT:
case T_CLASS:
case T_ICLASS:

View file

@ -35,14 +35,17 @@ namespace rr {
// static VALUE Uint32Value(VALUE self);
// static VALUE Int32Value(VALUE self);
static VALUE ToRubyObject(VALUE self);
static VALUE FromRubyObject(VALUE self, VALUE isolate, VALUE value);
static VALUE Equals(VALUE self, VALUE other);
static VALUE StrictEquals(VALUE self, VALUE other);
inline Value(VALUE value) : Ref<v8::Value>(value) {}
inline Value(v8::Handle<v8::Value> value) : Ref<v8::Value>(value) {}
inline Value(v8::Isolate* isolate, v8::Handle<v8::Value> value) : Ref<v8::Value>(isolate, value) {}
virtual operator VALUE();
virtual operator v8::Handle<v8::Value>() const;
static VALUE handleToRubyObject(v8::Isolate* isolate, v8::Handle<v8::Value> value);
static v8::Handle<v8::Value> rubyObjectToHandle(v8::Isolate* isolate, VALUE value);
static VALUE Empty;
};

View file

@ -1,8 +1,6 @@
require 'c_spec_helper'
describe V8::C::Isolate do
before(:each) { cleanup_isolates }
it 'can create a new isolate' do
expect(V8::C::Isolate.New).to be
end
@ -14,43 +12,4 @@ describe V8::C::Isolate do
expect(isolate_one.Equals(isolate_one)).to eq true
expect(isolate_one.Equals(isolate_two)).to eq false
end
describe 'GetCurrent' do
it 'can get the current isolate for the thread' do
isolate = V8::C::Isolate.New
isolate.Enter
expect(V8::C::Isolate.GetCurrent.Equals(isolate)).to eq true
end
it 'returns nil if there are no entered isolates' do
expect(V8::C::Isolate.GetCurrent).to be_nil
end
it 'is different accross threads' do
V8::C::Isolate.New.Enter
Thread.new do
expect(V8::C::Isolate.GetCurrent).to be_nil
end.join
end
end
it 'can enter and exit multiple times' do
isolate_one = V8::C::Isolate.New
isolate_two = V8::C::Isolate.New
isolate_one.Enter
isolate_one.Enter
isolate_two.Enter
expect(V8::C::Isolate.GetCurrent.Equals(isolate_two)).to eq true
isolate_two.Exit
expect(V8::C::Isolate.GetCurrent.Equals(isolate_one)).to eq true
isolate_one.Exit
expect(V8::C::Isolate.GetCurrent.Equals(isolate_one)).to eq true
isolate_one.Exit
end
end

View file

@ -1,41 +0,0 @@
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

View file

@ -4,13 +4,13 @@ describe V8::C::Object do
requires_v8_context
it 'can create a new object' do
expect(V8::C::Object.New).to be
expect(V8::C::Object.New(@isolate)).to be
end
it 'can store and retrieve a value' do
o = V8::C::Object.New
key = V8::C::String.NewFromUtf8('foo')
value = V8::C::String.NewFromUtf8('bar')
o = V8::C::Object.New(@isolate)
key = V8::C::String.NewFromUtf8(@isolate, 'foo')
value = V8::C::String.NewFromUtf8(@isolate, 'bar')
o.Set(key, value)
expect(o.Get(key).Utf8Value).to eq 'bar'
@ -48,10 +48,11 @@ describe V8::C::Object do
# end
it 'always returns the same ruby object for the same V8 object' do
one = V8::C::Object.New
two = V8::C::Object.New
key = V8::C::String.NewFromUtf8(@isolate, 'two')
one = V8::C::Object.New(@isolate)
two = V8::C::Object.New(@isolate)
one.Set('two', two)
expect(one.Get('two')).to be two
one.Set(key, two)
expect(one.Get(key)).to be two
end
end

View file

@ -4,34 +4,26 @@ describe V8::C::String do
requires_v8_context
it 'is a primitive' do
expect(V8::C::String.NewFromUtf8('test')).to be_a V8::C::Primitive
expect(V8::C::String.NewFromUtf8(@isolate, 'test')).to be_a V8::C::Primitive
end
it 'can create new strings' do
expect(V8::C::String.NewFromUtf8('test')).to be
expect(V8::C::String.NewFromUtf8(@isolate, 'test')).to be
end
it 'can be converted to a ruby string' do
expect(V8::C::String.NewFromUtf8('test').Utf8Value).to eq 'test'
expect(V8::C::String.NewFromUtf8(@isolate, 'test').Utf8Value).to eq 'test'
end
it 'can hold Unicode values outside the Basic Multilingual Plane' do
string = V8::C::String.NewFromUtf8("\u{100000}")
string = V8::C::String.NewFromUtf8(@isolate, "\u{100000}")
expect(string.Utf8Value).to eq "\u{100000}"
end
it 'can concatenate strings' do
string_one = V8::C::String.NewFromUtf8('Hello ')
string_two = V8::C::String.NewFromUtf8('World!')
string_one = V8::C::String.NewFromUtf8(@isolate, 'Hello ')
string_two = V8::C::String.NewFromUtf8(@isolate, 'World!')
expect(V8::C::String.Concat(string_one, string_two).Utf8Value).to eq 'Hello World!'
end
it 'can naturally translate ruby strings into v8 strings' do
expect(V8::C::String.Concat(V8::C::String.NewFromUtf8('Hello '), 'World').Utf8Value).to eq 'Hello World'
end
it 'can naturally translate ruby objects into v8 strings' do
expect(V8::C::String.Concat(V8::C::String.NewFromUtf8('forty two is '), 42).Utf8Value).to eq 'forty two is 42'
end
end

View file

@ -3,42 +3,38 @@ require 'c_spec_helper'
describe V8::C::Value do
requires_v8_context
def to_value(value)
object = V8::C::Object.New
key = V8::C::String.NewFromUtf8('key')
object.Set(key, value)
object.Get(key)
def convert(value)
V8::C::Value.FromRubyObject(@isolate, value).ToRubyObject
end
it 'converts strings' do
expect(to_value(V8::C::String.NewFromUtf8('value')).Utf8Value).to eq 'value'
expect(convert('value').Utf8Value).to eq 'value'
end
it 'converts nil' do
expect(to_value(nil)).to eq nil
expect(convert(nil)).to eq nil
end
it 'converts undefined to nil' do
object = V8::C::Object.New
key = V8::C::String.NewFromUtf8('key')
object = V8::C::Object.New(@isolate)
key = V8::C::String.NewFromUtf8(@isolate, 'key')
expect(object.Get(key)).to eq nil
end
it 'converts FixNums' do
expect(to_value(42)).to eq 42
expect(convert(42)).to eq 42
end
it 'converts booleans' do
expect(to_value(true)).to eq true
expect(to_value(false)).to eq false
expect(convert(true)).to eq true
expect(convert(false)).to eq false
end
it 'converts objects' do
object = V8::C::Object.New
object.Set(V8::C::String.NewFromUtf8('test'), 1)
object = V8::C::Object.New(@isolate)
object.Set(V8::C::String.NewFromUtf8(@isolate, 'test'), 1)
expect(to_value(object)).to eq object
expect(convert(object)).to eq object
end
end

View file

@ -11,28 +11,18 @@ module V8ContextHelpers
end
def bootstrap_v8_context
cleanup_isolates
@isolate = V8::C::Isolate.New
isolate = V8::C::Isolate.New
V8::C::Locker(isolate) do
isolate.Enter
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
isolate.Exit
end
end
def cleanup_isolates
V8::C::Isolate.GetCurrent.Exit while V8::C::Isolate.GetCurrent
end
end
RSpec.configure do |c|