Skip to content
This repository was archived by the owner on Dec 4, 2023. It is now read-only.

Commit 8cf68e3

Browse files
committed
Merge pull request #363 from cowboyd/coordinate-isolate-teardown
Coordinate isolate teardown
2 parents d1dd01d + 0ddf0be commit 8cf68e3

5 files changed

Lines changed: 107 additions & 40 deletions

File tree

ext/v8/isolate.cc

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,42 +4,39 @@
44

55
namespace rr {
66

7+
VALUE Isolate::Class;
8+
79
void Isolate::Init() {
810
rb_eval_string("require 'v8/retained_objects'");
911
ClassBuilder("Isolate").
1012
defineSingletonMethod("New", &New).
1113

1214
defineMethod("Dispose", &Isolate::Dispose).
13-
defineMethod("Equals", &rr::Isolate::PointerEquals).
1415

1516
store(&Class);
1617
}
1718

18-
1919
VALUE Isolate::New(VALUE self) {
2020
Isolate::IsolateData* data = new IsolateData();
21+
2122
VALUE rb_cRetainedObjects = rb_eval_string("V8::RetainedObjects");
2223
data->retained_objects = rb_funcall(rb_cRetainedObjects, rb_intern("new"), 0);
2324

2425
v8::Isolate::CreateParams create_params;
2526
create_params.array_buffer_allocator = &data->array_buffer_allocator;
27+
v8::Isolate* isolate = v8::Isolate::New(create_params);
28+
2629

27-
Isolate isolate(v8::Isolate::New(create_params));
2830
isolate->SetData(0, data);
2931
isolate->AddGCPrologueCallback(&clearReferences);
3032

31-
return isolate;
33+
data->isolate = isolate;
34+
return Isolate(isolate);
3235
}
3336

3437
VALUE Isolate::Dispose(VALUE self) {
3538
Isolate isolate(self);
36-
delete isolate.data();
3739
isolate->Dispose();
3840
return Qnil;
3941
}
40-
41-
template <>
42-
void Pointer<v8::Isolate>::unwrap(VALUE value) {
43-
Data_Get_Struct(value, class v8::Isolate, pointer);
44-
}
4542
}

ext/v8/isolate.h

Lines changed: 91 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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

ext/v8/ref.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ namespace rr {
3838
Holder* holder = unwrapHolder();
3939

4040
if (holder) {
41-
this->isolate = holder->isolate;
42-
this->handle = v8::Local<T>::New(holder->isolate, *holder->cell);
41+
this->isolate = holder->data->isolate;
42+
this->handle = v8::Local<T>::New(holder->data->isolate, *holder->cell);
4343
} else {
4444
this->isolate = NULL;
4545
this->handle = v8::Local<T>();
@@ -94,13 +94,15 @@ namespace rr {
9494

9595
struct Holder {
9696
Holder(v8::Isolate* isolate, v8::Handle<T> handle) :
97-
isolate(isolate), cell(new v8::Persistent<T>(isolate, handle)) {}
97+
data((Isolate::IsolateData*)isolate->GetData(0)), cell(new v8::Persistent<T>(isolate, handle)) {
98+
Isolate(data).incrementTotalReferences();
99+
}
98100

99101
virtual ~Holder() {
100-
Isolate(isolate).scheduleReleaseObject<T>(cell);
102+
Isolate(data).scheduleReleaseObject<T>(cell);
101103
}
102104

103-
v8::Isolate* isolate;
105+
Isolate::IsolateData* data;
104106
v8::Persistent<T>* cell;
105107
};
106108

spec/c/isolate_spec.rb

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,6 @@
77
expect(isolate).to be
88
end
99

10-
it 'can be tested for equality' do
11-
expect(isolate.Equals(isolate)).to eq true
12-
expect(isolate.Equals(V8::C::Isolate::New())).to eq false
13-
end
14-
1510
it "can be disposed of" do
1611
isolate.Dispose()
1712
end

spec/c_spec_helper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ def bootstrap_v8_context
2222
@ctx.Exit
2323
end
2424
end
25+
ensure
26+
@isolate.Dispose()
2527
end
2628
end
2729

0 commit comments

Comments
 (0)