mini_racer 0.2.7 → 0.2.12

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
  SHA256:
3
- metadata.gz: d194b6215e99a045c3f3464042f03257c79317a28b093be76ef10b98a7ce84b5
4
- data.tar.gz: 754e28ec33401634506fb3edccd8459ff84c0eb9b86c93ac967a8dca6e9f1fb0
3
+ metadata.gz: 35ced47bf0cd66e605b6bd14005c23381b9fa42e438b0ab45a3c99414d107634
4
+ data.tar.gz: '02968be5850b866d1c399995cb6d942dc826730447524df008f604035449050e'
5
5
  SHA512:
6
- metadata.gz: 0fe549674ed12bc928a7bd259a7ba4cd279f039dd8ea25941974f3b9748ec89ce9140f7281ff333ed21aa442b204a7ddf8b719f71f5b038a53c10792d986676b
7
- data.tar.gz: 3b4a9c5b0fd80926a7c8dea7cec4b6a6a857d80c91e55f6fcc621fc01eda4335af46d10006e6db8bdda9c509f4900205178b8745cda4bcbd3c1333606188ec35
6
+ metadata.gz: 3c4fc5f71bcfddd5d58f5b0ae0f68e4b59067005362afbf29f096752f63fab9ea0d0526f2ba02d5a85f89976e607eae395c3714b6186282a6bce19217ae86ec0
7
+ data.tar.gz: 3d6268a03f472c01fa68278e4349c18b270765a8e831b020286a929b0fd0f2a1422b2c0c11432a33a348ec28044a38acdcd55d4cff93b436acc2fbed07ddfdf8
@@ -1,9 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.3
4
3
  - 2.4
5
4
  - 2.5
6
5
  - 2.6
6
+ - 2.7
7
7
  - ruby-head
8
8
  matrix:
9
9
  include:
data/CHANGELOG CHANGED
@@ -1,3 +1,34 @@
1
+ - 15-05-2020
2
+
3
+ - 0.2.12
4
+
5
+ - FEATURE: isolate.low_memory_notification which can force a full GC
6
+ - FEATURE: MiniRacer::Context.new(ensure_gc_after_idle: 2) - to force full GC 2 seconds after context is idle, this allows you to conserve memory on isolates
7
+
8
+ - 14-05-2020
9
+
10
+ - 0.2.11
11
+
12
+ - FIX: dumping heap snapshots was not flushing the file leading to corrupt snapshots
13
+ - FIX: a use-after-free shutdown crash
14
+
15
+ - 0.2.10
16
+
17
+ - 22-04-2020
18
+
19
+ - FEATURE: memory softlimit support for nogvl_context_call
20
+
21
+ - 0.2.9
22
+
23
+ - 09-01-2020
24
+
25
+ - FIX: correct segfault when JS returns a Symbol and properly cast to ruby symbol
26
+
27
+ - 0.2.8
28
+
29
+ - 11-11-2019
30
+
31
+ - FIX: ensure thread live cycle is properly accounter for following file descriptor fix
1
32
 
2
33
  - 0.2.7
3
34
 
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MiniRacer
2
2
 
