2015-07-04 12:20:50 -05:00
|
|
|
// -*- mode: c++ -*-
|
2015-03-18 21:50:59 +00:00
|
|
|
#ifndef RR_ISOLATE
|
|
|
|
#define RR_ISOLATE
|
|
|
|
|
2015-07-05 11:49:26 -05:00
|
|
|
#include "vendor/concurrentqueue.h"
|
|
|
|
|
|
|
|
using namespace moodycamel;
|
|
|
|
|
2015-03-18 21:50:59 +00:00
|
|
|
namespace rr {
|
2015-07-04 12:20:50 -05:00
|
|
|
/**
|
|
|
|
* V8::C::Isolate
|
|
|
|
*
|
|
|
|
* Represents a fully encapsulated V8 virtual machine. Allocated
|
|
|
|
* from Ruby by calling `V8::C::Isolate::New()`
|
|
|
|
*
|
2015-07-05 11:26:03 -05:00
|
|
|
* 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`
|
|
|
|
*
|
2015-07-04 12:20:50 -05:00
|
|
|
* Note: You must call `Dispose()` on the isolate for its resources
|
2015-07-05 11:26:03 -05:00
|
|
|
* to be released, otherwise, it will be leaked.
|
2015-07-04 12:20:50 -05:00
|
|
|
*/
|
2015-03-18 21:50:59 +00:00
|
|
|
class Isolate : public Pointer<v8::Isolate> {
|
|
|
|
public:
|
2015-07-05 11:49:26 -05:00
|
|
|
struct IsolateData;
|
2015-03-18 21:50:59 +00:00
|
|
|
static void Init();
|
2015-03-21 09:51:17 +00:00
|
|
|
|
2015-03-18 21:50:59 +00:00
|
|
|
static VALUE New(VALUE self);
|
|
|
|
|
|
|
|
inline Isolate(v8::Isolate* isolate) : Pointer<v8::Isolate>(isolate) {}
|
|
|
|
inline Isolate(VALUE value) : Pointer<v8::Isolate>(value) {}
|
|
|
|
|
2015-07-05 11:26:03 -05:00
|
|
|
/**
|
|
|
|
* Converts the v8::Isolate into a Ruby Object, while setting up
|
|
|
|
* its book keeping data. E.g.
|
|
|
|
* VALUE rubyObject = Isolate(v8::Isolate::New());
|
|
|
|
*/
|
2015-07-06 00:36:25 -05:00
|
|
|
virtual operator VALUE() {
|
|
|
|
return Data_Wrap_Struct(Class, &releaseAndMarkRetainedObjects, 0, pointer);
|
2015-03-18 21:50:59 +00:00
|
|
|
}
|
|
|
|
|
2015-07-05 11:26:03 -05:00
|
|
|
/**
|
|
|
|
* Access the book-keeping data. e.g.
|
|
|
|
*
|
|
|
|
* Isolate(self).data();
|
|
|
|
*/
|
|
|
|
inline IsolateData* data() {
|
|
|
|
return (IsolateData*)pointer->GetData(0);
|
|
|
|
}
|
|
|
|
|
2015-07-05 19:03:38 -05:00
|
|
|
/**
|
|
|
|
* 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) {
|
2015-07-06 00:38:10 -05:00
|
|
|
data()->v8_release_queue.enqueue((v8::Persistent<void>*)cell);
|
2015-07-05 11:49:26 -05:00
|
|
|
}
|
2015-07-05 11:26:03 -05:00
|
|
|
|
2015-07-06 00:36:25 -05:00
|
|
|
inline void retainObject(VALUE object) {
|
|
|
|
rb_funcall(data()->retained_objects, rb_intern("add"), 1, object);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void releaseObject(VALUE object) {
|
|
|
|
rb_funcall(data()->retained_objects, rb_intern("remove"), 1, object);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void scheduleReleaseObject(VALUE object) {
|
|
|
|
data()->release_queue.enqueue(object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void releaseAndMarkRetainedObjects(v8::Isolate* isolate_) {
|
|
|
|
Isolate isolate(isolate_);
|
|
|
|
IsolateData* data = isolate.data();
|
|
|
|
VALUE object;
|
|
|
|
while (data->release_queue.try_dequeue(object)) {
|
|
|
|
isolate.releaseObject(object);
|
|
|
|
}
|
|
|
|
rb_gc_mark(data->retained_objects);
|
|
|
|
}
|
|
|
|
|
2015-07-05 19:33:05 -05:00
|
|
|
/**
|
|
|
|
* 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;
|
2015-07-06 00:38:10 -05:00
|
|
|
while (isolate.data()->v8_release_queue.try_dequeue(cell)) {
|
2015-07-05 19:33:05 -05:00
|
|
|
cell->Reset();
|
|
|
|
delete cell;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-05 11:49:26 -05:00
|
|
|
static VALUE Dispose(VALUE self);
|
2015-07-05 11:26:03 -05:00
|
|
|
|
2015-07-05 19:03:38 -05:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2015-07-05 11:49:26 -05:00
|
|
|
struct IsolateData {
|
2015-07-06 00:36:25 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* An instance of `V8::RetainedObjects` that contains all
|
|
|
|
* references held from from V8. As long as they are in this
|
|
|
|
* list, they won't be gc'd by Ruby.
|
|
|
|
*/
|
|
|
|
VALUE retained_objects;
|
|
|
|
|
2015-07-05 19:03:38 -05:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2015-07-06 00:38:10 -05:00
|
|
|
ConcurrentQueue<v8::Persistent<void>*> v8_release_queue;
|
2015-07-06 00:36:25 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Queue to hold
|
|
|
|
*/
|
|
|
|
ConcurrentQueue<VALUE> release_queue;
|
2015-07-05 11:26:03 -05:00
|
|
|
};
|
2015-03-18 21:50:59 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|