sq_mini_racer 0.2.5.0.1.beta3 → 0.3.1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -26,46 +26,55 @@ def cppflags_add_cpu_extension!
26
26
  end
27
27
 
28
28
  def libv8_gem_name
29
- return "libv8-solaris" if IS_SOLARIS
30
- return "libv8-alpine" if IS_LINUX_MUSL
31
-
32
- 'libv8'
29
+ 'libv8-node'
33
30
  end
34
31
 
35
- def libv8_version
36
- '7.3.492.27.1'
32
+ def libv8_requirement
33
+ '~> 15.5.1.0.beta1'
37
34
  end
38
35
 
39
- def libv8_basename
40
- "#{libv8_gem_name}-#{libv8_version}-#{ruby_platform}"
36
+ def libv8_basename(version)
37
+ "#{libv8_gem_name}-#{version}-#{ruby_platform}"
41
38
  end
42
39
 
43
- def libv8_gemspec
44
- "#{libv8_basename}.gemspec"
40
+ def libv8_gemspec(version)
41
+ "#{libv8_basename(version)}.gemspec"
45
42
  end
46
43
 
47
44
  def libv8_local_path(path=Gem.path)
48
- puts "looking for #{libv8_gemspec} in installed gems"
49
- candidates = path.map { |p| File.join(p, 'specifications', libv8_gemspec) }
50
- found = candidates.select { |f| File.exist?(f) }.first
45
+ name_glob = "#{libv8_gem_name}-*-#{ruby_platform}"
46
+
47
+ puts "looking for #{name_glob} in #{path.inspect}"
48
+
49
+ paths = path.map { |p| Dir.glob(File.join(p, 'specifications', name_glob + '.gemspec')) }.flatten
51
50
 
52
- unless found
53
- puts "#{libv8_gemspec} not found in installed gems"
51
+ if paths.empty?
52
+ puts "#{name_glob} not found in #{path.inspect}"
54
53
  return
55
54
  end
56
55
 
57
- puts "found in installed specs: #{found}"
56
+ specs = paths.map { |p| [p, eval(File.read(p))] }
57
+ .select { |_, spec| Gem::Requirement.new(libv8_requirement).satisfied_by?(spec.version) }
58
+ found_path, found_spec = specs.sort_by { |_, spec| spec.version }.last
58
59
 
59
- dir = File.expand_path(File.join(found, '..', '..', 'gems', libv8_basename))
60
+ unless found_path && found_spec
61
+ puts "not found in specs: no '#{libv8_requirement}' in #{paths.inspect}"
62
+ return
63
+ end
64
+
65
+ puts "found in specs: #{found_path}"
66
+
67
+ gemdir = File.basename(found_path, '.gemspec')
68
+ dir = File.expand_path(File.join(found_path, '..', '..', 'gems', gemdir))
60
69
 
61
70
  unless Dir.exist?(dir)
62
- puts "not found in installed gems: #{dir}"
71
+ puts "not found in gems: #{dir}"
63
72
  return
64
73
  end
65
74
 
66
- puts "found in installed gems: #{dir}"
75
+ puts "found in gems: #{dir}"
67
76
 
68
- dir
77
+ [dir, found_spec]
69
78
  end
70
79
 
71
80
  def vendor_path
@@ -73,26 +82,12 @@ def vendor_path
73
82
  end
74
83
 
75
84
  def libv8_vendor_path
76
- puts "looking for #{libv8_basename} in #{vendor_path}"
77
- path = Dir.glob("#{vendor_path}/#{libv8_basename}").first
78
-
79
- unless path
80
- puts "#{libv8_basename} not found in #{vendor_path}"
81
- return
82
- end
83
-
84
- puts "looking for #{libv8_basename}/lib/libv8.rb in #{vendor_path}"
85
- unless Dir.glob(File.join(vendor_path, libv8_basename, 'lib', 'libv8.rb')).first
86
- puts "#{libv8_basename}/lib/libv8.rb not found in #{vendor_path}"
87
- return
88
- end
89
-
90
- path
85
+ libv8_local_path([vendor_path])
91
86
  end
92
87
 
93
88
  def parse_platform(str)
94
89
  Gem::Platform.new(str).tap do |p|
95
- p.instance_eval { @os = 'linux-musl' } if str =~ /musl/
90
+ p.instance_eval { @version = 'musl' } if str =~ /-musl/ && p.version.nil?
96
91
  p.instance_eval { @cpu = 'x86_64' } if str =~ /universal.*darwin/
97
92
  end
98
93
  end
@@ -116,21 +111,23 @@ def libv8_remote_search
116
111
  json = JSON.parse(body)
117
112
 
118
113
  versions = json.select do |v|
119
- Gem::Version.new(v['number']) == Gem::Version.new(libv8_version)
114
+ Gem::Requirement.new(libv8_requirement).satisfied_by?(Gem::Version.new(v['number']))
120
115
  end
121
116
  abort(<<-ERROR) if versions.empty?
122
- ERROR: could not find #{libv8_version}
117
+ ERROR: could not find #{libv8_gem_name} (requirement #{libv8_requirement}) in rubygems.org
123
118
  ERROR
124
119
 
125
120
  platform_versions = versions.select do |v|
126
121
  parse_platform(v['platform']) == ruby_platform unless v['platform'] =~ /universal.*darwin/
127
122
  end
128
123
  abort(<<-ERROR) if platform_versions.empty?
129
- ERROR: found #{libv8_gem_name}-#{libv8_version}, but no binary for #{ruby_platform}
130
- try "gem install #{libv8_gem_name} -v '#{libv8_version}'" to attempt to build libv8 from source
124
+ ERROR: found gems matching #{libv8_gem_name}:'#{libv8_requirement}', but no binary for #{ruby_platform}
125
+ try "gem install #{libv8_gem_name}:'#{libv8_requirement}'" to attempt to build #{libv8_gem_name} from source
131
126
  ERROR
132
127
 
133
- platform_versions.first
128
+ puts "found #{libv8_gem_name} for #{ruby_platform} on rubygems: #{platform_versions.map { |v| v['number'] }.join(', ')}"
129
+
130
+ platform_versions.sort_by { |v| Gem::Version.new(v['number']) }.last
134
131
  end
135
132
 
136
133
  def libv8_download_uri(name, version, platform)
@@ -142,19 +139,19 @@ def libv8_downloaded_gem(name, version, platform)
142
139
  end
143
140
 
144
141
  def libv8_download(name, version, platform)
145
- FileUtils.mkdir_p(vendor_path)
142
+ FileUtils.mkdir_p(File.join(vendor_path, 'cache'))
146
143
  body = http_get(libv8_download_uri(name, version, platform))
147
- File.open(File.join(vendor_path, libv8_downloaded_gem(name, version, platform)), 'wb') { |f| f.write(body) }
144
+ File.open(File.join(vendor_path, 'cache', libv8_downloaded_gem(name, version, platform)), 'wb') { |f| f.write(body) }
148
145
  end
149
146
 
150
147
  def libv8_install!
151
- cmd = "gem install #{libv8_gem_name} --version '#{libv8_version}' --install-dir '#{vendor_path}'"
148
+ cmd = "gem install #{libv8_gem_name} --version '#{libv8_requirement}' --install-dir '#{vendor_path}'"
152
149
  puts "installing #{libv8_gem_name} using `#{cmd}`"
153
150
  rc = system(cmd)
154
151
 
155
152
  abort(<<-ERROR) unless rc
156
- ERROR: could not install #{libv8_gem_name} #{libv8_version}
157
- try "gem install #{libv8_gem_name} -v '#{libv8_version}'" to attempt to build libv8 from source
153
+ ERROR: could not install #{libv8_gem_name}:#{libv8_requirement}
154
+ try "gem install #{libv8_gem_name} -v '#{libv8_requirement}'" to attempt to build libv8 from source
158
155
  ERROR
159
156
 
160
157
  libv8_local_path([vendor_path])
@@ -168,16 +165,21 @@ def libv8_vendor!
168
165
  puts "downloading #{libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform'])} to #{vendor_path}"
169
166
  libv8_download(libv8_gem_name, version['number'], version['platform'])
170
167
 