3
- [![Build Status](https://travis-ci.org/discourse/mini_racer.svg?branch=master)](https://travis-ci.org/discourse/mini_racer)
3
+ [![Build Status](https://travis-ci.org/rubyjs/mini_racer.svg?branch=master)](https://travis-ci.org/rubyjs/mini_racer)
4
4
 
5
5
  Minimal, modern embedded V8 for Ruby.
6
6
 
@@ -230,12 +230,17 @@ context = MiniRacer::Context.new(isolate: isolate)
230
230
  # give up to 100ms for V8 garbage collection
231
231
  isolate.idle_notification(100)
232
232
 
233
+ # force V8 to perform a full GC
234
+ isolate.low_memory_notification
235
+
233
236
  ```
234
237
 
235
238
  This can come in handy to force V8 GC runs for example in between requests if you use MiniRacer on a web application.
236
239
 
237
240
  Note that this method maps directly to [`v8::Isolate::IdleNotification`](http://bespin.cz/~ondras/html/classv8_1_1Isolate.html#aea16cbb2e351de9a3ae7be2b7cb48297), and that in particular its return value is the same (true if there is no further garbage to collect, false otherwise) and the same caveats apply, in particular that `there is no guarantee that the [call will return] within the time limit.`
238
241
 
242
+ Additionally you may automate this process on a context by defining it with `MiniRacer::Content.new(ensure_gc_after_idle: 1)`. Using this will ensure V8 will run a full GC using `context.isolate.low_memory_notification` 1 second after the last eval on the context. Low memory notification is both slower and more aggressive than an idle_notification and will ensure long living isolates use minimal amounts of memory.
243
+
239
244
  ### V8 Runtime flags
240
245
 
241
246
  It is possible to set V8 Runtime flags:
@@ -273,7 +278,7 @@ context.eval js
273
278
  The same code without the harmony runtime flag results in a `MiniRacer::RuntimeError: RangeError: Maximum call stack size exceeded` exception.
274
279
  Please refer to http://node.green/ as a reference on other harmony features.
275
280
 
276
- A list of all V8 runtime flags can be found using `node --v8-options`, or else by perusing [the V8 source code for flags (make sure to use the right version of V8)](https://github.com/v8/v8/blob/master/src/flag-definitions.h).
281
+ A list of all V8 runtime flags can be found using `node --v8-options`, or else by perusing [the V8 source code for flags (make sure to use the right version of V8)](https://github.com/v8/v8/blob/master/src/flags/flag-definitions.h).
277
282
 
278
283
  Note that runtime flags must be set before any other operation (e.g. creating a context, a snapshot or an isolate), otherwise an exception will be thrown.
279
284
 
@@ -447,7 +452,7 @@ Add this to your .travis.yml file:
447
452
 
448
453
  ## Contributing
449
454
 
450
- Bug reports and pull requests are welcome on GitHub at https://github.com/discourse/mini_racer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
455
+ Bug reports and pull requests are welcome on GitHub at https://github.com/rubyjs/mini_racer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
451
456
 
452
457
 
453
458
  ## License
@@ -127,6 +127,7 @@ typedef struct {
127
127
  Local<Function> fun;
128
128
  Local<Value> *argv;
129
129
  EvalResult result;
130
+ size_t max_memory;
130
131
  } FunctionCall;
131
132
 
132
133
  enum IsolateFlags {
@@ -155,6 +156,10 @@ static VALUE rb_cDateTime = Qnil;
155
156
  static std::unique_ptr<Platform> current_platform = NULL;
156
157
  static std::mutex platform_lock;
157
158
 
159
+ static pthread_attr_t *thread_attr_p;
160
+ static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
161
+ static bool ruby_exiting; // guarded by exit_lock
162
+
158
163
  static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
159
164
  bool platform_already_initialized = false;
160
165
 
@@ -444,8 +449,27 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local context,
444
449
  return rb_hash;
445
450
  }
446
451
 
447
- Local<String> rstr = value->ToString(context).ToLocalChecked();
448
- return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
452
+ if (value->IsSymbol()) {
453
+ v8::String::Utf8Value symbol_name(isolate,
454
+ Local<Symbol>::Cast(value)->Name());
455
+
456
+ VALUE str_symbol = rb_enc_str_new(
457
+ *symbol_name,
458
+ symbol_name.length(),
459
+ rb_enc_find("utf-8")
460
+ );
461
+
462
+ return ID2SYM(rb_intern_str(str_symbol));
463
+ }
464
+
465
+ MaybeLocal<String> rstr_maybe = value->ToString(context);
466
+
467
+ if (rstr_maybe.IsEmpty()) {
468
+ return Qnil;
469
+ } else {
470
+ Local<String> rstr = rstr_maybe.ToLocalChecked();
471
+ return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
472
+ }
449
473
  }
450
474
 
451
475
  static VALUE convert_v8_to_ruby(Isolate* isolate,
@@ -745,6 +769,16 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
745
769
  return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
746
770
  }
747
771
 
772
+ static VALUE rb_isolate_low_memory_notification(VALUE self) {
773
+ IsolateInfo* isolate_info;
774
+ Data_Get_Struct(self, IsolateInfo, isolate_info);
775
+
776
+ if (current_platform == NULL) return Qfalse;
777
+
778
+ isolate_info->isolate->LowMemoryNotification();
779
+ return Qnil;
780
+ }
781
+
748
782
  static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
749
783
  ContextInfo* context_info;
750
784
  Data_Get_Struct(self, ContextInfo, context_info);
@@ -1185,7 +1219,7 @@ void free_isolate(IsolateInfo* isolate_info) {
1185
1219
  delete isolate_info->allocator;
1186
1220
  }
1187
1221
 
1188
- static void *free_context_raw(void* arg) {
1222
+ static void free_context_raw(void *arg) {
1189
1223
  ContextInfo* context_info = (ContextInfo*)arg;
1190
1224
  IsolateInfo* isolate_info = context_info->isolate_info;
1191
1225
  Persistent<Context>* context = context_info->context;
@@ -1202,6 +1236,20 @@ static void *free_context_raw(void* arg) {
1202
1236
  }
1203
1237
 
1204
1238
  xfree(context_info);
1239
+ }
1240
+
1241
+ static void *free_context_thr(void* arg) {
1242
+ if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
1243
+ return NULL;
1244
+ }
1245
+ if (ruby_exiting) {
1246
+ return NULL;
1247
+ }
1248
+
1249
+ free_context_raw(arg);
1250
+
1251
+ pthread_rwlock_unlock(&exit_lock);
1252
+
1205
1253
  return NULL;
1206
1254
  }
1207
1255
 
@@ -1215,22 +1263,17 @@ static void free_context(ContextInfo* context_info) {
1215
1263
  context_info_copy->context = context_info->context;
1216
1264
 
1217
1265
  if (isolate_info && isolate_info->refs() > 1) {
1218
- pthread_t free_context_thread;
1219
- if (pthread_create(&free_context_thread, NULL, free_context_raw, (void*)context_info_copy)) {
1220
- fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
1221
- }
1222
-
1266
+ pthread_t free_context_thread;
1267
+ if (pthread_create(&free_context_thread, thread_attr_p,
1268
+ free_context_thr, (void*)context_info_copy)) {
1269
+ fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
1270
+ }
1223
1271
  } else {
1224
1272
  free_context_raw(context_info_copy);
1225
1273
  }
1226
1274
 
1227
- if (context_info->context && isolate_info && isolate_info->isolate) {
1228
- context_info->context = NULL;
1229
- }
1230
-
1231
- if (isolate_info) {
1232
- context_info->isolate_info = NULL;
1233
- }
1275
+ context_info->context = NULL;
1276
+ context_info->isolate_info = NULL;
1234
1277
  }
1235
1278
 
1236
1279
  static void deallocate_isolate(void* data) {
@@ -1388,6 +1431,8 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1388
1431
  FileOutputStream stream(fp);
1389
1432
  snap->Serialize(&stream, HeapSnapshot::kJSON);
1390
1433
 
1434
+ fflush(fp);
1435
+
1391
1436
  const_cast<HeapSnapshot*>(snap)->Delete();
1392
1437
 
1393
1438
  return Qtrue;
@@ -1435,6 +1480,12 @@ nogvl_context_call(void *args) {
1435
1480
  // terminate ASAP
1436
1481
  isolate->SetData(DO_TERMINATE, (void*)false);
1437
1482
 
1483
+ if (call->max_memory > 0) {
1484
+ isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
1485
+ isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
1486
+ isolate->AddGCEpilogueCallback(gc_callback);
1487
+ }
1488
+
1438
1489
  Isolate::Scope isolate_scope(isolate);
1439
1490
  EscapableHandleScope handle_scope(isolate);
1440
1491
  TryCatch trycatch(isolate);
@@ -1492,6 +1543,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1492
1543
  call_argv = argv + 1;
1493
1544
  }
1494
1545
 
1546
+ call.max_memory = 0;
1547
+ VALUE mem_softlimit = rb_iv_get(self, "@max_memory");
1548
+ if (mem_softlimit != Qnil) {
1549
+ unsigned long sl_int = NUM2ULONG(mem_softlimit);
1550
+ call.max_memory = (size_t)sl_int;
1551
+ }
1552
+
1495
1553
  bool missingFunction = false;
1496
1554
  {
1497
1555
  Locker lock(isolate);
@@ -1549,6 +1607,16 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
1549
1607
  return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
1550
1608
  }
1551
1609
 
1610
+ static void set_ruby_exiting(VALUE value) {
1611
+ (void)value;
1612
+
1613
+ int res = pthread_rwlock_wrlock(&exit_lock);
1614
+ ruby_exiting = true;
1615
+ if (res == 0) {
1616
+ pthread_rwlock_unlock(&exit_lock);
1617
+ }
1618
+ }
1619
+
1552
1620
  extern "C" {
1553
1621
 
1554
1622
  void Init_mini_racer_extension ( void )
@@ -1599,8 +1667,19 @@ extern "C" {
1599
1667
  rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
1600
1668
 
1601
1669
  rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
1670
+ rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
1671
+
1602
1672
  rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
1603
1673
 
1604
1674
  rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
1675
+
1676
+ rb_set_end_proc(set_ruby_exiting, Qnil);
1677
+
1678
+ static pthread_attr_t attr;
1679
+ if (pthread_attr_init(&attr) == 0) {
1680
+ if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0) {
1681
+ thread_attr_p = &attr;
1682
+ }
1683
+ }
1605
1684
  }
1606
1685
  }
@@ -145,6 +145,15 @@ module MiniRacer
145
145
  end
146
146
  # false signals it should be fetched if requested
147
147
  @isolate = options[:isolate] || false
148
+
149
+ @ensure_gc_after_idle = options[:ensure_gc_after_idle]
150
+
151
+ if @ensure_gc_after_idle
152
+ @last_eval = nil
153
+ @ensure_gc_thread = nil
154
+ @ensure_gc_mutex = Mutex.new
155
+ end
156
+
148
157
  @disposed = false
149
158
 
150
159
  @callback_mutex = Mutex.new
@@ -203,6 +212,7 @@ module MiniRacer
203
212
  end
204
213
  ensure
205
214
  @eval_thread = nil
215
+ ensure_gc_thread if @ensure_gc_after_idle
206
216
  end
207
217
 
208
218
  def call(function_name, *arguments)
@@ -216,15 +226,17 @@ module MiniRacer
216
226
  end
217
227
  ensure
218
228
  @eval_thread = nil
229
+ ensure_gc_thread if @ensure_gc_after_idle
219
230
  end
220
231
 
221
232
  def dispose
222
233
  return if @disposed
223
234
  isolate_mutex.synchronize do
235
+ return if @disposed
224
236
  dispose_unsafe
237
+ @disposed = true
238
+ @isolate = nil # allow it to be garbage collected, if set
225
239
  end
226
- @disposed = true
227
- @isolate = nil # allow it to be garbage collected, if set
228
240
  end
229
241
 
230
242
 
@@ -273,6 +285,36 @@ module MiniRacer
273
285
 
274
286
  private
275
287
 
288
+ def ensure_gc_thread
289
+ @last_eval = Process.clock_gettime(Process::CLOCK_MONOTONIC)
290
+ @ensure_gc_mutex.synchronize do
291
+ @ensure_gc_thread = nil if !@ensure_gc_thread&.alive?
292
+ @ensure_gc_thread ||= Thread.new do
293
+ done = false
294
+ while !done
295
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
296
+
297
+ if @disposed
298
+ @ensure_gc_thread = nil
299
+ break
300
+ end
301
+
302
+ if @ensure_gc_after_idle < now - @last_eval
303
+ @ensure_gc_mutex.synchronize do
304
+ isolate_mutex.synchronize do
305
+ # extra 50ms to make sure that we really have enough time
306
+ isolate.low_memory_notification if !@disposed
307
+ @ensure_gc_thread = nil
308
+ done = true
309
+ end
310
+ end
311
+ end
312
+ sleep @ensure_gc_after_idle if !done
313
+ end
314
+ end
315
+ end
316
+ end
317
+
276
318
  def stop_attached
277
319
  @callback_mutex.synchronize{
278
320
  if @callback_running
@@ -313,9 +355,15 @@ module MiniRacer
313
355
 
314
356
  # ensure we do not leak a thread in state
315
357
  t.join
358
+ t = nil
316
359
 
317
360
  rval
318
361
  ensure
362
+ # exceptions need to be handled
363
+ if t && wp
364
+ wp.write("done")
365
+ t.join
366
+ end
319
367
  wp.close if wp
320
368
  rp.close if rp
321
369
  end
@@ -1,3 +1,3 @@
1
1
  module MiniRacer
2
- VERSION = "0.2.7"
2
+ VERSION = "0.2.12"
3
3
  end
@@ -14,18 +14,24 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = "https://github.com/discourse/mini_racer"
15
15
  spec.license = "MIT"
16
16
 
17
+ spec.metadata = {
18
+ "bug_tracker_uri" => "https://github.com/discourse/mini_racer/issues",
19
+ "changelog_uri" => "https://github.com/discourse/mini_racer/blob/v#{spec.version}/CHANGELOG",
20
+ "documentation_uri" => "https://www.rubydoc.info/gems/mini_racer/#{spec.version}",
21
+ "source_code_uri" => "https://github.com/discourse/mini_racer/tree/v#{spec.version}",
22
+ }
17
23
 
18
24
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(benchmark|test|spec|features|examples)/}) }
19
25
  spec.bindir = "exe"
20
26
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
27
  spec.require_paths = ["lib"]
22
28
 
23
- spec.add_development_dependency "bundler", "~> 1.12"
24
- spec.add_development_dependency "rake", "~> 10.0"
29
+ spec.add_development_dependency "bundler"
30
+ spec.add_development_dependency "rake", ">= 12.3.3"
25
31
  spec.add_development_dependency "minitest", "~> 5.0"
26
32
  spec.add_development_dependency "rake-compiler"
27
33
 
28
- spec.add_dependency 'libv8', '>= 6.9.411'
34
+ spec.add_dependency 'libv8', '> 7.3'
29
35
  spec.require_paths = ["lib", "ext"]
30
36
 
31
37
  spec.extensions = ["ext/mini_racer_extension/extconf.rb"]
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_racer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.2.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-11 00:00:00.000000000 Z
11
+ date: 2020-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.12'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.12'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: 12.3.3
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: 12.3.3
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -70,16 +70,16 @@ dependencies:
70
70
  name: libv8
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - ">"
74
74
  - !ruby/object:Gem::Version
75
- version: 6.9.411
75
+ version: '7.3'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - ">"
81
81
  - !ruby/object:Gem::Version
82
- version: 6.9.411
82
+ version: '7.3'
83
83
  description: Minimal embedded v8 engine for Ruby
84
84
  email:
85
85
  - sam.saffron@gmail.com
@@ -106,7 +106,11 @@ files:
106
106
  homepage: https://github.com/discourse/mini_racer
107
107
  licenses:
108
108
  - MIT
109
- metadata: {}
109
+ metadata:
110
+ bug_tracker_uri: https://github.com/discourse/mini_racer/issues
111
+ changelog_uri: https://github.com/discourse/mini_racer/blob/v0.2.12/CHANGELOG
112
+ documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.2.12
113
+ source_code_uri: https://github.com/discourse/mini_racer/tree/v0.2.12
110
114
  post_install_message:
111
115
  rdoc_options: []
112
116
  require_paths: