mirror of
https://github.com/rubyjs/therubyracer
synced 2023-03-27 23:21:42 -04:00
Merge pull request #1 from stormbreakerbg/isolate-reference-queue
Isolate reference queue
This commit is contained in:
commit
369b0d323d
7 changed files with 3665 additions and 47 deletions
|
@ -6,10 +6,8 @@ namespace rr {
|
||||||
ClassBuilder("Context").
|
ClassBuilder("Context").
|
||||||
defineSingletonMethod("New", &New).
|
defineSingletonMethod("New", &New).
|
||||||
|
|
||||||
defineMethod("Dispose", &Dispose).
|
|
||||||
defineMethod("Enter", &Enter).
|
defineMethod("Enter", &Enter).
|
||||||
defineMethod("Exit", &Exit).
|
defineMethod("Exit", &Exit).
|
||||||
|
|
||||||
defineMethod("Global", &Global).
|
defineMethod("Global", &Global).
|
||||||
|
|
||||||
store(&Class);
|
store(&Class);
|
||||||
|
@ -26,7 +24,6 @@ namespace rr {
|
||||||
|
|
||||||
Isolate isolate(rb_isolate);
|
Isolate isolate(rb_isolate);
|
||||||
Locker lock(isolate);
|
Locker lock(isolate);
|
||||||
|
|
||||||
return Context(v8::Context::New(
|
return Context(v8::Context::New(
|
||||||
isolate
|
isolate
|
||||||
// TODO
|
// TODO
|
||||||
|
@ -37,11 +34,6 @@ namespace rr {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE Context::Dispose(VALUE self) {
|
|
||||||
Context(self).dispose();
|
|
||||||
return Qnil;
|
|
||||||
}
|
|
||||||
|
|
||||||
VALUE Context::Enter(VALUE self) {
|
VALUE Context::Enter(VALUE self) {
|
||||||
Context context(self);
|
Context context(self);
|
||||||
Locker lock(context.getIsolate());
|
Locker lock(context.getIsolate());
|
||||||
|
|
|
@ -8,11 +8,8 @@ namespace rr {
|
||||||
static void Init();
|
static void Init();
|
||||||
|
|
||||||
static VALUE New(int argc, VALUE argv[], VALUE self);
|
static VALUE New(int argc, VALUE argv[], VALUE self);
|
||||||
static VALUE Dispose(VALUE self);
|
|
||||||
|
|
||||||
static VALUE Enter(VALUE self);
|
static VALUE Enter(VALUE self);
|
||||||
static VALUE Exit(VALUE self);
|
static VALUE Exit(VALUE self);
|
||||||
|
|
||||||
static VALUE Global(VALUE self);
|
static VALUE Global(VALUE self);
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
|
|
@ -14,12 +14,21 @@ namespace rr {
|
||||||
store(&Class);
|
store(&Class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VALUE Isolate::New(VALUE self) {
|
VALUE Isolate::New(VALUE self) {
|
||||||
return Isolate(v8::Isolate::New());
|
Isolate::IsolateData* data = new IsolateData();
|
||||||
|
v8::Isolate::CreateParams create_params;
|
||||||
|
create_params.array_buffer_allocator = &data->array_buffer_allocator;
|
||||||
|
Isolate isolate(v8::Isolate::New(create_params));
|
||||||
|
isolate->SetData(0, new IsolateData());
|
||||||
|
isolate->AddGCPrologueCallback(&clearReferences);
|
||||||
|
return isolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE Isolate::Dispose(VALUE self) {
|
VALUE Isolate::Dispose(VALUE self) {
|
||||||
Isolate(self)->Dispose();
|
Isolate isolate(self);
|
||||||
|
delete isolate.data();
|
||||||
|
isolate->Dispose();
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
#ifndef RR_ISOLATE
|
#ifndef RR_ISOLATE
|
||||||
#define RR_ISOLATE
|
#define RR_ISOLATE
|
||||||
|
|
||||||
|
#include "vendor/concurrentqueue.h"
|
||||||
|
|
||||||
|
using namespace moodycamel;
|
||||||
|
|
||||||
namespace rr {
|
namespace rr {
|
||||||
/**
|
/**
|
||||||
* V8::C::Isolate
|
* V8::C::Isolate
|
||||||
|
@ -9,11 +13,19 @@ namespace rr {
|
||||||
* Represents a fully encapsulated V8 virtual machine. Allocated
|
* Represents a fully encapsulated V8 virtual machine. Allocated
|
||||||
* from Ruby by calling `V8::C::Isolate::New()`
|
* from Ruby by calling `V8::C::Isolate::New()`
|
||||||
*
|
*
|
||||||
|
* Every v8::Isolate wrapped in Ruby will have an instance of
|
||||||
|
* `IsolateData` embedded in it that can be used for bookkeeping
|
||||||
|
* between the V8 and Ruby worlds. For example, when v8 objects are
|
||||||
|
* no longer needed by ruby, they'll be enqueued for later release
|
||||||
|
* inside the V8 garbarge collection thread. This queue lives in the
|
||||||
|
* `IsolateData`
|
||||||
|
*
|
||||||
* Note: You must call `Dispose()` on the isolate for its resources
|
* Note: You must call `Dispose()` on the isolate for its resources
|
||||||
* to be released.
|
* to be released, otherwise, it will be leaked.
|
||||||
*/
|
*/
|
||||||
class Isolate : public Pointer<v8::Isolate> {
|
class Isolate : public Pointer<v8::Isolate> {
|
||||||
public:
|
public:
|
||||||
|
struct IsolateData;
|
||||||
static void Init();
|
static void Init();
|
||||||
|
|
||||||
static VALUE New(VALUE self);
|
static VALUE New(VALUE self);
|
||||||
|
@ -21,11 +33,84 @@ namespace rr {
|
||||||
inline Isolate(v8::Isolate* isolate) : Pointer<v8::Isolate>(isolate) {}
|
inline Isolate(v8::Isolate* isolate) : Pointer<v8::Isolate>(isolate) {}
|
||||||
inline Isolate(VALUE value) : Pointer<v8::Isolate>(value) {}
|
inline Isolate(VALUE value) : Pointer<v8::Isolate>(value) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the v8::Isolate into a Ruby Object, while setting up
|
||||||
|
* its book keeping data. E.g.
|
||||||
|
* VALUE rubyObject = Isolate(v8::Isolate::New());
|
||||||
|
*/
|
||||||
inline operator VALUE() {
|
inline operator VALUE() {
|
||||||
return Data_Wrap_Struct(Class, 0, 0, pointer);
|
return Data_Wrap_Struct(Class, 0, 0, pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access the book-keeping data. e.g.
|
||||||
|
*
|
||||||
|
* Isolate(self).data();
|
||||||
|
*/
|
||||||
|
inline IsolateData* data() {
|
||||||
|
return (IsolateData*)pointer->GetData(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule a v8::Persistent reference to be be deleted with the next
|
||||||
|
* invocation of the V8 Garbarge Collector
|
||||||
|
*/
|
||||||
|
template <class T>
|
||||||
|
inline void scheduleDelete(v8::Persistent<T>* cell) {
|
||||||
|
data()->queue.enqueue((v8::Persistent<void>*)cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instance of v8::GCPrologueCallback, this will run in the v8
|
||||||
|
* GC thread, and clear out all the references that have been
|
||||||
|
* released from Ruby.
|
||||||
|
*/
|
||||||
|
static void clearReferences(v8::Isolate* i, v8::GCType type, v8::GCCallbackFlags flags) {
|
||||||
|
Isolate isolate(i);
|
||||||
|
v8::Persistent<void>* cell;
|
||||||
|
while (isolate.data()->queue.try_dequeue(cell)) {
|
||||||
|
cell->Reset();
|
||||||
|
delete cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static VALUE Dispose(VALUE self);
|
static VALUE Dispose(VALUE self);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recent versions of V8 will segfault unless you pass in an
|
||||||
|
* ArrayBufferAllocator into the create params of an isolate. This
|
||||||
|
* is the simplest implementation possible.
|
||||||
|
*/
|
||||||
|
class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
|
||||||
|
public:
|
||||||
|
virtual void* Allocate(size_t length) {
|
||||||
|
void* data = AllocateUninitialized(length);
|
||||||
|
return data == NULL ? data : memset(data, 0, length);
|
||||||
|
}
|
||||||
|
virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
|
||||||
|
virtual void Free(void* data, size_t) { free(data); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data specific to the Ruby embedding. It has the same life span
|
||||||
|
* as the isolate.
|
||||||
|
*/
|
||||||
|
struct IsolateData {
|
||||||
|
/**
|
||||||
|
* A custom ArrayBufferAllocator for this isolate. Why? because
|
||||||
|
* if you don't, it will segfault when you try and create an
|
||||||
|
* Context. That's why.
|
||||||
|
*/
|
||||||
|
ArrayBufferAllocator array_buffer_allocator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue to hold unused references to v8 objects. Once Ruby is
|
||||||
|
* finished with an object it will be enqueued here so that it
|
||||||
|
* can be released by the v8 garbarge collector later.
|
||||||
|
*/
|
||||||
|
ConcurrentQueue<v8::Persistent<void>*> queue;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
44
ext/v8/ref.h
44
ext/v8/ref.h
|
@ -32,13 +32,14 @@ namespace rr {
|
||||||
template <class T>
|
template <class T>
|
||||||
class Ref {
|
class Ref {
|
||||||
public:
|
public:
|
||||||
|
struct Holder;
|
||||||
Ref(VALUE value) {
|
Ref(VALUE value) {
|
||||||
this->value = value;
|
this->value = value;
|
||||||
Holder* holder = unwrapHolder();
|
Holder* holder = unwrapHolder();
|
||||||
|
|
||||||
if (holder) {
|
if (holder) {
|
||||||
this->isolate = holder->isolate;
|
this->isolate = holder->isolate;
|
||||||
this->handle = v8::Local<T>::New(holder->isolate, *holder->handle);
|
this->handle = v8::Local<T>::New(holder->isolate, *holder->cell);
|
||||||
} else {
|
} else {
|
||||||
this->isolate = NULL;
|
this->isolate = NULL;
|
||||||
this->handle = v8::Local<T>();
|
this->handle = v8::Local<T>();
|
||||||
|
@ -60,7 +61,7 @@ namespace rr {
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Data_Wrap_Struct(Class, 0, &Holder::destroy, new Holder(isolate, handle));
|
return Data_Wrap_Struct(Class, 0, &destroy, new Holder(isolate, handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -74,10 +75,8 @@ namespace rr {
|
||||||
return isolate;
|
return isolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
static void destroy(Holder* holder) {
|
||||||
Holder* holder = NULL;
|
delete holder;
|
||||||
Data_Get_Struct(this->value, class Holder, holder);
|
|
||||||
holder->dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -89,37 +88,16 @@ namespace rr {
|
||||||
inline v8::Handle<T> operator->() const { return *this; }
|
inline v8::Handle<T> operator->() const { return *this; }
|
||||||
inline v8::Handle<T> operator*() const { return *this; }
|
inline v8::Handle<T> operator*() const { return *this; }
|
||||||
|
|
||||||
class Holder {
|
struct Holder {
|
||||||
friend class Ref;
|
Holder(v8::Isolate* isolate, v8::Handle<T> handle) :
|
||||||
public:
|
isolate(isolate), cell(new v8::Persistent<T>(isolate, handle)) {}
|
||||||
Holder(v8::Isolate* isolate, v8::Handle<T> handle) {
|
|
||||||
this->disposed_p = false;
|
|
||||||
this->isolate = isolate;
|
|
||||||
this->handle = new v8::Persistent<T>(isolate, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~Holder() {
|
virtual ~Holder() {
|
||||||
this->dispose();
|
Isolate(isolate).scheduleDelete<T>(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
|
||||||
if (!this->disposed_p) {
|
|
||||||
handle->Reset();
|
|
||||||
delete handle;
|
|
||||||
this->disposed_p = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
v8::Isolate* isolate;
|
v8::Isolate* isolate;
|
||||||
v8::Persistent<T>* handle;
|
v8::Persistent<T>* cell;
|
||||||
bool disposed_p;
|
|
||||||
|
|
||||||
static void destroy(Holder* holder) {
|
|
||||||
holder->dispose();
|
|
||||||
|
|
||||||
// TODO: Use a nonblocking queue with `AddGCPrologueCallback`
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static VALUE Class;
|
static VALUE Class;
|
||||||
|
@ -128,7 +106,7 @@ namespace rr {
|
||||||
Holder* unwrapHolder() const {
|
Holder* unwrapHolder() const {
|
||||||
if (RTEST(this->value)) {
|
if (RTEST(this->value)) {
|
||||||
Holder* holder = NULL;
|
Holder* holder = NULL;
|
||||||
Data_Get_Struct(this->value, class Holder, holder);
|
Data_Get_Struct(this->value, struct Holder, holder);
|
||||||
return holder;
|
return holder;
|
||||||
} else {
|
} else {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
3542
ext/v8/vendor/concurrentqueue.h
vendored
Normal file
3542
ext/v8/vendor/concurrentqueue.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
15
spec/c/context_spec.rb
Normal file
15
spec/c/context_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
require 'c_spec_helper'
|
||||||
|
|
||||||
|
describe V8::C::Context do
|
||||||
|
let(:isolate) { V8::C::Isolate::New() }
|
||||||
|
let(:context) { V8::C::Context::New(isolate) }
|
||||||
|
around do |example|
|
||||||
|
V8::C::HandleScope(isolate) do
|
||||||
|
example.run
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can be instantiated" do
|
||||||
|
expect(context).to be
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue