mini_racer 0.2.8 → 0.4.0

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: 8f6c48eedae322fc66e7dc1f816d63d8d093a18a02167a16cada1cd04a34e821
4
- data.tar.gz: 0b4de89162986b4d729abff697c04de4f54216bb1a94c5d7737b7cf011fca85e
3
+ metadata.gz: e73ac3193df3f33b51463ba1125ac4eee312caecd47833c545e098ba91d11ece
4
+ data.tar.gz: d6d754829b36953d239ba85e84d2fc88c0af3ccbadd3f8d2e4cd9759bd24fab2
5
5
  SHA512:
6
- metadata.gz: ce21ef957f04319ed5109e6f5ddbc2c17be7f657d9beed5941950ccf57d36054341c5f6685c790ab217d8445e1af1758e7d16c1745d47955042453c6294784c3
7
- data.tar.gz: 4e58fdfde4f8f78ade9c470510296cb8dcd30e3c02a8827f4c4ba68ee49e41dee66de5419938c52c98d9df41d1dd02433b5b0d5389f0d0664da100f5087e0db1
6
+ metadata.gz: 52a0799d84be10bb03774789b3d12d4f0fe2d4159d310b9c6a1ae3dcf878a99cd6b7c786fae813f29754a894ecf5fcc6b7e07834c0aba994a16da5c54d75595b
7
+ data.tar.gz: bc478bdb582d7b786d28754200063d7ef185396e0da23840f006679bc1af6707f54c7a719918360afa9b24c2b4ab083ca081e2b2d6853095ecd5949a9482ecbd
data/.dockerignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ lib/mini_racer_extension.so
11
+ lib/mini_racer_loader.so
12
+ *.bundle
@@ -0,0 +1,80 @@
1
+ name: Test
2
+ on:
3
+ - push
4
+
5
+ jobs:
6
+ test-darwin:
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ os:
11
+ - '10.15'
12
+ - '11.0'
13
+ platform:
14
+ - x86_64
15
+ # arm64
16
+ name: Test (darwin)
17
+ runs-on: macos-${{ matrix.os }}
18
+ steps:
19
+ - name: Checkout
20
+ uses: actions/checkout@v2
21
+ - name: Bundle
22
+ run: bundle install
23
+ - name: Compile
24
+ run: bundle exec rake compile
25
+ - name: Test
26
+ run: bundle exec rake test
27
+ test-linux:
28
+ strategy:
29
+ fail-fast: false
30
+ matrix:
31
+ ruby:
32
+ - '2.4'
33
+ - '2.5'
34
+ - '2.6'
35
+ - '2.7'
36
+ - '3.0'
37
+ platform:
38
+ - amd64
39
+ - arm64
40
+ # arm
41
+ # ppc64le
42
+ # s390x
43
+ libc:
44
+ - gnu
45
+ - musl
46
+ name: Test (linux)
47
+ runs-on: ubuntu-20.04
48
+ steps:
49
+ - name: Enable ${{ matrix.platform }} platform
50
+ id: qemu
51
+ if: ${{ matrix.platform != 'amd64' }}
52
+ run: |
53
+ docker run --privileged --rm tonistiigi/binfmt:latest --install ${{ matrix.platform }} | tee platforms.json
54
+ echo "::set-output name=platforms::$(cat platforms.json)"
55
+ - name: Start container
56
+ id: container
57
+ run: |
58
+ case ${{ matrix.libc }} in
59
+ gnu)
60
+ echo 'ruby:${{ matrix.ruby }}'
61
+ ;;
62
+ musl)
63
+ echo 'ruby:${{ matrix.ruby }}-alpine'
64
+ ;;
65
+ esac > container_image
66
+ echo "::set-output name=image::$(cat container_image)"
67
+ docker run --rm -d -v "${PWD}":"${PWD}" -w "${PWD}" --platform linux/${{ matrix.platform }} $(cat container_image) /bin/sleep 64d | tee container_id
68
+ docker exec -w "${PWD}" $(cat container_id) uname -a
69
+ echo "::set-output name=id::$(cat container_id)"
70
+ - name: Install Alpine system dependencies
71
+ if: ${{ matrix.libc == 'musl' }}
72
+ run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} apk add --no-cache build-base linux-headers bash python2 python3 git curl tar clang binutils-gold
73
+ - name: Checkout
74
+ uses: actions/checkout@v2
75
+ - name: Bundle
76
+ run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle install
77
+ - name: Compile
78
+ run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle exec rake compile
79
+ - name: Test
80
+ run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle exec rake test
data/.gitignore CHANGED
@@ -8,4 +8,5 @@ Gemfile.lock
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  lib/mini_racer_extension.so
11
+ lib/mini_racer_loader.so
11
12
  *.bundle
data/.travis.yml CHANGED
@@ -1,23 +1,30 @@
1
1
  language: ruby
2
+ os: linux
2
3
  rvm:
3
- - 2.3
4
4
  - 2.4
5
5
  - 2.5
6
6
  - 2.6
7
+ - 2.7
8
+ - 3.0
7
9
  - ruby-head
8
- matrix:
10
+ arch:
11
+ - amd64
12
+ - arm64
13
+ jobs:
9
14
  include:
10
- - rvm: 2.5.1
15
+ - rvm: 2.5
11
16
  os: osx
12
17
  osx_image: xcode9.4
13
- - rvm: 2.4.0
18
+ - rvm: 2.6
14
19
  os: osx
15
- osx_image: xcode8.3
16
- - rvm: 2.4.0
20
+ osx_image: xcode11.3
21
+ - rvm: 2.6
17
22
  os: osx
18
- osx_image: xcode7.3
19
- dist: trusty
20
- sudo: true
23
+ osx_image: xcode12.2
24
+ - rvm: 2.7
25
+ os: osx
26
+ osx_image: xcode12.2
27
+ dist: xenial
21
28
  before_install:
22
29
  - gem update --system
23
30
  - gem install bundler -v 1.16.2
data/CHANGELOG CHANGED
@@ -1,3 +1,74 @@
1
+ - 11-04-2021
2
+
3
+ - 0.4.0
4
+
5
+ - FEATURE: upgrade to libv8 node 15.14.0 (v8 8.6.395.17)
6
+ - Promote 0.4.0.beta1 to release, using libv8-node release path
7
+
8
+ - 08-04-2021
9
+
10
+ - 0.4.0.beta1
11
+
12
+ - FIX: on downgrade mkmf was picking the wrong version of libv8, this fix will correct future issues
13
+ - FEATURE: upgraded libv8 to use node libv8 build which supports M1 and various ARM builds v8 moved to (8.6.395.17)
14
+
15
+ - 23-07-2020
16
+
17
+ - 0.3.1
18
+
19
+ - FIX: specify that libv8 must be larger than 8.4.255 but smaller than 8.5, this avoids issues going forward
20
+
21
+ - 22-07-2020
22
+
23
+ - 0.3.0
24
+
25
+ - FEATURE: upgraded to libv8 version 8.4.255.0
26
+
27
+ - 29-06-2020
28
+
29
+ - 0.2.15
30
+
31
+ - FEATURE: basic wasm support via pump_message_loop
32
+
33
+ - 15-05-2020
34
+
35
+ - 0.2.14
36
+
37
+ - FIX: ensure_gc_after_idle should take in milliseconds like the rest of the APIs not seconds
38
+ - FEATURE: strict params on MiniRacer::Context.new
39
+
40
+ - 15-05-2020
41
+
42
+ - 0.2.13
43
+
44
+ - FIX: edge case around ensure_gc_after_idle possibly firing when context is not idle
45
+
46
+ - 15-05-2020
47
+
48
+ - 0.2.12
49
+
50
+ - FEATURE: isolate.low_memory_notification which can force a full GC
51
+ - 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
52
+
53
+ - 14-05-2020
54
+
55
+ - 0.2.11
56
+
57
+ - FIX: dumping heap snapshots was not flushing the file leading to corrupt snapshots
58
+ - FIX: a use-after-free shutdown crash
59
+
60
+ - 0.2.10
61
+
62
+ - 22-04-2020
63
+
64
+ - FEATURE: memory softlimit support for nogvl_context_call
65
+
66
+ - 0.2.9
67
+
68
+ - 09-01-2020
69
+
70
+ - FIX: correct segfault when JS returns a Symbol and properly cast to ruby symbol
71
+
1
72
  - 0.2.8
2
73
 
3
74
  - 11-11-2019
data/Dockerfile ADDED
@@ -0,0 +1,21 @@
1
+ ARG RUBY_VERSION=2.7
2
+ FROM ruby:${RUBY_VERSION}
3
+
4
+ RUN test ! -f /etc/alpine-release || apk add --no-cache build-base git
5
+
6
+ # without this `COPY .git`, we get the following error:
7
+ # fatal: not a git repository (or any of the parent directories): .git
8
+ # but with it we need the full gem just to compile the extension because
9
+ # of gemspec's `git --ls-files`
10
+ # COPY .git /code/.git
11
+ COPY Gemfile mini_racer.gemspec /code/
12
+ COPY lib/mini_racer/version.rb /code/lib/mini_racer/version.rb
13
+ WORKDIR /code
14
+ RUN bundle install
15
+
16
+ COPY Rakefile /code/
17
+ COPY ext /code/ext/
18
+ RUN bundle exec rake compile
19
+
20
+ COPY . /code/
21
+ CMD bundle exec irb -rmini_racer
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: 1000)`. 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:
@@ -244,7 +249,7 @@ It is possible to set V8 Runtime flags:
244
249
  MiniRacer::Platform.set_flags! :noconcurrent_recompilation, max_inlining_levels: 10
245
250
  ```
246
251
 
247
- This can come in handy if you want to use MiniRacer with Unicorn, which doesn't seem to alwatys appreciate V8's liberal use of threading:
252
+ This can come in handy if you want to use MiniRacer with Unicorn, which doesn't seem to always appreciate V8's liberal use of threading:
248
253
  ```ruby
249
254
  MiniRacer::Platform.set_flags! :noconcurrent_recompilation, :noconcurrent_sweeping
250
255
  ```
@@ -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
 
@@ -310,6 +315,16 @@ context.eval("a = 2")
310
315
  # nothing works on the context from now on, its a shell waiting to be disposed
311
316
  ```
312
317
 
318
+ A MiniRacer context can also be dumped in a heapsnapshot file using `#write_heap_snapshot(file_or_io)`
319
+
320
+ ```ruby
321
+ context = MiniRacer::Context.new(timeout: 5)
322
+ context.eval("let a='testing';")
323
+ context.write_heap_snapshot("test.heapsnapshot")
324
+ ```
325
+
326
+ This file can then be loaded in the memory tab of the chrome dev console.
327
+
313
328
  ### Function call
314
329
 
315
330
  This calls the function passed as first argument:
@@ -447,7 +462,7 @@ Add this to your .travis.yml file:
447
462
 
448
463
  ## Contributing
449
464
 
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.
465
+ 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
466
 
452
467
 
453
468
  ## License
data/Rakefile CHANGED
@@ -11,6 +11,7 @@ end
11
11
  task :default => [:compile, :test]
12
12
 
13
13
  gem = Gem::Specification.load( File.dirname(__FILE__) + '/mini_racer.gemspec' )
14
+ Rake::ExtensionTask.new( 'mini_racer_loader', gem )
14
15
  Rake::ExtensionTask.new( 'mini_racer_extension', gem )
15
16
 
16
17
 
@@ -1,5 +1,7 @@
1
1
  require 'mkmf'
2
- require 'libv8'
2
+ require_relative '../../lib/mini_racer/version'
3
+ gem 'libv8-node', MiniRacer::LIBV8_NODE_VERSION
4
+ require 'libv8-node'
3
5
 
4
6
  IS_DARWIN = RUBY_PLATFORM =~ /darwin/
5
7
 
@@ -11,11 +13,17 @@ $CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
11
13
  $CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
12
14
  $CPPFLAGS += " -std=c++0x"
13
15
  $CPPFLAGS += " -fpermissive"
16
+ $CPPFLAGS += " -DV8_COMPRESS_POINTERS"
17
+ $CPPFLAGS += " -fvisibility=hidden "
14
18
 
15
19
  $CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
16
20
 
17
21
  $LDFLAGS.insert(0, " -stdlib=libc++ ") if IS_DARWIN
18
22
 
23
+ # check for missing symbols at link time
24
+ # $LDFLAGS += " -Wl,--no-undefined " unless IS_DARWIN
25
+ # $LDFLAGS += " -Wl,-undefined,error " if IS_DARWIN
26
+
19
27
  if ENV['CXX']
20
28
  puts "SETTING CXX"
21
29
  CONFIG['CXX'] = ENV['CXX']
@@ -53,7 +61,7 @@ if enable_config('debug') || enable_config('asan')
53
61
  CONFIG['debugflags'] << ' -ggdb3 -O0'
54
62
  end
55
63
 
56
- Libv8.configure_makefile
64
+ Libv8::Node.configure_makefile
57
65
 
58
66
  if enable_config('asan')
59
67
  $CPPFLAGS.insert(0, " -fsanitize=address ")
@@ -25,6 +25,7 @@ public:
25
25
  ArrayBuffer::Allocator* allocator;
26
26
  StartupData* startup_data;
27
27
  bool interrupted;
28
+ bool added_gc_cb;
28
29
  pid_t pid;
29
30
  VALUE mutex;
30
31
 
@@ -42,15 +43,12 @@ public:
42
43
 
43
44
 
44
45
  IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
45
- interrupted(false), pid(getpid()), refs_count(0) {
46
+ interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
46
47
  VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
47
48
  mutex = rb_class_new_instance(0, nullptr, cMutex);
48
49
  }
49
50
 
50
- ~IsolateInfo() {
51
- void free_isolate(IsolateInfo*);
52
- free_isolate(this);
53
- }
51
+ ~IsolateInfo();
54
52
 
55
53
  void init(SnapshotInfo* snapshot_info = nullptr);
56
54
 
@@ -127,6 +125,7 @@ typedef struct {
127
125
  Local<Function> fun;
128
126
  Local<Value> *argv;
129
127
  EvalResult result;
128
+ size_t max_memory;
130
129
  } FunctionCall;
131
130
 
132
131
  enum IsolateFlags {
@@ -155,6 +154,11 @@ static VALUE rb_cDateTime = Qnil;
155
154
  static std::unique_ptr<Platform> current_platform = NULL;
156
155
  static std::mutex platform_lock;
157
156
 
157
+ static pthread_attr_t *thread_attr_p;
158
+ static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
159
+ static bool ruby_exiting = false; // guarded by exit_lock
160
+ static bool single_threaded = false;
161
+
158
162
  static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
159
163
  bool platform_already_initialized = false;
160
164
 
@@ -166,6 +170,9 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
166
170
  platform_lock.lock();
167
171
 
168
172
  if (current_platform == NULL) {
173
+ if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
174
+ single_threaded = true;
175
+ }
169
176
  V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
170
177
  } else {
171
178
  platform_already_initialized = true;
@@ -232,11 +239,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
232
239
  Local<Value> local_value = v8res.ToLocalChecked();
233
240
  if ((local_value->IsObject() || local_value->IsArray()) &&
234
241
  !local_value->IsDate() && !local_value->IsFunction()) {
235
- Local<Object> JSON = context->Global()->Get(String::NewFromUtf8(isolate, "JSON"))
236
- ->ToObject(context).ToLocalChecked();
242
+ Local<Object> JSON = context->Global()->Get(
243
+ context, String::NewFromUtf8Literal(isolate, "JSON"))
244
+ .ToLocalChecked().As<Object>();
237
245
 
238
- Local<Function> stringify = JSON->Get(v8::String::NewFromUtf8(isolate, "stringify"))
239
- .As<Function>();
246
+ Local<Function> stringify = JSON->Get(
247
+ context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
248
+ .ToLocalChecked().As<Function>();
240
249
 
241
250
  Local<Object> object = local_value->ToObject(context).ToLocalChecked();
242
251
  const unsigned argc = 1;
@@ -290,7 +299,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
290
299
  } else if(trycatch.HasTerminated()) {
291
300
  evalRes.terminated = true;
292
301
  evalRes.message = new Persistent<Value>();
293
- Local<String> tmp = String::NewFromUtf8(isolate, "JavaScript was terminated (either by timeout or explicitly)");
302
+ Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
294
303
  evalRes.message->Reset(isolate, tmp);
295
304
  }
296
305
  if (!trycatch.StackTrace(context).IsEmpty()) {
@@ -307,7 +316,8 @@ nogvl_context_eval(void* arg) {
307
316
 
308
317
  EvalParams* eval_params = (EvalParams*)arg;
309
318
  EvalResult* result = eval_params->result;
310
- Isolate* isolate = eval_params->context_info->isolate_info->isolate;
319
+ IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
320
+ Isolate* isolate = isolate_info->isolate;
311
321
 
312
322
  Isolate::Scope isolate_scope(isolate);
313
323
  HandleScope handle_scope(isolate);
@@ -351,7 +361,10 @@ nogvl_context_eval(void* arg) {
351
361
  // parsing successful
352
362
  if (eval_params->max_memory > 0) {
353
363
  isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
364
+ if (!isolate_info->added_gc_cb) {
354
365
  isolate->AddGCEpilogueCallback(gc_callback);
366
+ isolate_info->added_gc_cb = true;
367
+ }
355
368
  }
356
369
 
357
370
  maybe_value = parsed_script.ToLocalChecked()->Run(context);
@@ -364,6 +377,12 @@ nogvl_context_eval(void* arg) {
364
377
  return NULL;
365
378
  }
366
379
 
380
+ static VALUE new_empty_failed_conv_obj() {
381
+ // TODO isolate code that translates execption to ruby
382
+ // exception so we can properly return it
383
+ return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
384
+ }
385
+
367
386
  // assumes isolate locking is in place
368
387
  static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
369
388
  Local<Value> value) {
@@ -395,8 +414,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
395
414
  VALUE rb_array = rb_ary_new();
396
415
  Local<Array> arr = Local<Array>::Cast(value);
397
416
  for(uint32_t i=0; i < arr->Length(); i++) {
398
- Local<Value> element = arr->Get(i);
399
- VALUE rb_elem = convert_v8_to_ruby(isolate, context, element);
417
+ MaybeLocal<Value> element = arr->Get(context, i);
418
+ if (element.IsEmpty()) {
419
+ continue;
420
+ }
421
+ VALUE rb_elem = convert_v8_to_ruby(isolate, context, element.ToLocalChecked());
400
422
  if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
401
423
  return rb_elem;
402
424
  }
@@ -426,26 +448,47 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
426
448
  if (!maybe_props.IsEmpty()) {
427
449
  Local<Array> props = maybe_props.ToLocalChecked();
428
450
  for(uint32_t i=0; i < props->Length(); i++) {
429
- Local<Value> key = props->Get(i);
430
- VALUE rb_key = convert_v8_to_ruby(isolate, context, key);
431
- Local<Value> prop_value = object->Get(key);
432
- // this may have failed due to Get raising
451
+ MaybeLocal<Value> key = props->Get(context, i);
452
+ if (key.IsEmpty()) {
453
+ return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
454
+ }
455
+ VALUE rb_key = convert_v8_to_ruby(isolate, context, key.ToLocalChecked());
433
456
 
434
- if (trycatch.HasCaught()) {
435
- // TODO isolate code that translates execption to ruby
436
- // exception so we can properly return it
437
- return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
457
+ MaybeLocal<Value> prop_value = object->Get(context, key.ToLocalChecked());
458
+ // this may have failed due to Get raising
459
+ if (prop_value.IsEmpty() || trycatch.HasCaught()) {
460
+ return new_empty_failed_conv_obj();
438
461
  }
439
462
 
440
- VALUE rb_value = convert_v8_to_ruby(isolate, context, prop_value);
463
+ VALUE rb_value = convert_v8_to_ruby(
464
+ isolate, context, prop_value.ToLocalChecked());
441
465
  rb_hash_aset(rb_hash, rb_key, rb_value);
442
466
  }
443
467
  }
444
468
  return rb_hash;
445
469
  }
446
470
 
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"));
471
+ if (value->IsSymbol()) {
472
+ v8::String::Utf8Value symbol_name(isolate,
473
+ Local<Symbol>::Cast(value)->Name());
474
+
475
+ VALUE str_symbol = rb_enc_str_new(
476
+ *symbol_name,
477
+ symbol_name.length(),
478
+ rb_enc_find("utf-8")
479
+ );
480
+
481
+ return ID2SYM(rb_intern_str(str_symbol));
482
+ }
483
+
484
+ MaybeLocal<String> rstr_maybe = value->ToString(context);
485
+
486
+ if (rstr_maybe.IsEmpty()) {
487
+ return Qnil;
488
+ } else {
489
+ Local<String> rstr = rstr_maybe.ToLocalChecked();
490
+ return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
491
+ }
449
492
  }
450
493
 
451
494
  static VALUE convert_v8_to_ruby(Isolate* isolate,
@@ -500,7 +543,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
500
543
  length = RARRAY_LEN(value);
501
544
  array = Array::New(isolate, (int)length);
502
545
  for(i=0; i<length; i++) {
503
- array->Set(i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
546
+ array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
504
547
  }
505
548
  return scope.Escape(array);
506
549
  case T_HASH:
@@ -509,7 +552,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
509
552
  length = RARRAY_LEN(hash_as_array);
510
553
  for(i=0; i<length; i++) {
511
554
  pair = rb_ary_entry(hash_as_array, i);
512
- object->Set(convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
555
+ object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
513
556
  convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
514
557
  }
515
558
  return scope.Escape(object);
@@ -539,9 +582,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
539
582
  case T_UNDEF:
540
583
  case T_NODE:
541
584
  default:
542
- return scope.Escape(String::NewFromUtf8(isolate, "Undefined Conversion"));
585
+ return scope.Escape(String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
586
+ }
543
587
  }
544
- }
545
588
 
546
589
  static void unblock_eval(void *ptr) {
547
590
  EvalParams* eval = (EvalParams*)ptr;
@@ -552,53 +595,43 @@ static void unblock_eval(void *ptr) {
552
595
  * The implementations of the run_extra_code(), create_snapshot_data_blob() and
553
596
  * warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
554
597
  */
555
- bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
598
+ static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
556
599
  const char *utf8_source, const char *name) {
557
600
  Context::Scope context_scope(context);
558
601
  TryCatch try_catch(isolate);
559
602
  Local<String> source_string;
560
- if (!String::NewFromUtf8(isolate, utf8_source,
561
- NewStringType::kNormal)
562
- .ToLocal(&source_string)) {
603
+ if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
563
604
  return false;
564
605
  }
565
- Local<v8::String> resource_name =
566
- String::NewFromUtf8(isolate, name, NewStringType::kNormal)
567
- .ToLocalChecked();
606
+ Local<String> resource_name =
607
+ String::NewFromUtf8(isolate, name).ToLocalChecked();
568
608
  ScriptOrigin origin(resource_name);
569
609
  ScriptCompiler::Source source(source_string, origin);
570
610
  Local<Script> script;
571
611
  if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
572
612
  return false;
573
- if (script->Run(context).IsEmpty())
574
- return false;
575
- // CHECK(!try_catch.HasCaught());
613
+ if (script->Run(context).IsEmpty()) return false;
576
614
  return true;
577
615
  }
578
616
 
579
- StartupData
617
+ static StartupData
580
618
  create_snapshot_data_blob(const char *embedded_source = nullptr) {
581
- // Create a new isolate and a new context from scratch, optionally run
582
- // a script to embed, and serialize to create a snapshot blob.
583
- StartupData result = {nullptr, 0};
584
- {
585
- SnapshotCreator snapshot_creator;
586
- Isolate *isolate = snapshot_creator.GetIsolate();
619
+ Isolate *isolate = Isolate::Allocate();
620
+
621
+ // Optionally run a script to embed, and serialize to create a snapshot blob.
622
+ SnapshotCreator snapshot_creator(isolate);
587
623
  {
588
624
  HandleScope scope(isolate);
589
- Local<Context> context = Context::New(isolate);
625
+ Local<v8::Context> context = v8::Context::New(isolate);
590
626
  if (embedded_source != nullptr &&
591
- !run_extra_code(isolate, context, embedded_source,
592
- "<embedded>")) {
593
- return result;
627
+ !run_extra_code(isolate, context, embedded_source, "<embedded>")) {
628
+ return {};
594
629
  }
595
630
  snapshot_creator.SetDefaultContext(context);
596
631
  }
597
- result = snapshot_creator.CreateBlob(
632
+ return snapshot_creator.CreateBlob(
598
633
  SnapshotCreator::FunctionCodeHandling::kClear);
599
634
  }
600
- return result;
601
- }
602
635
 
603
636
  StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
604
637
  const char *warmup_source) {
@@ -745,6 +778,29 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
745
778
  return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
746
779
  }
747
780
 
781
+ static VALUE rb_isolate_low_memory_notification(VALUE self) {
782
+ IsolateInfo* isolate_info;
783
+ Data_Get_Struct(self, IsolateInfo, isolate_info);
784
+
785
+ if (current_platform == NULL) return Qfalse;
786
+
787
+ isolate_info->isolate->LowMemoryNotification();
788
+ return Qnil;
789
+ }
790
+
791
+ static VALUE rb_isolate_pump_message_loop(VALUE self) {
792
+ IsolateInfo* isolate_info;
793
+ Data_Get_Struct(self, IsolateInfo, isolate_info);
794
+
795
+ if (current_platform == NULL) return Qfalse;
796
+
797
+ if (platform::PumpMessageLoop(current_platform.get(), isolate_info->isolate)){
798
+ return Qtrue;
799
+ } else {
800
+ return Qfalse;
801
+ }
802
+ }
803
+
748
804
  static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
749
805
  ContextInfo* context_info;
750
806
  Data_Get_Struct(self, ContextInfo, context_info);
@@ -1015,7 +1071,9 @@ gvl_ruby_callback(void* data) {
1015
1071
  callback_data.failed = false;
1016
1072
 
1017
1073
  if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1018
- args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Terminated execution during transition from Ruby to JS"));
1074
+ args->GetIsolate()->ThrowException(
1075
+ String::NewFromUtf8Literal(args->GetIsolate(),
1076
+ "Terminated execution during transition from Ruby to JS"));
1019
1077
  args->GetIsolate()->TerminateExecution();
1020
1078
  if (length > 0) {
1021
1079
  rb_ary_clear(ruby_args);
@@ -1029,7 +1087,7 @@ gvl_ruby_callback(void* data) {
1029
1087
 
1030
1088
  if(callback_data.failed) {
1031
1089
  rb_iv_set(parent, "@current_exception", result);
1032
- args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Ruby exception"));
1090
+ args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
1033
1091
  }
1034
1092
  else {
1035
1093
  HandleScope scope(args->GetIsolate());
@@ -1100,7 +1158,9 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1100
1158
 
1101
1159
  if (parent_object == Qnil) {
1102
1160
  context->Global()->Set(
1103
- v8_str, FunctionTemplate::New(isolate, ruby_callback, external)
1161
+ context,
1162
+ v8_str,
1163
+ FunctionTemplate::New(isolate, ruby_callback, external)
1104
1164
  ->GetFunction(context)
1105
1165
  .ToLocalChecked());
1106
1166
 
@@ -1113,7 +1173,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1113
1173
 
1114
1174
  MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
1115
1175
  if (parsed_script.IsEmpty()) {
1116
- parse_error = true;
1176
+ parse_error = true;
1117
1177
  } else {
1118
1178
  MaybeLocal<Value> maybe_value =
1119
1179
  parsed_script.ToLocalChecked()->Run(context);
@@ -1123,11 +1183,12 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1123
1183
  Local<Value> value = maybe_value.ToLocalChecked();
1124
1184
  if (value->IsObject()) {
1125
1185
  value.As<Object>()->Set(
1126
- v8_str, FunctionTemplate::New(
1127
- isolate, ruby_callback, external)
1186
+ context,
1187
+ v8_str,
1188
+ FunctionTemplate::New(isolate, ruby_callback, external)
1128
1189
  ->GetFunction(context)
1129
1190
  .ToLocalChecked());
1130
- attach_error = false;
1191
+ attach_error = false;
1131
1192
  }
1132
1193
  }
1133
1194
  }
@@ -1157,35 +1218,39 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
1157
1218
  return context_info->isolate_info->mutex;
1158
1219
  }
1159
1220
 
1160
- void free_isolate(IsolateInfo* isolate_info) {
1161
-
1162
- if (isolate_info->isolate) {
1163
- Locker lock(isolate_info->isolate);
1164
- }
1165
-
1166
- if (isolate_info->isolate) {
1167
- if (isolate_info->interrupted) {
1168
- fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, it can not be disposed and memory will not be reclaimed till the Ruby process exits.\n");
1221
+ IsolateInfo::~IsolateInfo() {
1222
+ if (isolate) {
1223
+ if (this->interrupted) {
1224
+ fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, "
1225
+ "it can not be disposed and memory will not be "
1226
+ "reclaimed till the Ruby process exits.\n");
1169
1227
  } else {
1170
-
1171
- if (isolate_info->pid != getpid()) {
1172
- fprintf(stderr, "WARNING: V8 isolate was forked, it can not be disposed and memory will not be reclaimed till the Ruby process exits.\n");
1228
+ if (this->pid != getpid() && !single_threaded) {
1229
+ fprintf(stderr, "WARNING: V8 isolate was forked, "
1230
+ "it can not be disposed and "
1231
+ "memory will not be reclaimed "
1232
+ "till the Ruby process exits.\n"
1233
+ "It is VERY likely your process will hang.\n"
1234
+ "If you wish to use v8 in forked environment "
1235
+ "please ensure the platform is initialized with:\n"
1236
+ "MiniRacer::Platform.set_flags! :single_threaded\n"
1237
+ );
1173
1238
  } else {
1174
- isolate_info->isolate->Dispose();
1239
+ isolate->Dispose();
1175
1240
  }
1176
1241
  }
1177
- isolate_info->isolate = NULL;
1242
+ isolate = nullptr;
1178
1243
  }
1179
1244
 
1180
- if (isolate_info->startup_data) {
1181
- delete[] isolate_info->startup_data->data;
1182
- delete isolate_info->startup_data;
1245
+ if (startup_data) {
1246
+ delete[] startup_data->data;
1247
+ delete startup_data;
1183
1248
  }
1184
1249
 
1185
- delete isolate_info->allocator;
1250
+ delete allocator;
1186
1251
  }
1187
1252
 
1188
- static void *free_context_raw(void* arg) {
1253
+ static void free_context_raw(void *arg) {
1189
1254
  ContextInfo* context_info = (ContextInfo*)arg;
1190
1255
  IsolateInfo* isolate_info = context_info->isolate_info;
1191
1256
  Persistent<Context>* context = context_info->context;
@@ -1202,6 +1267,20 @@ static void *free_context_raw(void* arg) {
1202
1267
  }
1203
1268
 
1204
1269
  xfree(context_info);
1270
+ }
1271
+
1272
+ static void *free_context_thr(void* arg) {
1273
+ if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
1274
+ return NULL;
1275
+ }
1276
+ if (ruby_exiting) {
1277
+ return NULL;
1278
+ }
1279
+
1280
+ free_context_raw(arg);
1281
+
1282
+ pthread_rwlock_unlock(&exit_lock);
1283
+
1205
1284
  return NULL;
1206
1285
  }
1207
1286
 
@@ -1215,22 +1294,17 @@ static void free_context(ContextInfo* context_info) {
1215
1294
  context_info_copy->context = context_info->context;
1216
1295
 
1217
1296
  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
-
1297
+ pthread_t free_context_thread;
1298
+ if (pthread_create(&free_context_thread, thread_attr_p,
1299
+ free_context_thr, (void*)context_info_copy)) {
1300
+ fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
1301
+ }
1223
1302
  } else {
1224
1303
  free_context_raw(context_info_copy);
1225
1304
  }
1226
1305
 
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
- }
1306
+ context_info->context = NULL;
1307
+ context_info->isolate_info = NULL;
1234
1308
  }
1235
1309
 
1236
1310
  static void deallocate_isolate(void* data) {
@@ -1245,7 +1319,7 @@ static void mark_isolate(void* data) {
1245
1319
  isolate_info->mark();
1246
1320
  }
1247
1321
 
1248
- void deallocate(void* data) {
1322
+ static void deallocate(void* data) {
1249
1323
  ContextInfo* context_info = (ContextInfo*)data;
1250
1324
 
1251
1325
  free_context(context_info);
@@ -1260,22 +1334,22 @@ static void mark_context(void* data) {
1260
1334
  }
1261
1335
  }
1262
1336
 
1263
- void deallocate_external_function(void * data) {
1337
+ static void deallocate_external_function(void * data) {
1264
1338
  xfree(data);
1265
1339
  }
1266
1340
 
1267
- void deallocate_snapshot(void * data) {
1341
+ static void deallocate_snapshot(void * data) {
1268
1342
  SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
1269
1343
  delete[] snapshot_info->data;
1270
1344
  xfree(snapshot_info);
1271
1345
  }
1272
1346
 
1273
- VALUE allocate_external_function(VALUE klass) {
1347
+ static VALUE allocate_external_function(VALUE klass) {
1274
1348
  VALUE* self = ALLOC(VALUE);
1275
1349
  return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
1276
1350
  }
1277
1351
 
1278
- VALUE allocate(VALUE klass) {
1352
+ static VALUE allocate(VALUE klass) {
1279
1353
  ContextInfo* context_info = ALLOC(ContextInfo);
1280
1354
  context_info->isolate_info = NULL;
1281
1355
  context_info->context = NULL;
@@ -1283,7 +1357,7 @@ VALUE allocate(VALUE klass) {
1283
1357
  return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
1284
1358
  }
1285
1359
 
1286
- VALUE allocate_snapshot(VALUE klass) {
1360
+ static VALUE allocate_snapshot(VALUE klass) {
1287
1361
  SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
1288
1362
  snapshot_info->data = NULL;
1289
1363
  snapshot_info->raw_size = 0;
@@ -1291,7 +1365,7 @@ VALUE allocate_snapshot(VALUE klass) {
1291
1365
  return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
1292
1366
  }
1293
1367
 
1294
- VALUE allocate_isolate(VALUE klass) {
1368
+ static VALUE allocate_isolate(VALUE klass) {
1295
1369
  IsolateInfo* isolate_info = new IsolateInfo();
1296
1370
 
1297
1371
  return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
@@ -1388,6 +1462,8 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1388
1462
  FileOutputStream stream(fp);
1389
1463
  snap->Serialize(&stream, HeapSnapshot::kJSON);
1390
1464
 
1465
+ fflush(fp);
1466
+
1391
1467
  const_cast<HeapSnapshot*>(snap)->Delete();
1392
1468
 
1393
1469
  return Qtrue;
@@ -1428,13 +1504,23 @@ nogvl_context_call(void *args) {
1428
1504
  if (!call) {
1429
1505
  return NULL;
1430
1506
  }
1431
- Isolate* isolate = call->context_info->isolate_info->isolate;
1507
+ IsolateInfo *isolate_info = call->context_info->isolate_info;
1508
+ Isolate* isolate = isolate_info->isolate;
1432
1509
 
1433
1510
  // in gvl flag
1434
1511
  isolate->SetData(IN_GVL, (void*)false);
1435
1512
  // terminate ASAP
1436
1513
  isolate->SetData(DO_TERMINATE, (void*)false);
1437
1514
 
1515
+ if (call->max_memory > 0) {
1516
+ isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
1517
+ isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
1518
+ if (!isolate_info->added_gc_cb) {
1519
+ isolate->AddGCEpilogueCallback(gc_callback);
1520
+ isolate_info->added_gc_cb = true;
1521
+ }
1522
+ }
1523
+
1438
1524
  Isolate::Scope isolate_scope(isolate);
1439
1525
  EscapableHandleScope handle_scope(isolate);
1440
1526
  TryCatch trycatch(isolate);
@@ -1492,6 +1578,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1492
1578
  call_argv = argv + 1;
1493
1579
  }
1494
1580
 
1581
+ call.max_memory = 0;
1582
+ VALUE mem_softlimit = rb_iv_get(self, "@max_memory");
1583
+ if (mem_softlimit != Qnil) {
1584
+ unsigned long sl_int = NUM2ULONG(mem_softlimit);
1585
+ call.max_memory = (size_t)sl_int;
1586
+ }
1587
+
1495
1588
  bool missingFunction = false;
1496
1589
  {
1497
1590
  Locker lock(isolate);
@@ -1503,8 +1596,11 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1503
1596
 
1504
1597
  // examples of such usage can be found in
1505
1598
  // https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
1506
- Local<String> fname = String::NewFromUtf8(isolate, call.function_name);
1507
- MaybeLocal<v8::Value> val = context->Global()->Get(fname);
1599
+ MaybeLocal<String> fname = String::NewFromUtf8(isolate, call.function_name);
1600
+ MaybeLocal<v8::Value> val;
1601
+ if (!fname.IsEmpty()) {
1602
+ val = context->Global()->Get(context, fname.ToLocalChecked());
1603
+ }
1508
1604
 
1509
1605
  if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
1510
1606
  missingFunction = true;
@@ -1549,9 +1645,20 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
1549
1645
  return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
1550
1646
  }
1551
1647
 
1648
+ static void set_ruby_exiting(VALUE value) {
1649
+ (void)value;
1650
+
1651
+ int res = pthread_rwlock_wrlock(&exit_lock);
1652
+
1653
+ ruby_exiting = true;
1654
+ if (res == 0) {
1655
+ pthread_rwlock_unlock(&exit_lock);
1656
+ }
1657
+ }
1658
+
1552
1659
  extern "C" {
1553
1660
 
1554
- void Init_mini_racer_extension ( void )
1661
+ __attribute__((visibility("default"))) void Init_mini_racer_extension ( void )
1555
1662
  {
1556
1663
  VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
1557
1664
  rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
@@ -1599,8 +1706,23 @@ extern "C" {
1599
1706
  rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
1600
1707
 
1601
1708
  rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
1709
+ rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
1710
+ rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
1602
1711
  rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
1603
1712
 
1604
1713
  rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
1714
+
1715
+ rb_set_end_proc(set_ruby_exiting, Qnil);
1716
+
1717
+ static pthread_attr_t attr;
1718
+ if (pthread_attr_init(&attr) == 0) {
1719
+ if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0) {
1720
+ thread_attr_p = &attr;
1721
+ }
1722
+ }
1723
+ auto on_fork_for_child = []() {
1724
+ exit_lock = PTHREAD_RWLOCK_INITIALIZER;
1725
+ };
1726
+ pthread_atfork(nullptr, nullptr, on_fork_for_child);
1605
1727
  }
1606
1728
  }