therubyracer 0.9.0 → 0.9.1beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of therubyracer might be problematic. Click here for more details.
- data/.gitignore +2 -5
- data/.yardopts +1 -0
- data/ext/v8/v8_handle.cpp +54 -6
- data/ext/v8/v8_handle.h +4 -0
- data/ext/v8/v8_locker.cpp +115 -17
- data/lib/v8.rb +1 -0
- data/lib/v8/c/locker.rb +18 -0
- data/lib/v8/portal/proxies.rb +0 -4
- data/lib/v8/version.rb +1 -1
- data/specthread/spec_helper.rb +2 -0
- data/specthread/threading_spec.rb +13 -0
- metadata +10 -9
data/.gitignore
CHANGED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
lib/**/*.rb ext/**/*.cpp
|
data/ext/v8/v8_handle.cpp
CHANGED
@@ -4,28 +4,72 @@
|
|
4
4
|
|
5
5
|
using namespace v8;
|
6
6
|
|
7
|
+
/**
|
8
|
+
* Creates a new Persistent storage cell for `handle`
|
9
|
+
* so that we can reference it from Ruby.
|
10
|
+
*/
|
7
11
|
v8_handle::v8_handle(Handle<void> handle) : handle(Persistent<void>::New(handle)) {
|
8
12
|
this->weakref_callback = Qnil;
|
9
13
|
this->weakref_callback_parameters = Qnil;
|
10
14
|
this->dead = false;
|
11
15
|
}
|
12
16
|
|
13
|
-
v8_handle::~v8_handle() {
|
14
|
-
handle.Dispose();
|
15
|
-
handle.Clear();
|
16
|
-
dead = true;
|
17
|
-
}
|
17
|
+
v8_handle::~v8_handle() {}
|
18
18
|
|
19
19
|
namespace {
|
20
|
+
/**
|
21
|
+
* Holds dead references, that are no longer being held in Ruby, so that they can be garbage collected
|
22
|
+
* inside of V8
|
23
|
+
*/
|
24
|
+
VALUE handle_queue;
|
25
|
+
|
26
|
+
/**
|
27
|
+
* Invoked by the Ruby garbage collector whenever it determines that this handle is
|
28
|
+
* still reachable. We in turn, mark our weak callback parameters, so that it knows
|
29
|
+
* they are reachable too.
|
30
|
+
*/
|
20
31
|
void v8_handle_mark(v8_handle* handle) {
|
21
32
|
rb_gc_mark(handle->weakref_callback);
|
22
33
|
rb_gc_mark(handle->weakref_callback_parameters);
|
23
34
|
}
|
24
35
|
|
36
|
+
/**
|
37
|
+
* Deallocates this handle. This function is invoked on Zombie handles after they have
|
38
|
+
* been released from V8 and finally
|
39
|
+
*/
|
25
40
|
void v8_handle_free(v8_handle* handle) {
|
26
41
|
delete handle;
|
27
42
|
}
|
28
43
|
|
44
|
+
/**
|
45
|
+
* Whenver a V8::C::Handle becomes garbage collected, we do not free it immediately.
|
46
|
+
* instead, we put them into a "zombie" queue, where its corresponding V8 storage cell
|
47
|
+
* can be released safely while the V8 engine is running. A zombie Ruby object is
|
48
|
+
* created to wrap it so that it can be stored in the queue.
|
49
|
+
*/
|
50
|
+
void v8_handle_enqueue(v8_handle* handle) {
|
51
|
+
handle->dead = true;
|
52
|
+
VALUE zombie = Data_Wrap_Struct(rr_v8_handle_class(), 0, v8_handle_free, handle);
|
53
|
+
rb_ary_unshift(handle_queue, zombie);
|
54
|
+
}
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Drains the dead handle queue, and releases them from V8
|
58
|
+
*
|
59
|
+
* This implements the V8 `GCPrologueCallback` and is registered to run before
|
60
|
+
* each invocation of the V8 garbage collector. It empties the queue of dead handles
|
61
|
+
* and disposes of them. It is important to do this operations inside V8 so that
|
62
|
+
* Ruby garbage collection is never locked, and never touches V8.
|
63
|
+
*/
|
64
|
+
void v8_handle_dequeue(GCType type, GCCallbackFlags flags) {
|
65
|
+
for (VALUE handle = rb_ary_pop(handle_queue); RTEST(handle); handle = rb_ary_pop(handle_queue)) {
|
66
|
+
v8_handle* dead = NULL;
|
67
|
+
Data_Get_Struct(handle, struct v8_handle, dead);
|
68
|
+
dead->handle.Dispose();
|
69
|
+
dead->handle.Clear();
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
29
73
|
VALUE New(VALUE self, VALUE handle) {
|
30
74
|
if (RTEST(handle)) {
|
31
75
|
Persistent<void> that = rr_v8_handle<void>(handle);
|
@@ -101,11 +145,15 @@ void rr_init_handle() {
|
|
101
145
|
rr_define_method(HandleClass, "ClearWeak", ClearWeak, 0);
|
102
146
|
rr_define_method(HandleClass, "IsNearDeath", IsNearDeath, 0);
|
103
147
|
rr_define_method(HandleClass, "IsWeak", IsWeak, 0);
|
148
|
+
|
149
|
+
handle_queue = rb_ary_new();
|
150
|
+
rb_gc_register_address(&handle_queue);
|
151
|
+
V8::AddGCPrologueCallback(v8_handle_dequeue);
|
104
152
|
}
|
105
153
|
|
106
154
|
VALUE rr_v8_handle_new(VALUE klass, v8::Handle<void> handle) {
|
107
155
|
v8_handle* new_handle = new v8_handle(handle);
|
108
|
-
return Data_Wrap_Struct(klass, v8_handle_mark,
|
156
|
+
return Data_Wrap_Struct(klass, v8_handle_mark, v8_handle_enqueue, new_handle);
|
109
157
|
}
|
110
158
|
|
111
159
|
VALUE rr_v8_handle_class() {
|
data/ext/v8/v8_handle.h
CHANGED
@@ -4,6 +4,10 @@
|
|
4
4
|
#include <v8.h>
|
5
5
|
#include "ruby.h"
|
6
6
|
|
7
|
+
/**
|
8
|
+
* Holds a reference to a V8 heap object. This serves as the base
|
9
|
+
* class for all of the low-level proxies that reference into V8
|
10
|
+
*/
|
7
11
|
struct v8_handle {
|
8
12
|
v8_handle(v8::Handle<void> object);
|
9
13
|
virtual ~v8_handle();
|
data/ext/v8/v8_locker.cpp
CHANGED
@@ -4,38 +4,136 @@
|
|
4
4
|
using namespace v8;
|
5
5
|
|
6
6
|
namespace {
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
namespace Lock {
|
8
|
+
|
9
|
+
/**
|
10
|
+
* Document-method: V8::C::Locker#new
|
11
|
+
*
|
12
|
+
* Allocates and returns a new `v8::Locker` object. The thread that instantiated
|
13
|
+
* this object will hold the V8 interpreter lock until it is released with a
|
14
|
+
* corresponding call to {#delete}.
|
15
|
+
*
|
16
|
+
* It critical that you call {#delete} to deallocate it, preferably within the same method.
|
17
|
+
* If you don't, two bad things will happen:
|
18
|
+
*
|
19
|
+
* 1. You'll leak the underlying C++ object
|
20
|
+
* 1. Worse, you'll leave the V8 vm locked to this thread forever
|
21
|
+
*
|
22
|
+
* It's dangerous! Be sure to `ensure`.
|
23
|
+
*
|
24
|
+
* for detailed semantics see the locking {API http://izs.me/v8-docs/classv8_1_1Unlocker.html}
|
25
|
+
*
|
26
|
+
* @return [V8::C::Locker] the new locker
|
27
|
+
*/
|
28
|
+
|
29
|
+
VALUE New(VALUE LockerClass) {
|
30
|
+
Locker* locker = new Locker();
|
31
|
+
return Data_Wrap_Struct(LockerClass, 0, 0, (void*)locker);
|
32
|
+
}
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Document-method: V8::C::Locker#delete
|
36
|
+
*
|
37
|
+
* Pop this lock off the stack for this thread. For a full run down of V8 locking
|
38
|
+
* semantics see the locking {API http://izs.me/v8-docs/classv8_1_1Unlocker.html}
|
39
|
+
* @return nil
|
40
|
+
*/
|
41
|
+
VALUE Delete(VALUE self) {
|
42
|
+
Locker* locker = 0;
|
43
|
+
Data_Get_Struct(self, class Locker, locker);
|
44
|
+
delete locker;
|
45
|
+
}
|
10
46
|
}
|
11
|
-
|
12
|
-
|
13
|
-
|
47
|
+
|
48
|
+
namespace Unlock {
|
49
|
+
/**
|
50
|
+
* Document-method: V8::C::Unlocker#new
|
51
|
+
*
|
52
|
+
* Allocates and returns a new `v8::UnLocker` object, temporarily releasing any locks that
|
53
|
+
* this thread is holding. It will reaquire all of the locksto {#delete}.
|
54
|
+
*
|
55
|
+
* This is a great thing to do when you want to call out to some code that might do some
|
56
|
+
* waiting, sleeping, and you want to politely let other threads use this VM.
|
57
|
+
*
|
58
|
+
* It critical that you call {#delete} to deallocate it, preferably within the same method.
|
59
|
+
* If you don't, two bad things will happen:
|
60
|
+
*
|
61
|
+
* 1. You'll leak the underlying C++ object
|
62
|
+
* 1. You won't restore the locks to your current thread, and will mess things up horribly
|
63
|
+
*
|
64
|
+
* It's dangerous! Be sure to `ensure`.
|
65
|
+
*
|
66
|
+
* For details on V8 locking semantics, see the locking {API http://izs.me/v8-docs/classv8_1_1Unlocker.html}
|
67
|
+
* @return [V8::C::Unocker] the new locker
|
68
|
+
*/
|
69
|
+
VALUE New(VALUE UnlockerClass) {
|
70
|
+
Unlocker* unlocker = new Unlocker();
|
71
|
+
return Data_Wrap_Struct(UnlockerClass, 0, 0, (void*)unlocker);
|
72
|
+
}
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Document-method: V8::C::Unlocker#delete
|
76
|
+
*
|
77
|
+
* Restore any locks to the stack that were temporarily removed by this `Unlocker`.
|
78
|
+
* For a full run down, see semantics see the locking {API http://izs.me/v8-docs/classv8_1_1Unlocker.html}
|
79
|
+
* @return nil
|
80
|
+
*/
|
81
|
+
VALUE Delete(VALUE self) {
|
82
|
+
Unlocker* unlocker;
|
83
|
+
Data_Get_Struct(self, class Unlocker, unlocker);
|
84
|
+
delete unlocker;
|
85
|
+
}
|
14
86
|
}
|
15
|
-
|
16
|
-
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Document-method: V8::C::Locker#StartPreemption
|
90
|
+
* Start preemption.
|
91
|
+
* When preemption is started, a timer is fired every n milli seconds that will switch between
|
92
|
+
* multiple threads that are in contention for the V8 lock.
|
93
|
+
*
|
94
|
+
* @param [Integer] every_n_ms
|
95
|
+
* @return nil
|
96
|
+
*/
|
97
|
+
VALUE StartPreemption(VALUE self, VALUE every_n_ms) {
|
98
|
+
Locker::StartPreemption(NUM2INT(rb_to_int(every_n_ms)));
|
17
99
|
return Qnil;
|
18
100
|
}
|
101
|
+
|
102
|
+
/**
|
103
|
+
* Document-method: V8::C::Locker#StartPreemption
|
104
|
+
* Stop preemption
|
105
|
+
*/
|
19
106
|
VALUE StopPreemption(VALUE self) {
|
20
107
|
Locker::StopPreemption();
|
21
108
|
return Qnil;
|
22
109
|
}
|
110
|
+
|
111
|
+
/**
|
112
|
+
* Document-method: V8::C::Locker#IsLocked
|
113
|
+
* Returns whether or not the locker is locked by the current thread.
|
114
|
+
*/
|
23
115
|
VALUE IsLocked(VALUE self) {
|
24
116
|
return rr_v82rb(Locker::IsLocked());
|
25
117
|
}
|
118
|
+
|
119
|
+
/**
|
120
|
+
* Document-method: V8::C::Locker#IsActive
|
121
|
+
* Returns whether v8::Locker is being used by this V8 instance.
|
122
|
+
*/
|
26
123
|
VALUE IsActive(VALUE self) {
|
27
124
|
return rr_v82rb(Locker::IsActive());
|
28
125
|
}
|
29
126
|
}
|
30
127
|
|
31
128
|
void rr_init_v8_locker() {
|
32
|
-
VALUE
|
33
|
-
VALUE
|
34
|
-
|
35
|
-
|
36
|
-
rr_define_singleton_method(
|
37
|
-
|
38
|
-
rr_define_singleton_method(
|
39
|
-
rr_define_singleton_method(
|
40
|
-
rr_define_singleton_method(
|
129
|
+
VALUE LockerClass = rr_define_class("Locker");
|
130
|
+
VALUE UnlockerClass = rr_define_class("Unlocker");
|
131
|
+
rr_define_singleton_method(LockerClass, "new", Lock::New, 0);
|
132
|
+
rr_define_method(LockerClass, "delete", Lock::Delete, 0);
|
133
|
+
rr_define_singleton_method(UnlockerClass, "new", Unlock::New, 0);
|
134
|
+
rr_define_method(UnlockerClass, "delete", Unlock::Delete, 0);
|
135
|
+
rr_define_singleton_method(LockerClass, "StartPreemption", StartPreemption, 1);
|
136
|
+
rr_define_singleton_method(LockerClass, "StopPreemption", StopPreemption, 0);
|
137
|
+
rr_define_singleton_method(LockerClass, "IsLocked", IsLocked, 0);
|
138
|
+
rr_define_singleton_method(LockerClass, "IsActive", IsActive, 0);
|
41
139
|
}
|
data/lib/v8.rb
CHANGED
data/lib/v8/c/locker.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module V8
|
2
|
+
module C
|
3
|
+
# Shim to support the old style locking syntax. We don't currently
|
4
|
+
# deprecate this because it might make a comeback at some point.
|
5
|
+
#
|
6
|
+
# to synchronize access to V8, and to associate V8 with this thread:
|
7
|
+
#
|
8
|
+
# Locker() do
|
9
|
+
# #... interact with v8
|
10
|
+
# end
|
11
|
+
def self.Locker
|
12
|
+
lock = Locker.new
|
13
|
+
yield
|
14
|
+
ensure
|
15
|
+
lock.delete
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/v8/portal/proxies.rb
CHANGED
@@ -43,7 +43,6 @@ module V8
|
|
43
43
|
@js_proxies_js2rb[proxy] = target
|
44
44
|
@js_proxies_rb2js[target] = proxy
|
45
45
|
proxy.MakeWeak(nil, @clear_js_proxy)
|
46
|
-
V8::C::V8::AdjustAmountOfExternalAllocatedMemory(16 * 1024)
|
47
46
|
end
|
48
47
|
|
49
48
|
# Lookup the JavaScript proxy for a natively Ruby object
|
@@ -66,7 +65,6 @@ module V8
|
|
66
65
|
@rb_proxies_rb2js[proxy.object_id] = target
|
67
66
|
@rb_proxies_js2rb[target] = proxy.object_id
|
68
67
|
ObjectSpace.define_finalizer(proxy, @clear_rb_proxy)
|
69
|
-
V8::C::V8::AdjustAmountOfExternalAllocatedMemory(8 * 1024)
|
70
68
|
end
|
71
69
|
|
72
70
|
# Looks up the Ruby proxy for an object that is natively JavaScript
|
@@ -115,7 +113,6 @@ module V8
|
|
115
113
|
rb = @js2rb[proxy]
|
116
114
|
@js2rb.delete(proxy)
|
117
115
|
@rb2js.delete(rb)
|
118
|
-
V8::C::V8::AdjustAmountOfExternalAllocatedMemory(-16 * 1024)
|
119
116
|
end
|
120
117
|
end
|
121
118
|
|
@@ -146,7 +143,6 @@ module V8
|
|
146
143
|
if js = @rb2js[proxy_id]
|
147
144
|
@rb2js.delete(proxy_id)
|
148
145
|
@js2rb.delete(js)
|
149
|
-
V8::C::V8::AdjustAmountOfExternalAllocatedMemory(-8 * 1024)
|
150
146
|
end
|
151
147
|
end
|
152
148
|
end
|
data/lib/v8/version.rb
CHANGED
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: therubyracer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
version: 0.9.
|
4
|
+
prerelease: 5
|
5
|
+
version: 0.9.1beta1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Charles Lowell
|
@@ -11,7 +11,7 @@ autorequire:
|
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
13
|
|
14
|
-
date: 2011-06-
|
14
|
+
date: 2011-06-15 00:00:00 -05:00
|
15
15
|
default_executable:
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
@@ -70,6 +70,7 @@ files:
|
|
70
70
|
- .gitignore
|
71
71
|
- .gitmodules
|
72
72
|
- .rspec
|
73
|
+
- .yardopts
|
73
74
|
- Changelog.md
|
74
75
|
- Gemfile
|
75
76
|
- README.md
|
@@ -120,6 +121,7 @@ files:
|
|
120
121
|
- lib/v8.rb
|
121
122
|
- lib/v8/access.rb
|
122
123
|
- lib/v8/array.rb
|
124
|
+
- lib/v8/c/locker.rb
|
123
125
|
- lib/v8/cli.rb
|
124
126
|
- lib/v8/context.rb
|
125
127
|
- lib/v8/error.rb
|
@@ -150,6 +152,8 @@ files:
|
|
150
152
|
- specmem/object_memspec.rb
|
151
153
|
- specmem/proxies_memspec.rb
|
152
154
|
- specmem/spec_helper.rb
|
155
|
+
- specthread/spec_helper.rb
|
156
|
+
- specthread/threading_spec.rb
|
153
157
|
- therubyracer.gemspec
|
154
158
|
- spec/redjs/.gitignore
|
155
159
|
- spec/redjs/README.txt
|
@@ -170,19 +174,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
170
174
|
requirements:
|
171
175
|
- - ">="
|
172
176
|
- !ruby/object:Gem::Version
|
173
|
-
hash: -
|
177
|
+
hash: -227927920256907833
|
174
178
|
segments:
|
175
179
|
- 0
|
176
180
|
version: "0"
|
177
181
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
178
182
|
none: false
|
179
183
|
requirements:
|
180
|
-
- - "
|
184
|
+
- - ">"
|
181
185
|
- !ruby/object:Gem::Version
|
182
|
-
|
183
|
-
segments:
|
184
|
-
- 0
|
185
|
-
version: "0"
|
186
|
+
version: 1.3.1
|
186
187
|
requirements: []
|
187
188
|
|
188
189
|
rubyforge_project: therubyracer
|