mirror of
https://github.com/rubyjs/therubyracer
synced 2023-03-27 23:21:42 -04:00
use a lock-free queue to finalize v8 handles.
This commit is contained in:
parent
3c8bd2bb1d
commit
73d363c11a
2 changed files with 52 additions and 21 deletions
43
ext/v8/gc.cc
43
ext/v8/gc.cc
|
@ -1,25 +1,42 @@
|
|||
#include "rr.h"
|
||||
|
||||
namespace rr {
|
||||
VALUE queue;
|
||||
GC::Queue* queue;
|
||||
|
||||
namespace gc {
|
||||
VALUE dequeue(VALUE q) {
|
||||
return rb_funcall(q, rb_intern("pop"), 1, Qtrue);
|
||||
GC::Queue::Queue() : first(0), divider(0), last(0){
|
||||
first = new GC::Queue::Node(NULL);
|
||||
divider = first;
|
||||
last = first;
|
||||
}
|
||||
|
||||
void GC::Queue::Enqueue(void* reference) {
|
||||
last->next = new Node(reference);
|
||||
last = last->next;
|
||||
while (first != divider) {
|
||||
Node* tmp = first;
|
||||
first = first->next;
|
||||
delete tmp;
|
||||
}
|
||||
}
|
||||
void GC::Push(VALUE phantom) {
|
||||
rb_funcall(queue, rb_intern("<<"), 1, phantom);
|
||||
|
||||
void* GC::Queue::Dequeue() {
|
||||
void* result = NULL;
|
||||
if (divider != last) {
|
||||
result = divider->next->value;
|
||||
divider = divider->next;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void GC::Pop(v8::GCType type, v8::GCCallbackFlags flags) {
|
||||
VALUE next = rb_protect(&gc::dequeue, queue, NULL);
|
||||
rb_set_errinfo(Qnil);
|
||||
if (!NIL_P(next)) {
|
||||
Phantom(next).destroy();
|
||||
|
||||
void GC::Finalize(void* phantom) {
|
||||
queue->Enqueue(phantom);
|
||||
}
|
||||
void GC::Drain(v8::GCType type, v8::GCCallbackFlags flags) {
|
||||
for(Phantom phantom = queue->Dequeue(); phantom.NotNull(); phantom = queue->Dequeue()) {
|
||||
phantom.destroy();
|
||||
}
|
||||
}
|
||||
void GC::Init() {
|
||||
rb_gc_register_address(&queue);
|
||||
queue = rb_eval_string("require 'thread'; Queue.new");
|
||||
queue = new GC::Queue();
|
||||
}
|
||||
}
|
30
ext/v8/rr.h
30
ext/v8/rr.h
|
@ -20,8 +20,23 @@ protected:
|
|||
|
||||
class GC {
|
||||
public:
|
||||
static void Push(VALUE phantom);
|
||||
static void Pop(v8::GCType type, v8::GCCallbackFlags flags);
|
||||
class Queue {
|
||||
public:
|
||||
Queue();
|
||||
void Enqueue(void* phantom);
|
||||
void* Dequeue();
|
||||
private:
|
||||
struct Node {
|
||||
Node(void* val ) : value(val), next(NULL) { }
|
||||
void* value;
|
||||
Node* next;
|
||||
};
|
||||
Node* first; // for producer only
|
||||
Node* divider;
|
||||
Node* last;
|
||||
};
|
||||
static void Finalize(void* phantom);
|
||||
static void Drain(v8::GCType type, v8::GCCallbackFlags flags);
|
||||
static void Init();
|
||||
};
|
||||
|
||||
|
@ -52,21 +67,17 @@ public:
|
|||
Holder(v8::Handle<T> handle, VALUE klass) {
|
||||
this->handle = v8::Persistent<T>::New(handle);
|
||||
this->value = Data_Wrap_Struct(klass, 0, &Holder::enqueue, this);
|
||||
this->phantom = Data_Wrap_Struct(rb_cObject, 0, 0, this);
|
||||
rb_gc_register_address(&phantom);
|
||||
}
|
||||
virtual ~Holder() {
|
||||
handle.Dispose();
|
||||
rb_gc_unregister_address(&phantom);
|
||||
}
|
||||
protected:
|
||||
VALUE value;
|
||||
VALUE phantom;
|
||||
v8::Persistent<T> handle;
|
||||
|
||||
static void enqueue(Holder* holder) {
|
||||
holder->value = Qnil;
|
||||
GC::Push(holder->phantom);
|
||||
GC::Finalize(holder);
|
||||
}
|
||||
};
|
||||
Ref(Holder* holder) {
|
||||
|
@ -77,7 +88,10 @@ public:
|
|||
|
||||
class Phantom : public Ref<void> {
|
||||
public:
|
||||
inline Phantom(VALUE value) : Ref<void>(value) {}
|
||||
inline Phantom(void* reference) : Ref<void>((Ref<void>::Holder*)reference) {}
|
||||
inline bool NotNull() {
|
||||
return this->holder != NULL;
|
||||
}
|
||||
inline void destroy() {
|
||||
delete holder;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue