h8 0.1.4 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7384caa28f422353e348519ea0f56f9869f8243f
4
- data.tar.gz: ef389e8693081cf4c7173bbffd31e2127bcf978e
3
+ metadata.gz: 0d9eec220ddcefcec439cacbc06a3a1aa9df74a4
4
+ data.tar.gz: ff56274f0e8f976ea5afe8b35ed01259032330db
5
5
  SHA512:
6
- metadata.gz: ef76e195a1db60ca07987b46e5060830a73221c7e45daaf8e4fa8b8a8ab7540a25ed1f65740421cddeb19936d3abf8cd24f1a5ea5911292402c1f7b90b366875
7
- data.tar.gz: 171b71959ebba1159e62c771a8f529d648307667770dbda0c03bfdaa106abb45c293bd5b7781ff9258abf286886d4e300ae20cc09bb875c6001861a033a784bc
6
+ metadata.gz: 43eecac7e09f262bddbcb68fe49717c82f27778205bc2b6aa2bb0808b1ea6bc2131c7bdb3d808253a7a35134ff8da9c7cb5d92d190b86f6f46b5e39388782f1d
7
+ data.tar.gz: 9741491ec26a3698b9a431624c4726db9989afa6ece6e09773eec14a7dcc711ca1fd9163467cc27e27ce2a947daa3af74713f99e17edc6407793a956c965e0ac
data/README.md CHANGED
@@ -8,7 +8,8 @@ This gem was intended to replace therubyracer for many reasons:
8
8
  * therubyracer has critical bugs that are not fixed for a long time, under load it produces
9
9
  numerous frequent crashes.
10
10
 
11
- * therubyracer still uses antique version of V8, H8 uses the latest 3.31 branch
11
+ * therubyracer still uses antique version of V8, H8 uses the latest 3.31 branch, which, for example,
12
+ has the generators support.
12
13
 
13
14
  * H8 is designed to provide very tight and effective integration of two allocation systems and
14
15
  object models, passing the same objects between different systems wrapping and unwrapping them
@@ -36,11 +37,12 @@ uncaught javascript exceptions raise ruby error in ruby code.
36
37
 
37
38
  - Integrated CoffeeScript support
38
39
 
39
- ## Main difference from therubyracer/features not ready
40
+ - H8 is thread safe (using Lockers) and releases gvl when executing js code (and reqcquires it as
41
+ need), thus other ruby threads can work in parallel with javascript executing threads. Still,
42
+ h8 does not releases Locker when calling ruby code from javascript - for performance considerations.
43
+
40
44
 
41
- - H8 is thread safe (uses Lockers) but script is executed in the calling ruby thread without
42
- releasing gvl (other ruby threads can not perform on this core while javascript code runs). We are
43
- working on it, as simply releasing GVL and reacquring it may degrade performance.
45
+ ## Main difference from therubyracer/features not ready
44
46
 
45
47
  - labmda/proc passed as var to the context **does not receives first (this) argument
46
48
  automatically!**
@@ -98,11 +100,7 @@ Install first a valid v8 version. We provide a ready package!
98
100
 
99
101
  sudo apt-get install libv8-3.31-dev
100
102
 
101
- It should install prerequisites, if not, manually install
102
-
103
- sudo apt-get install libicu-dev
104
-
105
- You might also need to install GMP.
103
+ Usually it is all you need. Rarely, You might also need to install GMP.
106
104
 
107
105
  ### Setting up
108
106
 
data/ext/h8/JsCatcher.cpp CHANGED
@@ -14,7 +14,12 @@ JsCatcher::JsCatcher(H8* h8) : h8(h8), v8::TryCatch(h8->getIsolate()) {}
14
14
 
15
15
  void JsCatcher::throwIfCaught() {
16
16
  if( HasCaught() ) {
17
+ if( h8->isInterrupted() ) {
18
+ puts("INTERRUPTED!");
19
+ throw JsError(h8, "interrupted");
20
+ }
17
21
  if( !CanContinue() && HasTerminated() ) {
22
+ // if( HasTerminated() ) {
18
23
  throw JsTimeoutError(h8);
19
24
  }
20
25
  throw JsError(h8, Message(), Exception());
data/ext/h8/h8.cpp CHANGED
@@ -4,6 +4,7 @@
4
4
  #include <chrono>
5
5
 
6
6
  #include "h8.h"
7
+ #include <ruby/thread.h>
7
8
  #include "ruby_gate.h"
8
9
 
9
10
  void h8::JsError::raise() {
@@ -61,6 +62,43 @@ void h8::H8::ruby_mark_gc() const {
61
62
  ((AllocatedResource*) x)->rb_mark_gc();
62
63
  }
63
64
 
65
+ struct CallerParams {
66
+ h8::H8* h8;
67
+ Handle<Script>& script;
68
+ Local<Value>& result;
69
+ };
70
+
71
+ static void* script_caller(void* param) {
72
+ CallerParams *cp = (CallerParams*) param;
73
+ cp->h8->setGvlReleased(true);
74
+ cp->result = cp->script->Run();
75
+ return NULL;
76
+ }
77
+
78
+ static void unblock_script(void* param) {
79
+ h8::H8* h8 = (h8::H8*) param;
80
+ if( rb_thread_interrupted(rb_thread_current()) ) {
81
+ printf("UNBLOCK!!!! CALLED!!! Why? %p\n", param);
82
+ h8->setInterrupted();
83
+ h8->getIsolate()->TerminateExecution();
84
+ }
85
+ else {
86
+ puts("UBF not interrupted. why?");
87
+ }
88
+ }
89
+
90
+ void h8::H8::invoke(v8::Handle<v8::Script> script, Local<Value>& result) {
91
+ #if 1
92
+ CallerParams cp = { this, script, result };
93
+ rb_interrupted = false;
94
+ rb_thread_call_without_gvl(script_caller, &cp, NULL, NULL);
95
+ setGvlReleased(false);
96
+ #else
97
+ gvl_released = false;
98
+ result = script->Run();
99
+ #endif
100
+ }
101
+
64
102
  v8::Handle<v8::Value> h8::H8::eval(const char* script_utf, unsigned max_ms) {
65
103
  v8::EscapableHandleScope escape(isolate);
66
104
  Local<Value> result;
@@ -87,11 +125,11 @@ v8::Handle<v8::Value> h8::H8::eval(const char* script_utf, unsigned max_ms) {
87
125
  isolate->TerminateExecution();
88
126
  }
89
127
  });
90
- script->Run();
128
+ invoke(script, result);
91
129
  cv.notify_all();
92
130
  thr.join();
93
131
  } else {
94
- result = script->Run();
132
+ invoke(script, result);
95
133
  }
96
134
  try_catch.throwIfCaught();
97
135
  }
data/ext/h8/h8.h CHANGED
@@ -197,10 +197,21 @@ public:
197
197
 
198
198
  void ruby_mark_gc() const;
199
199
 
200
+ bool isGvlReleased() const noexcept { return gvl_released; }
201
+
202
+ void setGvlReleased(bool state) noexcept { gvl_released = state; }
203
+
204
+ void setInterrupted() {
205
+ rb_interrupted = true;
206
+ }
207
+
208
+ bool isInterrupted() const { return rb_interrupted; }
209
+
200
210
  virtual ~H8();
201
211
 
202
212
  private:
203
213
  friend VALUE h8::context_alloc(VALUE klass);
214
+ void invoke(v8::Handle<v8::Script> script, Local<Value>& result);
204
215
 
205
216
  Isolate *isolate;
206
217
  VALUE self;
@@ -208,6 +219,8 @@ private:
208
219
  Persistent<Context> persistent_context;
209
220
 
210
221
  bool is_error = false;
222
+ bool gvl_released = false;
223
+ bool rb_interrupted = false;
211
224
 
212
225
  chain resources;
213
226
  };
data/ext/h8/js_gate.cpp CHANGED
@@ -1,18 +1,45 @@
1
1
  #include "h8.h"
2
2
  #include "JsCatcher.h"
3
+ #include <ruby/thread.h>
3
4
 
4
5
  using namespace h8;
5
6
 
7
+ struct ApplyParams {
8
+ Local<Object> object;
9
+ Local<Value> &result;
10
+ Local<Value>& self;
11
+ Local<Value> *js_args;
12
+ int count;
13
+ H8* h8;
14
+ };
15
+
16
+ static inline void* call_without_gvl(void* param) {
17
+ ApplyParams *ap = (ApplyParams*)param;
18
+ ap->h8->setGvlReleased(true);
19
+ ap->result = ap->object->CallAsFunction(ap->self, ap->count, ap->js_args);
20
+ return NULL;
21
+ }
22
+
23
+ #define MAX_ARGS (128)
24
+
6
25
  VALUE JsGate::apply(Local<Value> self, VALUE args) const {
7
26
  H8::Scope scope(h8);
8
- long count = RARRAY_LEN(args);
9
- Local<Value> *js_args = new Local<Value> [count];
27
+ int count = RARRAY_LEN(args);
28
+ if( count >= MAX_ARGS )
29
+ throw JsError(h8, "Too many arguments for the callable");
30
+ // Local<Value> *js_args = new Local<Value> [count];
31
+ Local<Value> js_args[MAX_ARGS];
10
32
  for (int i = 0; i < count; i++) {
11
33
  js_args[i] = h8->to_js(rb_ary_entry(args, i));
12
34
  }
13
35
  h8::JsCatcher catcher(h8);
14
- Local<Value> result = object()->CallAsFunction(self, count, js_args);
36
+ Local<Value> result;
37
+
38
+ ApplyParams ap = { object(), result, self, js_args, count, h8 };
39
+ rb_thread_call_without_gvl(call_without_gvl, &ap, NULL, NULL );
40
+ h8->setGvlReleased(false);
41
+
15
42
  catcher.throwIfCaught();
16
- delete [] js_args;
43
+ // delete [] js_args;
17
44
  return h8->to_ruby(result);
18
45
  }
data/ext/h8/ruby_gate.cpp CHANGED
@@ -1,9 +1,31 @@
1
+ #include <functional>
1
2
  #include "h8.h"
2
3
  #include "ruby_gate.h"
3
4
  #include <ruby.h>
5
+ #include <ruby/thread.h>
4
6
 
5
7
  using namespace h8;
6
8
 
9
+ static void* unblock_caller(void *param) {
10
+ const std::function<void(void)>* pblock =
11
+ (const std::function<void(void)>*) param;
12
+ (*pblock)();
13
+ return NULL;
14
+ }
15
+
16
+ static void with_gvl(RubyGate *gate,
17
+ const std::function<void(void)> &block) {
18
+ // v8::Unlocker unlock(gate->isolate());
19
+ H8 *h8 = gate->getH8();
20
+ if (h8->isGvlReleased()) {
21
+ h8->setGvlReleased(false);
22
+ rb_thread_call_with_gvl(unblock_caller, (void*) &block);
23
+ h8->setGvlReleased(true);
24
+ }
25
+ else
26
+ block();
27
+ }
28
+
7
29
  h8::RubyGate::RubyGate(H8* _context, VALUE object) :
8
30
  context(_context), ruby_object(object), next(0), prev(0) {
9
31
  v8::HandleScope scope(context->getIsolate());
@@ -15,7 +37,7 @@ h8::RubyGate::RubyGate(H8* _context, VALUE object) :
15
37
  templ->SetCallAsFunctionHandler(&ObjectCallback);
16
38
 
17
39
  templ->SetNamedPropertyHandler(RubyGate::mapGet, RubyGate::mapSet);
18
- templ->SetIndexedPropertyHandler(RubyGate::indexGet,RubyGate::indexSet);
40
+ templ->SetIndexedPropertyHandler(RubyGate::indexGet, RubyGate::indexSet);
19
41
 
20
42
  v8::Handle<v8::Object> handle = templ->NewInstance();
21
43
  handle->SetAlignedPointerInInternalField(1, RUBYGATE_ID);
@@ -29,8 +51,7 @@ void h8::RubyGate::mapGet(Local<String> name,
29
51
  rg->getProperty(name, info);
30
52
  }
31
53
 
32
- void h8::RubyGate::mapSet(Local<String> name,
33
- Local<Value> value,
54
+ void h8::RubyGate::mapSet(Local<String> name, Local<Value> value,
34
55
  const PropertyCallbackInfo<Value> &info) {
35
56
  RubyGate *rg = RubyGate::unwrap(info.This());
36
57
  assert(rg != 0);
@@ -44,8 +65,7 @@ void h8::RubyGate::indexGet(uint32_t index,
44
65
  rg->getIndex(index, info);
45
66
  }
46
67
 
47
- void h8::RubyGate::indexSet(uint32_t index,
48
- Local<Value> value,
68
+ void h8::RubyGate::indexSet(uint32_t index, Local<Value> value,
49
69
  const PropertyCallbackInfo<Value> &info) {
50
70
  RubyGate *rg = RubyGate::unwrap(info.This());
51
71
  assert(rg != 0);
@@ -99,59 +119,66 @@ void h8::RubyGate::rescued_call(VALUE rb_args, VALUE (*call)(VALUE),
99
119
 
100
120
  void h8::RubyGate::doObjectCallback(
101
121
  const v8::FunctionCallbackInfo<v8::Value>& args) {
102
-
103
- VALUE rb_args = ruby_args(args, 1);
104
- rb_ary_push(rb_args, ruby_object);
105
- return rescued_call(rb_args, call, [&] (VALUE res) {
106
- args.GetReturnValue().Set(context->to_js(res));
122
+ with_gvl(this, [&] {
123
+ VALUE rb_args = ruby_args(args, 1);
124
+ rb_ary_push(rb_args, ruby_object);
125
+ rescued_call(rb_args, call, [&] (VALUE res) {
126
+ args.GetReturnValue().Set(context->to_js(res));
127
+ });
107
128
  });
108
129
  }
109
130
 
110
131
  void h8::RubyGate::getProperty(Local<String> name,
111
132
  const PropertyCallbackInfo<Value> &info) {
112
- VALUE rb_args = rb_ary_new2(2);
113
- rb_ary_push(rb_args, ruby_object);
114
- rb_ary_push(rb_args, context->to_ruby(name));
115
- return rescued_call(rb_args, secure_call, [&] (VALUE res) {
116
- info.GetReturnValue().Set(context->to_js(res));
117
- });
133
+ with_gvl(this, [&] {
134
+ VALUE rb_args = rb_ary_new2(2);
135
+ rb_ary_push(rb_args, ruby_object);
136
+ rb_ary_push(rb_args, context->to_ruby(name));
137
+ rescued_call(rb_args, secure_call, [&] (VALUE res) {
138
+ info.GetReturnValue().Set(context->to_js(res));
139
+ });
140
+ });
118
141
  }
119
142
 
120
- void h8::RubyGate::setProperty(Local<String> name,
121
- Local<Value> value,
143
+ void h8::RubyGate::setProperty(Local<String> name, Local<Value> value,
122
144
  const PropertyCallbackInfo<Value> &info) {
123
- VALUE rb_args = rb_ary_new2(3);
124
- rb_ary_push(rb_args, context->to_ruby(value));
125
- rb_ary_push(rb_args, ruby_object);
126
- VALUE method = context->to_ruby(name);
127
- method = rb_str_cat2(method, "=");
128
- rb_ary_push(rb_args, method);
129
- return rescued_call(rb_args, secure_call, [&] (VALUE res) {
130
- info.GetReturnValue().Set(context->to_js(res));
131
- });
145
+ with_gvl(this, [&] {
146
+ VALUE rb_args = rb_ary_new2(3);
147
+ rb_ary_push(rb_args, context->to_ruby(value));
148
+ rb_ary_push(rb_args, ruby_object);
149
+ VALUE method = context->to_ruby(name);
150
+ method = rb_str_cat2(method, "=");
151
+ rb_ary_push(rb_args, method);
152
+ rescued_call(rb_args, secure_call, [&] (VALUE res) {
153
+ info.GetReturnValue().Set(context->to_js(res));
154
+ });
155
+ });
132
156
  }
133
157
 
134
158
  void h8::RubyGate::getIndex(uint32_t index,
135
159
  const PropertyCallbackInfo<Value> &info) {
136
- VALUE rb_args = rb_ary_new2(3);
137
- rb_ary_push(rb_args, INT2FIX(index));
138
- rb_ary_push(rb_args, ruby_object);
139
- rb_ary_push(rb_args, rb_str_new2("[]"));
140
- return rescued_call(rb_args, secure_call, [&] (VALUE res) {
141
- info.GetReturnValue().Set(context->to_js(res));
142
- });
160
+ with_gvl(this, [&] {
161
+ VALUE rb_args = rb_ary_new2(3);
162
+ rb_ary_push(rb_args, INT2FIX(index));
163
+ rb_ary_push(rb_args, ruby_object);
164
+ rb_ary_push(rb_args, rb_str_new2("[]"));
165
+ rescued_call(rb_args, secure_call, [&] (VALUE res) {
166
+ info.GetReturnValue().Set(context->to_js(res));
167
+ });
168
+ });
143
169
  }
144
170
 
145
- void h8::RubyGate::setIndex(uint32_t index,
146
- Local<Value> value,
171
+ void h8::RubyGate::setIndex(uint32_t index, Local<Value> value,
147
172
  const PropertyCallbackInfo<Value> &info) {
148
- VALUE rb_args = rb_ary_new2(4);
149
- rb_ary_push(rb_args, INT2FIX(index));
150
- rb_ary_push(rb_args, context->to_ruby(value));
151
- rb_ary_push(rb_args, ruby_object);
152
- rb_ary_push(rb_args, rb_str_new2("[]="));
153
- return rescued_call(rb_args, secure_call, [&] (VALUE res) {
154
- info.GetReturnValue().Set(context->to_js(res));
155
- });
173
+ with_gvl(this, [&] {
174
+ VALUE rb_args = rb_ary_new2(4);
175
+ rb_ary_push(rb_args, INT2FIX(index));
176
+ rb_ary_push(rb_args, context->to_ruby(value));
177
+ rb_ary_push(rb_args, ruby_object);
178
+ rb_ary_push(rb_args, rb_str_new2("[]="));
179
+ rescued_call(rb_args, secure_call, [&] (VALUE res) {
180
+ info.GetReturnValue().Set(context->to_js(res));
181
+ });
182
+ });
156
183
  }
157
184
 
data/ext/h8/ruby_gate.h CHANGED
@@ -54,27 +54,35 @@ public:
54
54
  virtual ~RubyGate() {
55
55
  }
56
56
 
57
+ Isolate* isolate() const noexcept {
58
+ return context->getIsolate();
59
+ }
60
+
61
+ H8* getH8() {
62
+ return context;
63
+ }
64
+
57
65
  protected:
58
66
  /**
59
67
  * Perform rb_rescue call to 'call' callback, and invoke block with value returned by callback
60
68
  * unless a ruby exception is caught, in which a correct JsError is thrown.
61
69
  */
62
- void rescued_call(VALUE rb_args, VALUE (*call)(VALUE),const std::function<void(VALUE)> &block);
70
+ void rescued_call(VALUE rb_args, VALUE (*call)(VALUE),
71
+ const std::function<void(VALUE)> &block);
63
72
 
64
73
  /**
65
74
  * Convert some v8 parameters-like object to ruby arguments array, allocating
66
75
  * extra slots in array if need
67
76
  */
68
- template <class T>
77
+ template<class T>
69
78
  VALUE ruby_args(const T& args, unsigned extras = 0) {
70
79
  unsigned n = args.Length();
71
- VALUE rb_args = rb_ary_new2(n+extras);
80
+ VALUE rb_args = rb_ary_new2(n + extras);
72
81
  for (unsigned i = 0; i < n; i++)
73
82
  rb_ary_push(rb_args, context->to_ruby(args[i]));
74
83
  return rb_args;
75
84
  }
76
85
 
77
-
78
86
  /**
79
87
  * Ruby callable callback for rb_rescue and like. Args [0..-2] are call arguments,
80
88
  * last arg[-1] should be a callable to perform call with (for performance
@@ -90,28 +98,34 @@ protected:
90
98
  /**
91
99
  * callback for rb_rescue. Sets last_ruby_error.
92
100
  */
93
- static VALUE rescue_callback(VALUE me,VALUE exception_object);
101
+ static VALUE rescue_callback(VALUE me, VALUE exception_object);
94
102
 
95
- void getProperty(Local<String> name, const PropertyCallbackInfo<Value> &info);
96
- void setProperty(Local<String> name, Local<Value> value,const PropertyCallbackInfo<Value> &info);
103
+ void getProperty(Local<String> name,
104
+ const PropertyCallbackInfo<Value> &info);
105
+ void setProperty(Local<String> name, Local<Value> value,
106
+ const PropertyCallbackInfo<Value> &info);
97
107
 
98
108
  void getIndex(uint32_t index, const PropertyCallbackInfo<Value> &info);
99
- void setIndex(uint32_t index, Local<Value> value,const PropertyCallbackInfo<Value> &info);
109
+ void setIndex(uint32_t index, Local<Value> value,
110
+ const PropertyCallbackInfo<Value> &info);
100
111
  private:
101
112
 
102
113
  void doObjectCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
103
114
 
104
115
  static void ObjectCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
105
116
 
106
- static void mapGet(Local<String> name, const PropertyCallbackInfo<Value> &info);
107
- static void mapSet(Local<String> name, Local<Value> value,const PropertyCallbackInfo<Value> &info);
117
+ static void mapGet(Local<String> name,
118
+ const PropertyCallbackInfo<Value> &info);
119
+ static void mapSet(Local<String> name, Local<Value> value,
120
+ const PropertyCallbackInfo<Value> &info);
108
121
 
109
- static void indexGet(uint32_t index, const PropertyCallbackInfo<Value> &info);
110
- static void indexSet(uint32_t index, Local<Value> value, const PropertyCallbackInfo<Value> &info);
122
+ static void indexGet(uint32_t index,
123
+ const PropertyCallbackInfo<Value> &info);
124
+ static void indexSet(uint32_t index, Local<Value> value,
125
+ const PropertyCallbackInfo<Value> &info);
111
126
 
112
127
  void throw_js();
113
128
 
114
-
115
129
  friend class H8;
116
130
 
117
131
  H8 *context;
data/lib/h8/coffee.rb CHANGED
@@ -25,7 +25,7 @@ module H8
25
25
  # Compile coffeescript and return javascript. Keyword parameters are
26
26
  # passed to H8::Context#eval - like time limits and so on.
27
27
  #
28
- # This method IS THREAD SAFE, it shares single
28
+ # This method IS THREAD SAFE, though it shares single
29
29
  # compiler instance across all threads with a mutex.
30
30
  def self.compile src, ** kwargs
31
31
  @@mutex.synchronize {
data/lib/h8/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module H8
2
- VERSION = "0.1.4"
2
+ VERSION = "0.2.1"
3
3
  end
data/spec/context_spec.rb CHANGED
@@ -4,16 +4,16 @@ require 'h8'
4
4
  describe 'context' do
5
5
 
6
6
  it 'should create' do
7
- cxt = H8::Context.new
7
+ cxt = H8::Context.new
8
8
  cxt[:one] = 1
9
9
  cxt.eval("");
10
10
  # cxt.eval("'Res: ' + (2+5);")
11
11
  end
12
12
 
13
13
  it 'should gate simple values to JS context' do
14
- cxt = H8::Context.new foo: 'hello', bar: 'world'
14
+ cxt = H8::Context.new foo: 'hello', bar: 'world'
15
15
  cxt[:sign] = '!'
16
- res = cxt.eval "foo+' '+bar+sign;"
16
+ res = cxt.eval "foo+' '+bar+sign;"
17
17
  res.should == 'hello world!'
18
18
  cxt.set_all one: 101, real: 1.21
19
19
  cxt.eval("one + one;").should == 202
@@ -21,25 +21,25 @@ describe 'context' do
21
21
  end
22
22
 
23
23
  it 'should gate H8::Values back to JS context' do
24
- cxt = H8::Context.new
25
- obj = cxt.eval "('che bel');"
24
+ cxt = H8::Context.new
25
+ obj = cxt.eval "('che bel');"
26
26
  cxt[:first] = obj
27
- res = cxt.eval "first + ' giorno';"
27
+ res = cxt.eval "first + ' giorno';"
28
28
  res.should == 'che bel giorno'
29
29
  end
30
30
 
31
31
  it 'should not gate H8::Values between contexts' do
32
- cxt = H8::Context.new
33
- obj = cxt.eval "({res: 'che bel'});"
32
+ cxt = H8::Context.new
33
+ obj = cxt.eval "({res: 'che bel'});"
34
34
  # This should be ok
35
35
  cxt[:first] = obj
36
- res = cxt.eval "first.res + ' giorno';"
36
+ res = cxt.eval "first.res + ' giorno';"
37
37
  res.should == 'che bel giorno'
38
38
  # And that should fail
39
39
  cxt1 = H8::Context.new
40
- expect( -> {
40
+ expect(-> {
41
41
  cxt1[:first] = obj
42
- res = cxt1.eval "first.res + ' giorno';"
42
+ res = cxt1.eval "first.res + ' giorno';"
43
43
  }).to raise_error(H8::Error)
44
44
  end
45
45
 
@@ -54,7 +54,7 @@ describe 'context' do
54
54
  end
55
55
 
56
56
  it 'should limit script execution time' do
57
- cxt = H8::Context.new
57
+ cxt = H8::Context.new
58
58
  # cxt[:print] = -> (*args) { puts args.join(' ')}
59
59
  script = <<-End
60
60
  var start = new Date();
@@ -67,34 +67,61 @@ describe 'context' do
67
67
  End
68
68
  # end
69
69
  t = Time.now
70
- expect( -> {
70
+ expect(-> {
71
71
  c2 = cxt.eval script, max_time: 0.2
72
72
  }).to raise_error(H8::TimeoutError)
73
73
  (Time.now - t).should < 0.25
74
74
  cxt.eval('(last-start)/1000').should < 250
75
75
  end
76
76
 
77
+ it 'should have generators' do
78
+ script = <<-End
79
+ function* f(base) {
80
+ var count = base;
81
+ var end = base +3;
82
+ print("F is calleed", count);
83
+ while(count < end) {
84
+ var r = yield count++;
85
+ print("yield returned", r);
86
+ }
87
+ }
88
+ var g = f(100);
89
+ print(g.next().value);
90
+ print(g.next(1).value);
91
+ End
92
+ c = H8::Context.new
93
+ c[:print] = -> (*args) { args.join(' ') }
94
+ c.eval script
95
+ end
96
+
77
97
  it 'should work in many threads' do
78
- sum = 0
79
- valid = 0
80
- n = 10
81
- contexts = []
82
- tt = n.times.map { |n|
83
- valid += (n+1)*100 + 10
84
- Thread.start {
85
- cxt = H8::Context.new
86
- contexts << cxt
98
+ # pending
99
+ sum = 0
100
+ mutex = Mutex.new
101
+ valid = 0
102
+ n = 10
103
+ tt = []
104
+ n.times { |n|
105
+ cxt = nil
106
+ t = Thread.start {
107
+ cxt = H8::Context.new
87
108
  cxt[:array] = 100024.times.map { |x| x*(n+1) }
88
- cxt[:n] = n+1
89
- sum += cxt.eval('result = array[100] + 10')
109
+ cxt[:n] = n+1
110
+ mutex.synchronize {
111
+ valid += (n+1)*100 + 10
112
+ sum += cxt.eval('result = array[100] + 10')
113
+ }
114
+ tt << OpenStruct.new({ thread: t, number: n, context: cxt })
90
115
  }
91
116
  }
92
- tt.each &:join
93
- sum.should == valid
117
+ tt.each{ |x| x.thread.join }
118
+ mutex.synchronize {
119
+ sum.should == valid
120
+ }
94
121
  GC.start
95
122
  # Cross-thread access
96
- contexts.each { |cxt|
97
- s, n = cxt.eval('data = [result,n]')
123
+ tt.each { |x|
124
+ s, n = x.context.eval('data = [result,n]')
98
125
  s.should == 100*n + 10
99
126
  }
100
127
  end
@@ -5,7 +5,9 @@ describe 'ruby gate' do
5
5
 
6
6
  it 'should gate callables' do
7
7
  cxt = H8::Context.new
8
+ count = 0
8
9
  cxt[:fn] = -> (a, b) {
10
+ count += 1
9
11
  a + b
10
12
  }
11
13
 
@@ -14,6 +16,7 @@ describe 'ruby gate' do
14
16
  cxt = nil
15
17
  res = nil
16
18
  GC.start
19
+ count.should == 1
17
20
  end
18
21
 
19
22
  # it 'should gate callables in therubyrace mode' do
@@ -47,7 +50,7 @@ describe 'ruby gate' do
47
50
  res.to_i.should == -11
48
51
  end
49
52
 
50
- it 'should gate callables with varargs' do
53
+ it 'va: should gate callables with varargs' do
51
54
  cxt = H8::Context.new
52
55
  cxt[:fn] = -> (*args) {
53
56
  args.reduce(0) { |all, x| all+x }
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require 'h8'
3
+
4
+ describe 'threading' do
5
+ before do
6
+ @counter_script = <<-End
7
+ var count = 0;
8
+ var endTime = end*1000 + 150;
9
+ while( new Date().getTime() < endTime ) count++;
10
+ (count);
11
+ End
12
+ @context = H8::Context.new
13
+ @context[:print] = -> (*args) {
14
+ puts "D: #{args.join(',')}"
15
+ }
16
+ Thread.pass
17
+ @end_time = Time.now.to_i + 1
18
+ @context[:end] = @end_time
19
+ end
20
+
21
+ it 'without tout: should run JS/ruby threads in parallel' do
22
+ cnt2 = 0
23
+ Thread.start {
24
+ cnt2 += 1 while Time.now.to_i < @end_time
25
+ }
26
+ res = @context.eval @counter_script, timeout: 5000
27
+ cnt = cnt2
28
+ fail "JS thread does not run in parallel" if res < 1
29
+ fail "JS thread does not run in parallel" if cnt < 1
30
+ (res / cnt).should < 16
31
+ end
32
+
33
+
34
+ it 'should run JS callables in threads in parallel' do
35
+ fn = @context.eval "res = function (end) { #{@counter_script} return count; }"
36
+ cnt2 = 0
37
+ Thread.start {
38
+ cnt2 += 1 while Time.now.to_i < @end_time
39
+ }
40
+ res = fn.call(@end_time)
41
+ cnt = cnt2
42
+ fail "JS thread does not run in parallel" if res < 1
43
+ fail "JS thread does not run in parallel" if cnt < 1
44
+ (res / cnt).should < 16
45
+ end
46
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: h8
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - sergeych
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-30 00:00:00.000000000 Z
11
+ date: 2015-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -113,6 +113,7 @@ files:
113
113
  - spec/js_gate_spec.rb
114
114
  - spec/ruby_gate_spec.rb
115
115
  - spec/spec_helper.rb
116
+ - spec/threading_spec.rb
116
117
  homepage: https://github.com/sergeych/hybrid8
117
118
  licenses:
118
119
  - MIT
@@ -134,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
135
  version: '0'
135
136
  requirements: []
136
137
  rubyforge_project:
137
- rubygems_version: 2.4.5
138
+ rubygems_version: 2.2.2
138
139
  signing_key:
139
140
  specification_version: 4
140
141
  summary: Minimalistic and fast Ruby <--> V8 integration
@@ -144,3 +145,4 @@ test_files:
144
145
  - spec/js_gate_spec.rb
145
146
  - spec/ruby_gate_spec.rb
146
147
  - spec/spec_helper.rb
148
+ - spec/threading_spec.rb