h8 0.1.4 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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