171
- package = Gem::Package.new(File.join(vendor_path, libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform'])))
172
- package.extract_files(File.join(vendor_path, File.basename(libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform']), '.gem')))
168
+ package = Gem::Package.new(File.join(vendor_path, 'cache', libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform'])))
169
+ package.extract_files(File.join(vendor_path, 'gems', File.basename(libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform']), '.gem')))
170
+ FileUtils.mkdir_p(File.join(vendor_path, 'specifications'))
171
+ File.open(File.join(vendor_path, 'specifications', File.basename(libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform']), '.gem') + '.gemspec'), 'wb') { |f| f.write(package.spec.to_ruby) }
173
172
 
174
173
  libv8_vendor_path
175
174
  end
176
175
 
177
176
  def ensure_libv8_load_path
178
- puts "detected platform #{RUBY_PLATFORM} => #{ruby_platform}"
177
+ puts "platform ruby:#{RUBY_PLATFORM} rubygems:#{Gem::Platform.new(RUBY_PLATFORM)} detected:#{ruby_platform}"
179
178
 
180
- libv8_path = libv8_local_path || libv8_vendor_path || libv8_vendor!
179
+ libv8_path, spec = libv8_local_path
180
+ if !ENV['ONLY_INSTALLED_LIBV8_GEM'] && !libv8_path
181
+ libv8_path, spec = libv8_vendor_path || libv8_vendor!
182
+ end
181
183
 
182
184
  abort(<<-ERROR) unless libv8_path
183
185
  ERROR: could not find #{libv8_gem_name}
@@ -189,7 +191,7 @@ end
189
191
 
190
192
  ensure_libv8_load_path
191
193
 
192
- require 'libv8'
194
+ require 'libv8-node'
193
195
 
194
196
  IS_DARWIN = RUBY_PLATFORM =~ /darwin/
195
197
 
@@ -202,17 +204,24 @@ $CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
202
204
  $CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
203
205
  $CPPFLAGS += " -std=c++0x"
204
206
  $CPPFLAGS += " -fpermissive"
207
+ $CPPFLAGS += " -DV8_COMPRESS_POINTERS"
208
+ $CPPFLAGS += " -fvisibility=hidden "
205
209
  cppflags_add_frame_pointer!
206
210
  cppflags_add_cpu_extension!
207
211
 
208
212
  $CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
209
213
 
210
214
  $LDFLAGS.insert(0, " -stdlib=libc++ ") if IS_DARWIN
215
+ $LDFLAGS += " -Wl,--no-undefined " unless IS_DARWIN
216
+ $LDFLAGS += " -Wl,-undefined,error " if IS_DARWIN
211
217
 
212
218
  if ENV['CXX']
213
219
  puts "SETTING CXX"
214
220
  CONFIG['CXX'] = ENV['CXX']
215
221
  end
222
+ # 1.9 has no $CXXFLAGS
223
+ $CPPFLAGS += " #{ENV['CPPFLAGS']}" if ENV['CPPFLAGS']
224
+ $LDFLAGS += " #{ENV['LDFLAGS']}" if ENV['LDFLAGS']
216
225
 
217
226
  CXX11_TEST = <<EOS
218
227
  #if __cplusplus <= 199711L
@@ -246,7 +255,7 @@ if enable_config('debug') || enable_config('asan')
246
255
  CONFIG['debugflags'] << ' -ggdb3 -O0'
247
256
  end
248
257
 
249
- Libv8.configure_makefile
258
+ Libv8::Node.configure_makefile
250
259
 
251
260
  if enable_config('asan')
252
261
  $CPPFLAGS.insert(0, " -fsanitize=address ")
@@ -34,7 +34,11 @@
34
34
  #include <atomic>
35
35
  #include <math.h>
36
36
  #include "compat.hpp"
37
+ #ifdef __x86_64__
37
38
  #include "simdutf8check.h"
39
+ #endif
40
+
41
+ #include <time.h>
38
42
 
39
43
  using namespace v8;
40
44
 
@@ -49,6 +53,7 @@ public:
49
53
  ArrayBuffer::Allocator* allocator;
50
54
  StartupData* startup_data;
51
55
  bool interrupted;
56
+ bool added_gc_cb;
52
57
  pid_t pid;
53
58
  VALUE mutex;
54
59
 
@@ -66,15 +71,12 @@ public:
66
71
 
67
72
 
68
73
  IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
69
- interrupted(false), pid(getpid()), refs_count(0) {
74
+ interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
70
75
  VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
71
76
  mutex = rb_class_new_instance(0, nullptr, cMutex);
72
77
  }
73
78
 
74
- ~IsolateInfo() {
75
- void free_isolate(IsolateInfo*);
76
- free_isolate(this);
77
- }
79
+ ~IsolateInfo();
78
80
 
79
81
  void init(SnapshotInfo* snapshot_info = nullptr);
80
82
 
@@ -151,6 +153,7 @@ typedef struct {
151
153
  Local<Function> fun;
152
154
  Local<Value> *argv;
153
155
  EvalResult result;
156
+ size_t max_memory;
154
157
  } FunctionCall;
155
158
 
156
159
  enum IsolateFlags {
@@ -179,6 +182,11 @@ static VALUE rb_cDateTime = Qnil;
179
182
  static std::unique_ptr<Platform> current_platform = NULL;
180
183
  static std::mutex platform_lock;
181
184
 
185
+ static pthread_attr_t *thread_attr_p;
186
+ static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
187
+ static bool ruby_exiting = false; // guarded by exit_lock
188
+ static bool single_threaded = false;
189
+
182
190
  static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
183
191
  bool platform_already_initialized = false;
184
192
 
@@ -190,6 +198,9 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
190
198
  platform_lock.lock();
191
199
 
192
200
  if (current_platform == NULL) {
201
+ if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
202
+ single_threaded = true;
203
+ }
193
204
  V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
194
205
  } else {
195
206
  platform_already_initialized = true;
@@ -256,11 +267,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
256
267
  Local<Value> local_value = v8res.ToLocalChecked();
257
268
  if ((local_value->IsObject() || local_value->IsArray()) &&
258
269
  !local_value->IsDate() && !local_value->IsFunction()) {
259
- Local<Object> JSON = context->Global()->Get(String::NewFromUtf8(isolate, "JSON"))
260
- ->ToObject(context).ToLocalChecked();
270
+ Local<Object> JSON = context->Global()->Get(
271
+ context, String::NewFromUtf8Literal(isolate, "JSON"))
272
+ .ToLocalChecked().As<Object>();
261
273
 
262
- Local<Function> stringify = JSON->Get(v8::String::NewFromUtf8(isolate, "stringify"))
263
- .As<Function>();
274
+ Local<Function> stringify = JSON->Get(
275
+ context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
276
+ .ToLocalChecked().As<Function>();
264
277
 
265
278
  Local<Object> object = local_value->ToObject(context).ToLocalChecked();
266
279
  const unsigned argc = 1;
@@ -314,7 +327,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
314
327
  } else if(trycatch.HasTerminated()) {
315
328
  evalRes.terminated = true;
316
329
  evalRes.message = new Persistent<Value>();
317
- Local<String> tmp = String::NewFromUtf8(isolate, "JavaScript was terminated (either by timeout or explicitly)");
330
+ Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
318
331
  evalRes.message->Reset(isolate, tmp);
319
332
  }
320
333
  if (!trycatch.StackTrace(context).IsEmpty()) {
@@ -335,7 +348,8 @@ nogvl_context_eval(void* arg) {
335
348
 
336
349
  EvalParams* eval_params = (EvalParams*)arg;
337
350
  EvalResult* result = eval_params->result;
338
- Isolate* isolate = eval_params->context_info->isolate_info->isolate;
351
+ IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
352
+ Isolate* isolate = isolate_info->isolate;
339
353
 
340
354
  Isolate::Scope isolate_scope(isolate);
341
355
  HandleScope handle_scope(isolate);
@@ -379,7 +393,10 @@ nogvl_context_eval(void* arg) {
379
393
  // parsing successful
380
394
  if (eval_params->max_memory > 0) {
381
395
  isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
396
+ if (!isolate_info->added_gc_cb) {
382
397
  isolate->AddGCEpilogueCallback(gc_callback);
398
+ isolate_info->added_gc_cb = true;
399
+ }
383
400
  }
384
401
 
385
402
  maybe_value = parsed_script.ToLocalChecked()->Run(context);
@@ -392,6 +409,12 @@ nogvl_context_eval(void* arg) {
392
409
  return 0;
393
410
  }
394
411
 
412
+ static VALUE new_empty_failed_conv_obj() {
413
+ // TODO isolate code that translates execption to ruby
414
+ // exception so we can properly return it
415
+ return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
416
+ }
417
+
395
418
  // assumes isolate locking is in place
396
419
  static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
397
420
  Local<Value> value) {
@@ -423,8 +446,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
423
446
  VALUE rb_array = rb_ary_new();
424
447
  Local<Array> arr = Local<Array>::Cast(value);
425
448
  for(uint32_t i=0; i < arr->Length(); i++) {
426
- Local<Value> element = arr->Get(i);
427
- VALUE rb_elem = convert_v8_to_ruby(isolate, context, element);
449
+ MaybeLocal<Value> element = arr->Get(context, i);
450
+ if (element.IsEmpty()) {
451
+ continue;
452
+ }
453
+ VALUE rb_elem = convert_v8_to_ruby(isolate, context, element.ToLocalChecked());
428
454
  if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
429
455
  return rb_elem;
430
456
  }
@@ -454,26 +480,47 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
454
480
  if (!maybe_props.IsEmpty()) {
455
481
  Local<Array> props = maybe_props.ToLocalChecked();
456
482
  for(uint32_t i=0; i < props->Length(); i++) {
457
- Local<Value> key = props->Get(i);
458
- VALUE rb_key = convert_v8_to_ruby(isolate, context, key);
459
- Local<Value> prop_value = object->Get(key);
460
- // this may have failed due to Get raising
483
+ MaybeLocal<Value> key = props->Get(context, i);
484
+ if (key.IsEmpty()) {
485
+ return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
486
+ }
487
+ VALUE rb_key = convert_v8_to_ruby(isolate, context, key.ToLocalChecked());
461
488
 
462
- if (trycatch.HasCaught()) {
463
- // TODO isolate code that translates execption to ruby
464
- // exception so we can properly return it
465
- return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
489
+ MaybeLocal<Value> prop_value = object->Get(context, key.ToLocalChecked());
490
+ // this may have failed due to Get raising
491
+ if (prop_value.IsEmpty() || trycatch.HasCaught()) {
492
+ return new_empty_failed_conv_obj();
466
493
  }
467
494
 
468
- VALUE rb_value = convert_v8_to_ruby(isolate, context, prop_value);
495
+ VALUE rb_value = convert_v8_to_ruby(
496
+ isolate, context, prop_value.ToLocalChecked());
469
497
  rb_hash_aset(rb_hash, rb_key, rb_value);
470
498
  }
471
499
  }
472
500
  return rb_hash;
473
501
  }
474
502
 
475
- Local<String> rstr = value->ToString(context).ToLocalChecked();
476
- return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
503
+ if (value->IsSymbol()) {
504
+ v8::String::Utf8Value symbol_name(isolate,
505
+ Local<Symbol>::Cast(value)->Name());
506
+
507
+ VALUE str_symbol = rb_enc_str_new(
508
+ *symbol_name,
509
+ symbol_name.length(),
510
+ rb_enc_find("utf-8")
511
+ );
512
+
513
+ return ID2SYM(rb_intern_str(str_symbol));
514
+ }
515
+
516
+ MaybeLocal<String> rstr_maybe = value->ToString(context);
517
+
518
+ if (rstr_maybe.IsEmpty()) {
519
+ return Qnil;
520
+ } else {
521
+ Local<String> rstr = rstr_maybe.ToLocalChecked();
522
+ return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
523
+ }
477
524
  }
478
525
 
479
526
  static VALUE convert_v8_to_ruby(Isolate* isolate,
@@ -519,11 +566,13 @@ static inline Local<Value> convert_ruby_str_to_v8(
519
566
  static const rb_encoding *usascii_enc = rb_usascii_encoding();
520
567
  static const rb_encoding *latin1_enc = rb_enc_find("ISO-8859-1");
521
568
  assert(latin1_enc != nullptr);
569
+ #ifdef __x86_64__
522
570
  #ifndef __AVX2__
523
571
  # define validate_utf8 validate_utf8_fast
524
572
  #else
525
573
  static const (*validate_utf8)(const char *, size_t) =
526
574
  best_utf8_validate_func();
575
+ #endif
527
576
  #endif
528
577
 
529
578
  rb_encoding *enc = rb_enc_get(value);
@@ -532,8 +581,12 @@ static inline Local<Value> convert_ruby_str_to_v8(
532
581
  if (len < 0 || len > INT_MAX) {
533
582
  return Null(isolate);
534
583
  }
584
+ #ifdef __x86_64__
535
585
  bool is_valid_utf8 = enc == utf8_enc &&
536
586
  validate_utf8(str, static_cast<size_t>(len));
587
+ #else
588
+ bool is_valid_utf8 = false;
589
+ #endif
537
590
 
538
591
  MaybeLocal<String> v8str;
539
592
  int int_len = static_cast<int>(len);
@@ -596,7 +649,11 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
596
649
  case T_FLOAT:
597
650
  return scope.Escape(Number::New(isolate, NUM2DBL(value)));
598
651
  case T_STRING:
599
- return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
652
+ //#ifndef __x86_64__
653
+ // return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
654
+ //#else
655
+ return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
656
+ //#endif
600
657
  case T_NIL:
601
658
  return scope.Escape(Null(isolate));
602
659
  case T_TRUE:
@@ -608,7 +665,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
608
665
  length = RARRAY_LEN(value);
609
666
  array = Array::New(isolate, (int)length);
610
667
  for(i=0; i<length; i++) {
611
- array->Set(i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
668
+ array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
612
669
  }
613
670
  return scope.Escape(array);
614
671
  }
@@ -619,7 +676,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
619
676
  length = RARRAY_LEN(hash_as_array);
620
677
  for(i=0; i<length; i++) {
621
678
  pair = rb_ary_entry(hash_as_array, i);
622
- object->Set(convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
679
+ object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
623
680
  convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
624
681
  }
625
682
  return scope.Escape(object);
@@ -627,7 +684,11 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
627
684
  case T_SYMBOL:
628
685
  {
629
686
  value = rb_funcall(value, rb_intern("to_s"), 0);
630
- return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
687
+ //#ifndef __x86_64__
688
+ // return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
689
+ //#else
690
+ return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
691
+ //#endif
631
692
  }
632
693
  case T_DATA:
633
694
  {
@@ -659,10 +720,15 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
659
720
  if (rb_respond_to(value, rb_intern("to_s"))) {
660
721
  // TODO: if this throws we're screwed
661
722
  value = rb_funcall(value, rb_intern("to_s"), 0);
723
+ //#ifndef __x86_64__
724
+ // return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
725
+ //#else
662
726
  return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
727
+ //#endif
663
728
  }
664
- return scope.Escape(String::NewFromUtf8(isolate, "Undefined Conversion"));
665
- }
729
+ return scope.Escape(
730
+ String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
731
+ }
666
732
  }
667
733
  }
668
734
 
@@ -675,53 +741,43 @@ static void unblock_eval(void *ptr) {
675
741
  * The implementations of the run_extra_code(), create_snapshot_data_blob() and
676
742
  * warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
677
743
  */
678
- bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
744
+ static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
679
745
  const char *utf8_source, const char *name) {
680
746
  Context::Scope context_scope(context);
681
747
  TryCatch try_catch(isolate);
682
748
  Local<String> source_string;
683
- if (!String::NewFromUtf8(isolate, utf8_source,
684
- NewStringType::kNormal)
685
- .ToLocal(&source_string)) {
749
+ if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
686
750
  return false;
687
751
  }
688
- Local<v8::String> resource_name =
689
- String::NewFromUtf8(isolate, name, NewStringType::kNormal)
690
- .ToLocalChecked();
752
+ Local<String> resource_name =
753
+ String::NewFromUtf8(isolate, name).ToLocalChecked();
691
754
  ScriptOrigin origin(resource_name);
692
755
  ScriptCompiler::Source source(source_string, origin);
693
756
  Local<Script> script;
694
757
  if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
695
758
  return false;
696
- if (script->Run(context).IsEmpty())
697
- return false;
698
- // CHECK(!try_catch.HasCaught());
759
+ if (script->Run(context).IsEmpty()) return false;
699
760
  return true;
700
761
  }
701
762
 
702
- StartupData
763
+ static StartupData
703
764
  create_snapshot_data_blob(const char *embedded_source = nullptr) {
704
- // Create a new isolate and a new context from scratch, optionally run
705
- // a script to embed, and serialize to create a snapshot blob.
706
- StartupData result = {nullptr, 0};
765
+ Isolate *isolate = Isolate::Allocate();
766
+
767
+ // Optionally run a script to embed, and serialize to create a snapshot blob.
768
+ SnapshotCreator snapshot_creator(isolate);
707
769
  {
708
- SnapshotCreator snapshot_creator;
709
- Isolate *isolate = snapshot_creator.GetIsolate();
710
- {
711
770
  HandleScope scope(isolate);
712
- Local<Context> context = Context::New(isolate);
771
+ Local<v8::Context> context = v8::Context::New(isolate);
713
772
  if (embedded_source != nullptr &&
714
- !run_extra_code(isolate, context, embedded_source,
715
- "<embedded>")) {
716
- return result;
773
+ !run_extra_code(isolate, context, embedded_source, "<embedded>")) {
774
+ return {};
717
775
  }
718
776
  snapshot_creator.SetDefaultContext(context);
719
777
  }
720
- result = snapshot_creator.CreateBlob(
778
+ return snapshot_creator.CreateBlob(
721
779
  SnapshotCreator::FunctionCodeHandling::kClear);
722
780
  }
723
- return result;
724
- }
725
781
 
726
782
  StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
727
783
  const char *warmup_source) {
@@ -868,6 +924,29 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
868
924
  return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
869
925
  }
870
926
 
927
+ static VALUE rb_isolate_low_memory_notification(VALUE self) {
928
+ IsolateInfo* isolate_info;
929
+ Data_Get_Struct(self, IsolateInfo, isolate_info);
930
+
931
+ if (current_platform == NULL) return Qfalse;
932
+
933
+ isolate_info->isolate->LowMemoryNotification();
934
+ return Qnil;
935
+ }
936
+
937
+ static VALUE rb_isolate_pump_message_loop(VALUE self) {
938
+ IsolateInfo* isolate_info;
939
+ Data_Get_Struct(self, IsolateInfo, isolate_info);
940
+
941
+ if (current_platform == NULL) return Qfalse;
942
+
943
+ if (platform::PumpMessageLoop(current_platform.get(), isolate_info->isolate)){
944
+ return Qtrue;
945
+ } else {
946
+ return Qfalse;
947
+ }
948
+ }
949
+
871
950
  static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
872
951
  ContextInfo* context_info;
873
952
  Data_Get_Struct(self, ContextInfo, context_info);
@@ -1142,7 +1221,9 @@ gvl_ruby_callback(void* data) {
1142
1221
  callback_data.failed = false;
1143
1222
 
1144
1223
  if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1145
- args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Terminated execution during transition from Ruby to JS"));
1224
+ args->GetIsolate()->ThrowException(
1225
+ String::NewFromUtf8Literal(args->GetIsolate(),
1226
+ "Terminated execution during transition from Ruby to JS"));
1146
1227
  args->GetIsolate()->TerminateExecution();
1147
1228
  if (length > 0) {
1148
1229
  rb_ary_clear(ruby_args);
@@ -1156,7 +1237,7 @@ gvl_ruby_callback(void* data) {
1156
1237
 
1157
1238
  if(callback_data.failed) {
1158
1239
  rb_iv_set(parent, "@current_exception", result);
1159
- args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Ruby exception"));
1240
+ args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
1160
1241
  }
1161
1242
  else {
1162
1243
  HandleScope scope(args->GetIsolate());
@@ -1231,7 +1312,9 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1231
1312
 
1232
1313
  if (parent_object == Qnil) {
1233
1314
  context->Global()->Set(
1234
- v8_str, FunctionTemplate::New(isolate, ruby_callback, external)
1315
+ context,
1316
+ v8_str,
1317
+ FunctionTemplate::New(isolate, ruby_callback, external)
1235
1318
  ->GetFunction(context)
1236
1319
  .ToLocalChecked());
1237
1320
 
@@ -1244,7 +1327,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1244
1327
 
1245
1328
  MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
1246
1329
  if (parsed_script.IsEmpty()) {
1247
- parse_error = true;
1330
+ parse_error = true;
1248
1331
  } else {
1249
1332
  MaybeLocal<Value> maybe_value =
1250
1333
  parsed_script.ToLocalChecked()->Run(context);
@@ -1254,11 +1337,12 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1254
1337
  Local<Value> value = maybe_value.ToLocalChecked();
1255
1338
  if (value->IsObject()) {
1256
1339
  value.As<Object>()->Set(
1257
- v8_str, FunctionTemplate::New(
1258
- isolate, ruby_callback, external)
1340
+ context,
1341
+ v8_str,
1342
+ FunctionTemplate::New(isolate, ruby_callback, external)
1259
1343
  ->GetFunction(context)
1260
1344
  .ToLocalChecked());
1261
- attach_error = false;
1345
+ attach_error = false;
1262
1346
  }
1263
1347
  }
1264
1348
  }
@@ -1288,35 +1372,39 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
1288
1372
  return context_info->isolate_info->mutex;
1289
1373
  }
1290
1374
 
1291
- void free_isolate(IsolateInfo* isolate_info) {
1292
-
1293
- if (isolate_info->isolate) {
1294
- Locker lock(isolate_info->isolate);
1295
- }
1296
-
1297
- if (isolate_info->isolate) {
1298
- if (isolate_info->interrupted) {
1299
- 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");
1375
+ IsolateInfo::~IsolateInfo() {
1376
+ if (isolate) {
1377
+ if (this->interrupted) {
1378
+ fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, "
1379
+ "it can not be disposed and memory will not be "
1380
+ "reclaimed till the Ruby process exits.\n");
1300
1381
  } else {
1301
-
1302
- if (isolate_info->pid != getpid()) {
1303
- fprintf(stderr, "WARNING: V8 isolate was forked, it can not be disposed and memory will not be reclaimed till the Ruby process exits.\n");
1382
+ if (this->pid != getpid() && !single_threaded) {
1383
+ fprintf(stderr, "WARNING: V8 isolate was forked, "
1384
+ "it can not be disposed and "
1385
+ "memory will not be reclaimed "
1386
+ "till the Ruby process exits.\n"
1387
+ "It is VERY likely your process will hang.\n"
1388
+ "If you wish to use v8 in forked environment "
1389
+ "please ensure the platform is initialized with:\n"
1390
+ "MiniRacer::Platform.set_flags! :single_threaded\n"
1391
+ );
1304
1392
  } else {
1305
- isolate_info->isolate->Dispose();
1393
+ isolate->Dispose();
1306
1394
  }
1307
1395
  }
1308
- isolate_info->isolate = NULL;
1396
+ isolate = nullptr;
1309
1397
  }
1310
1398
 
1311
- if (isolate_info->startup_data) {
1312
- delete[] isolate_info->startup_data->data;
1313
- delete isolate_info->startup_data;
1399
+ if (startup_data) {
1400
+ delete[] startup_data->data;
1401
+ delete startup_data;
1314
1402
  }
1315
1403
 
1316
- delete isolate_info->allocator;
1404
+ delete allocator;
1317
1405
  }
1318
1406
 
1319
- static void *free_context_raw(void* arg) {
1407
+ static void free_context_raw(void *arg) {
1320
1408
  ContextInfo* context_info = (ContextInfo*)arg;
1321
1409
  IsolateInfo* isolate_info = context_info->isolate_info;
1322
1410
  Persistent<Context>* context = context_info->context;
@@ -1333,6 +1421,20 @@ static void *free_context_raw(void* arg) {
1333
1421
  }
1334
1422
 
1335
1423
  xfree(context_info);
1424
+ }
1425
+
1426
+ static void *free_context_thr(void* arg) {
1427
+ if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
1428
+ return NULL;
1429
+ }
1430
+ if (ruby_exiting) {
1431
+ return NULL;
1432
+ }
1433
+
1434
+ free_context_raw(arg);
1435
+
1436
+ pthread_rwlock_unlock(&exit_lock);
1437
+
1336
1438
  return NULL;
1337
1439
  }
1338
1440
 
@@ -1347,22 +1449,17 @@ static void free_context(ContextInfo* context_info) {
1347
1449
 
1348
1450
  if (isolate_info && isolate_info->refs() > 1) {
1349
1451
  pthread_t free_context_thread;
1350
- if (pthread_create(&free_context_thread, NULL, free_context_raw, (void*)context_info_copy)) {
1452
+ if (pthread_create(&free_context_thread, thread_attr_p,
1453
+ free_context_thr, (void*)context_info_copy)) {
1351
1454
  fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
1352
1455
  }
1353
-
1354
1456
  } else {
1355
1457
  free_context_raw(context_info_copy);
1356
1458
  }
1357
1459
 
1358
- if (context_info->context && isolate_info && isolate_info->isolate) {
1359
1460
  context_info->context = NULL;
1360
- }
1361
-
1362
- if (isolate_info) {
1363
1461
  context_info->isolate_info = NULL;
1364
1462
  }
1365
- }
1366
1463
 
1367
1464
  static void deallocate_isolate(void* data) {
1368
1465
 
@@ -1376,7 +1473,7 @@ static void mark_isolate(void* data) {
1376
1473
  isolate_info->mark();
1377
1474
  }
1378
1475
 
1379
- void deallocate(void* data) {
1476
+ static void deallocate(void* data) {
1380
1477
  ContextInfo* context_info = (ContextInfo*)data;
1381
1478
 
1382
1479
  free_context(context_info);
@@ -1391,22 +1488,22 @@ static void mark_context(void* data) {
1391
1488
  }
1392
1489
  }
1393
1490
 
1394
- void deallocate_external_function(void * data) {
1491
+ static void deallocate_external_function(void * data) {
1395
1492
  xfree(data);
1396
1493
  }
1397
1494
 
1398
- void deallocate_snapshot(void * data) {
1495
+ static void deallocate_snapshot(void * data) {
1399
1496
  SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
1400
1497
  delete[] snapshot_info->data;
1401
1498
  xfree(snapshot_info);
1402
1499
  }
1403
1500
 
1404
- VALUE allocate_external_function(VALUE klass) {
1501
+ static VALUE allocate_external_function(VALUE klass) {
1405
1502
  VALUE* self = ALLOC(VALUE);
1406
1503
  return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
1407
1504
  }
1408
1505
 
1409
- VALUE allocate(VALUE klass) {
1506
+ static VALUE allocate(VALUE klass) {
1410
1507
  ContextInfo* context_info = ALLOC(ContextInfo);
1411
1508
  context_info->isolate_info = NULL;
1412
1509
  context_info->context = NULL;
@@ -1414,7 +1511,7 @@ VALUE allocate(VALUE klass) {
1414
1511
  return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
1415
1512
  }
1416
1513
 
1417
- VALUE allocate_snapshot(VALUE klass) {
1514
+ static VALUE allocate_snapshot(VALUE klass) {
1418
1515
  SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
1419
1516
  snapshot_info->data = NULL;
1420
1517
  snapshot_info->raw_size = 0;
@@ -1422,7 +1519,7 @@ VALUE allocate_snapshot(VALUE klass) {
1422
1519
  return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
1423
1520
  }
1424
1521
 
1425
- VALUE allocate_isolate(VALUE klass) {
1522
+ static VALUE allocate_isolate(VALUE klass) {
1426
1523
  IsolateInfo* isolate_info = new IsolateInfo();
1427
1524
 
1428
1525
  return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
@@ -1519,6 +1616,8 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1519
1616
  FileOutputStream stream(fp);
1520
1617
  snap->Serialize(&stream, HeapSnapshot::kJSON);
1521
1618
 
1619
+ fflush(fp);
1620
+
1522
1621
  const_cast<HeapSnapshot*>(snap)->Delete();
1523
1622
 
1524
1623
  return Qtrue;
@@ -1576,13 +1675,23 @@ nogvl_context_call(void *args) {
1576
1675
  if (!call) {
1577
1676
  return 0;
1578
1677
  }
1579
- Isolate* isolate = call->context_info->isolate_info->isolate;
1678
+ IsolateInfo *isolate_info = call->context_info->isolate_info;
1679
+ Isolate* isolate = isolate_info->isolate;
1580
1680
 
1581
1681
  // in gvl flag
1582
1682
  isolate->SetData(IN_GVL, (void*)false);
1583
1683
  // terminate ASAP
1584
1684
  isolate->SetData(DO_TERMINATE, (void*)false);
1585
1685
 
1686
+ if (call->max_memory > 0) {
1687
+ isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
1688
+ isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
1689
+ if (!isolate_info->added_gc_cb) {
1690
+ isolate->AddGCEpilogueCallback(gc_callback);
1691
+ isolate_info->added_gc_cb = true;
1692
+ }
1693
+ }
1694
+
1586
1695
  Isolate::Scope isolate_scope(isolate);
1587
1696
  EscapableHandleScope handle_scope(isolate);
1588
1697
  TryCatch trycatch(isolate);
@@ -1640,6 +1749,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1640
1749
  call_argv = argv + 1;
1641
1750
  }
1642
1751
 
1752
+ call.max_memory = 0;
1753
+ VALUE mem_softlimit = rb_iv_get(self, "@max_memory");
1754
+ if (mem_softlimit != Qnil) {
1755
+ unsigned long sl_int = NUM2ULONG(mem_softlimit);
1756
+ call.max_memory = (size_t)sl_int;
1757
+ }
1758
+
1643
1759
  bool missingFunction = false;
1644
1760
  {
1645
1761
  Locker lock(isolate);
@@ -1651,8 +1767,11 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1651
1767
 
1652
1768
  // examples of such usage can be found in
1653
1769
  // https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
1654
- Local<String> fname = String::NewFromUtf8(isolate, call.function_name);
1655
- MaybeLocal<v8::Value> val = context->Global()->Get(fname);
1770
+ MaybeLocal<String> fname = String::NewFromUtf8(isolate, call.function_name);
1771
+ MaybeLocal<v8::Value> val;
1772
+ if (!fname.IsEmpty()) {
1773
+ val = context->Global()->Get(context, fname.ToLocalChecked());
1774
+ }
1656
1775
 
1657
1776
  if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
1658
1777
  missingFunction = true;
@@ -1701,12 +1820,44 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
1701
1820
  return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
1702
1821
  }
1703
1822
 
1823
+ static void set_ruby_exiting(VALUE value) {
1824
+ (void)value;
1825
+
1826
+ int res = pthread_rwlock_wrlock(&exit_lock);
1827
+
1828
+ ruby_exiting = true;
1829
+ if (res == 0) {
1830
+ pthread_rwlock_unlock(&exit_lock);
1831
+ }
1832
+ }
1833
+
1834
+ static VALUE rb_monotime(VALUE self) {
1835
+ struct timespec ts;
1836
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
1837
+ return INT2FIX(-1);
1838
+ }
1839
+
1840
+ return DBL2NUM(
1841
+ (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0);
1842
+ }
1843
+
1704
1844
  extern "C" {
1705
1845
 
1706
- void Init_sq_mini_racer_extension ( void )
1846
+ __attribute__((visibility("default"))) void Init_sq_mini_racer_extension ( void )
1707
1847
  {
1708
- VALUE rb_mSqreen = rb_define_module("Sqreen");
1848
+ ID sqreen_id = rb_intern("Sqreen");
1849
+ VALUE rb_mSqreen;
1850
+ if (rb_const_defined(rb_cObject, sqreen_id)) {
1851
+ rb_mSqreen = rb_const_get(rb_cObject, sqreen_id);
1852
+ if (TYPE(rb_mSqreen) != T_MODULE) {
1853
+ rb_raise(rb_eTypeError, "Sqreen is not a module");
1854
+ return;
1855
+ }
1856
+ } else {
1857
+ rb_mSqreen = rb_define_module("Sqreen");
1858
+ }
1709
1859
  VALUE rb_mMiniRacer = rb_define_module_under(rb_mSqreen, "MiniRacer");
1860
+ rb_define_module_function(rb_mMiniRacer, "monotime", (VALUE(*)(...))&rb_monotime, 0);
1710
1861
  rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
1711
1862
  rb_cSnapshot = rb_define_class_under(rb_mMiniRacer, "Snapshot", rb_cObject);
1712
1863
  rb_cIsolate = rb_define_class_under(rb_mMiniRacer, "Isolate", rb_cObject);
@@ -1753,8 +1904,23 @@ extern "C" {
1753
1904
  rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
1754
1905
 
1755
1906
  rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
1907
+ rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
1908
+ rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
1756
1909
  rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
1757
1910
 
1758
1911
  rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
1912
+
1913
+ rb_set_end_proc(set_ruby_exiting, Qnil);
1914
+
1915
+ static pthread_attr_t attr;
1916
+ if (pthread_attr_init(&attr) == 0) {
1917
+ if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0) {
1918
+ thread_attr_p = &attr;
1919
+ }
1920
+ }
1921
+ auto on_fork_for_child = []() {
1922
+ exit_lock = PTHREAD_RWLOCK_INITIALIZER;
1923
+ };
1924
+ pthread_atfork(nullptr, nullptr, on_fork_for_child);
1759
1925
  }
1760
1926
  }