@@ -23,33 +23,86 @@ namespace rr {
2323 * Note: You must call `Dispose()` on the isolate for its resources
2424 * to be released, otherwise, it will be leaked.
2525 */
26- class Isolate : public Pointer <v8::Isolate> {
26+ class Isolate {
2727 public:
2828 struct IsolateData ;
29+ static VALUE Class;
2930 static void Init ();
3031
3132 static VALUE New (VALUE self);
3233
33- inline Isolate (v8::Isolate* isolate) : Pointer<v8::Isolate>(isolate) {}
34- inline Isolate (VALUE value) : Pointer<v8::Isolate>(value) {}
34+ inline Isolate (IsolateData* data_) : data(data_) {}
35+ inline Isolate (v8::Isolate* isolate) :
36+ data((IsolateData*)isolate->GetData(0 )) {}
37+ inline Isolate (VALUE value) {
38+ Data_Get_Struct (value, struct IsolateData , data);
39+ }
40+
41+ /* *
42+ * Called, when Ruby no longer has any more references to this
43+ * instance of `V8::C::Isolate`. However, this does not
44+ * necessarily mean that we can delete the isolate data because there
45+ * could be outstanding objects... things like a V8::C::String
46+ * that are part of this isolate that have yet to be garbage
47+ * collected. The last object in the isolate, including the
48+ * isolate itself is responsible for deleting the isolate data.
49+ *
50+ */
51+ static void destroy (IsolateData* data) {
52+ Isolate isolate (data);
53+ isolate.decrementTotalReferences ();
54+ }
55+
56+ /* *
57+ * Every time we take out a reference to a V8 object, call this
58+ * method.
59+ */
60+ inline void incrementTotalReferences () {
61+ char counter (0 );
62+ data->total_ruby_references_queue .enqueue (counter);
63+ }
64+
65+ /* *
66+ * Every time a Ruby reference to a V8 object is garbage
67+ * collected, call this method. If this is the last object
68+ * associated with this isolate, then the isolate data can, and
69+ * will, be safely deleted.
70+ */
71+ inline bool decrementTotalReferences () {
72+ char counter (0 );
73+ if (data->total_ruby_references_queue .try_dequeue (counter)) {
74+ return true ;
75+ } else {
76+ delete data;
77+ return false ;
78+ }
79+ }
3580
3681 /* *
3782 * Converts the v8::Isolate into a Ruby Object, while setting up
3883 * its book keeping data. E.g.
3984 * VALUE rubyObject = Isolate(v8::Isolate::New());
4085 */
4186 virtual operator VALUE () {
42- return Data_Wrap_Struct (Class, &releaseAndMarkRetainedObjects, 0 , pointer );
87+ return Data_Wrap_Struct (Class, &releaseAndMarkRetainedObjects, &destroy, data );
4388 }
4489
4590 /* *
46- * Access the book-keeping data. e.g.
91+ * Convert this into a v8::Isolate* for those areas of the API
92+ * that call for it: E.g.
4793 *
48- * Isolate(self).data( );
94+ * v8::Context::New( Isolate(self));
4995 */
50- inline IsolateData* data () {
51- return (IsolateData*)pointer->GetData (0 );
52- }
96+ inline operator v8::Isolate*() { return data->isolate ; }
97+
98+ /* *
99+ * Dereference the underlying v8::Isolate so that we can call methods
100+ * on it. E.g.
101+ *
102+ * Isolate(self)->IsDead();
103+ */
104+
105+ inline v8::Isolate* operator ->() { return data->isolate ; }
53106
54107 /* *
55108 * Schedule a v8::Persistent reference to be be deleted with the next
@@ -59,7 +112,9 @@ namespace rr {
59112 */
60113 template <class T >
61114 inline void scheduleReleaseObject (v8::Persistent<T>* cell) {
62- data ()->v8_release_queue .enqueue ((v8::Persistent<void >*)cell);
115+ if (this ->decrementTotalReferences ()) {
116+ data->v8_release_queue .enqueue ((v8::Persistent<void >*)cell);
117+ }
63118 }
64119
65120 /* *
@@ -68,7 +123,7 @@ namespace rr {
68123 * where you do not hold any Ruby locks (such as the V8 GC thread)
69124 */
70125 inline void scheduleReleaseObject (VALUE object) {
71- data () ->rb_release_queue .enqueue (object);
126+ data->rb_release_queue .enqueue (object);
72127 }
73128
74129 /* *
@@ -80,7 +135,7 @@ namespace rr {
80135 * Note: should be called from a place where Ruby locks are held.
81136 */
82137 inline void retainObject (VALUE object) {
83- rb_funcall (data () ->retained_objects , rb_intern (" add" ), 1 , object);
138+ rb_funcall (data->retained_objects , rb_intern (" add" ), 1 , object);
84139 }
85140
86141 /* *
@@ -91,18 +146,15 @@ namespace rr {
91146 * Note: should be called from a place where Ruby locks are held.
92147 */
93148 inline void releaseObject (VALUE object) {
94- rb_funcall (data () ->retained_objects , rb_intern (" remove" ), 1 , object);
149+ rb_funcall (data->retained_objects , rb_intern (" remove" ), 1 , object);
95150 }
96151
97-
98152 /* *
99153 * The `gc_mark()` callback for this Isolate's
100154 * Data_Wrap_Struct. It releases all pending Ruby objects.
101155 */
102-
103- static void releaseAndMarkRetainedObjects (v8::Isolate* isolate_) {
104- Isolate isolate (isolate_);
105- IsolateData* data = isolate.data ();
156+ static void releaseAndMarkRetainedObjects (IsolateData* data) {
157+ Isolate isolate (data);
106158 VALUE object;
107159 while (data->rb_release_queue .try_dequeue (object)) {
108160 isolate.releaseObject (object);
@@ -118,7 +170,7 @@ namespace rr {
118170 static void clearReferences (v8::Isolate* i, v8::GCType type, v8::GCCallbackFlags flags) {
119171 Isolate isolate (i);
120172 v8::Persistent<void >* cell;
121- while (isolate.data () ->v8_release_queue .try_dequeue (cell)) {
173+ while (isolate.data ->v8_release_queue .try_dequeue (cell)) {
122174 cell->Reset ();
123175 delete cell;
124176 }
@@ -148,6 +200,11 @@ namespace rr {
148200 */
149201 struct IsolateData {
150202
203+ /* *
204+ * The actual Isolate
205+ */
206+ v8::Isolate* isolate;
207+
151208 /* *
152209 * An instance of `V8::RetainedObjects` that contains all
153210 * references held from from V8. As long as they are in this
@@ -157,7 +214,7 @@ namespace rr {
157214
158215 /* *
159216 * A custom ArrayBufferAllocator for this isolate. Why? because
160- * if you don't, it will segfault when you try and create an
217+ * if you don't, it will segfault when you try and create a
161218 * Context. That's why.
162219 */
163220 ArrayBufferAllocator array_buffer_allocator;
@@ -176,7 +233,21 @@ namespace rr {
176233 * needs it.
177234 */
178235 ConcurrentQueue<VALUE> rb_release_queue;
236+
237+ /* *
238+ * Contains a number of tokens representing all of the live Ruby
239+ * references that are currently active in this Isolate. Every
240+ * time a Ruby reference to a V8 object is added, an item is
241+ * added to this queue. Every time a ruby reference is garbage
242+ * collected, an item is pulled out of the queue. Whenever this
243+ * queue reaches 0, then it is time to delete the isolate data
244+ * since there are no more object associated with it.
245+ */
246+ ConcurrentQueue<int > total_ruby_references_queue;
247+
179248 };
249+
250+ IsolateData* data;
180251 };
181252}
182253
0 commit comments