mini_racer 0.2.7 → 0.2.12

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
  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: