mini_racer 0.2.9 → 0.5.0

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.
@@ -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
 
@@ -117,6 +115,7 @@ typedef struct {
117
115
  useconds_t timeout;
118
116
  EvalResult* result;
119
117
  size_t max_memory;
118
+ size_t marshal_stackdepth;
120
119
  } EvalParams;
121
120
 
122
121
  typedef struct {
@@ -127,13 +126,153 @@ typedef struct {
127
126
  Local<Function> fun;
128
127
  Local<Value> *argv;
129
128
  EvalResult result;
129
+ size_t max_memory;
130
+ size_t marshal_stackdepth;
130
131
  } FunctionCall;
131
132
 
132
- enum IsolateFlags {
133
- IN_GVL,
134
- DO_TERMINATE,
135
- MEM_SOFTLIMIT_VALUE,
136
- MEM_SOFTLIMIT_REACHED,
133
+ class IsolateData {
134
+ public:
135
+ enum Flag {
136
+ // first flags are bitfield
137
+ // max count: sizeof(uintptr_t) * 8
138
+ IN_GVL, // whether we are inside of ruby gvl or not
139
+ DO_TERMINATE, // terminate as soon as possible
140
+ MEM_SOFTLIMIT_REACHED, // we've hit the memory soft limit
141
+ MEM_SOFTLIMIT_MAX, // maximum memory value
142
+ MARSHAL_STACKDEPTH_REACHED, // we've hit our max stack depth
143
+ MARSHAL_STACKDEPTH_VALUE, // current stackdepth
144
+ MARSHAL_STACKDEPTH_MAX, // maximum stack depth during marshal
145
+ };
146
+
147
+ static void Init(Isolate *isolate) {
148
+ // zero out all fields in the bitfield
149
+ isolate->SetData(0, 0);
150
+ }
151
+
152
+ static uintptr_t Get(Isolate *isolate, Flag flag) {
153
+ Bitfield u = { reinterpret_cast<uint64_t>(isolate->GetData(0)) };
154
+ switch (flag) {
155
+ case IN_GVL: return u.IN_GVL;
156
+ case DO_TERMINATE: return u.DO_TERMINATE;
157
+ case MEM_SOFTLIMIT_REACHED: return u.MEM_SOFTLIMIT_REACHED;
158
+ case MEM_SOFTLIMIT_MAX: return static_cast<uintptr_t>(u.MEM_SOFTLIMIT_MAX) << 10;
159
+ case MARSHAL_STACKDEPTH_REACHED: return u.MARSHAL_STACKDEPTH_REACHED;
160
+ case MARSHAL_STACKDEPTH_VALUE: return u.MARSHAL_STACKDEPTH_VALUE;
161
+ case MARSHAL_STACKDEPTH_MAX: return u.MARSHAL_STACKDEPTH_MAX;
162
+ }
163
+
164
+ // avoid compiler warning
165
+ return u.IN_GVL;
166
+ }
167
+
168
+ static void Set(Isolate *isolate, Flag flag, uintptr_t value) {
169
+ Bitfield u = { reinterpret_cast<uint64_t>(isolate->GetData(0)) };
170
+ switch (flag) {
171
+ case IN_GVL: u.IN_GVL = value; break;
172
+ case DO_TERMINATE: u.DO_TERMINATE = value; break;
173
+ case MEM_SOFTLIMIT_REACHED: u.MEM_SOFTLIMIT_REACHED = value; break;
174
+ // drop least significant 10 bits 'store memory amount in kb'
175
+ case MEM_SOFTLIMIT_MAX: u.MEM_SOFTLIMIT_MAX = value >> 10; break;
176
+ case MARSHAL_STACKDEPTH_REACHED: u.MARSHAL_STACKDEPTH_REACHED = value; break;
177
+ case MARSHAL_STACKDEPTH_VALUE: u.MARSHAL_STACKDEPTH_VALUE = value; break;
178
+ case MARSHAL_STACKDEPTH_MAX: u.MARSHAL_STACKDEPTH_MAX = value; break;
179
+ }
180
+ isolate->SetData(0, reinterpret_cast<void*>(u.dataPtr));
181
+ }
182
+
183
+ private:
184
+ struct Bitfield {
185
+ // WARNING: this would explode on platforms below 64 bit ptrs
186
+ // compiler will fail here, making it clear for them.
187
+ // Additionally, using the other part of the union to reinterpret the
188
+ // memory is undefined behavior according to spec, but is / has been stable
189
+ // across major compilers for decades.
190
+ static_assert(sizeof(uintptr_t) >= sizeof(uint64_t), "mini_racer not supported on this platform. ptr size must be at least 64 bit.");
191
+ union {
192
+ uint64_t dataPtr: 64;
193
+ // order in this struct matters. For cpu performance keep larger subobjects
194
+ // aligned on their boundaries (8 16 32), try not to straddle
195
+ struct {
196
+ size_t MEM_SOFTLIMIT_MAX:22;
197
+ bool IN_GVL:1;
198
+ bool DO_TERMINATE:1;
199
+ bool MEM_SOFTLIMIT_REACHED:1;
200
+ bool MARSHAL_STACKDEPTH_REACHED:1;
201
+ uint8_t :0; // align to next 8bit bound
202
+ size_t MARSHAL_STACKDEPTH_VALUE:10;
203
+ uint8_t :0; // align to next 8bit bound
204
+ size_t MARSHAL_STACKDEPTH_MAX:10;
205
+ };
206
+ };
207
+ };
208
+ };
209
+
210
+ struct StackCounter {
211
+ static void Reset(Isolate* isolate) {
212
+ if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX) > 0) {
213
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, 0);
214
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, false);
215
+ }
216
+ }
217
+
218
+ static void SetMax(Isolate* isolate, size_t marshalMaxStackDepth) {
219
+ if (marshalMaxStackDepth > 0) {
220
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX, marshalMaxStackDepth);
221
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, 0);
222
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, false);
223
+ }
224
+ }
225
+
226
+ StackCounter(Isolate* isolate) {
227
+ this->isActive = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX) > 0;
228
+
229
+ if (this->isActive) {
230
+ this->isolate = isolate;
231
+ this->IncDepth(1);
232
+ }
233
+ }
234
+
235
+ bool IsTooDeep() {
236
+ if (!this->IsActive()) {
237
+ return false;
238
+ }
239
+
240
+ size_t depth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE);
241
+ size_t maxDepth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_MAX);
242
+ if (depth > maxDepth) {
243
+ IsolateData::Set(this->isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, true);
244
+ return true;
245
+ }
246
+
247
+ return false;
248
+ }
249
+
250
+ bool IsActive() {
251
+ return this->isActive && !IsolateData::Get(this->isolate, IsolateData::DO_TERMINATE);
252
+ }
253
+
254
+ ~StackCounter() {
255
+ if (this->IsActive()) {
256
+ this->IncDepth(-1);
257
+ }
258
+ }
259
+
260
+ private:
261
+ Isolate* isolate;
262
+ bool isActive;
263
+
264
+ void IncDepth(int direction) {
265
+ int inc = direction > 0 ? 1 : -1;
266
+
267
+ size_t depth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE);
268
+
269
+ // don't decrement past 0
270
+ if (inc > 0 || depth > 0) {
271
+ depth += inc;
272
+ }
273
+
274
+ IsolateData::Set(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, depth);
275
+ }
137
276
  };
138
277
 
139
278
  static VALUE rb_cContext;
@@ -155,6 +294,11 @@ static VALUE rb_cDateTime = Qnil;
155
294
  static std::unique_ptr<Platform> current_platform = NULL;
156
295
  static std::mutex platform_lock;
157
296
 
297
+ static pthread_attr_t *thread_attr_p;
298
+ static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
299
+ static bool ruby_exiting = false; // guarded by exit_lock
300
+ static bool single_threaded = false;
301
+
158
302
  static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
159
303
  bool platform_already_initialized = false;
160
304
 
@@ -166,6 +310,9 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
166
310
  platform_lock.lock();
167
311
 
168
312
  if (current_platform == NULL) {
313
+ if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
314
+ single_threaded = true;
315
+ }
169
316
  V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
170
317
  } else {
171
318
  platform_already_initialized = true;
@@ -198,16 +345,18 @@ static void init_v8() {
198
345
  }
199
346
 
200
347
  static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
201
- if((bool)isolate->GetData(MEM_SOFTLIMIT_REACHED)) return;
348
+ if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
349
+ return;
350
+ }
202
351
 
203
- size_t softlimit = *(size_t*) isolate->GetData(MEM_SOFTLIMIT_VALUE);
352
+ size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
204
353
 
205
354
  HeapStatistics stats;
206
355
  isolate->GetHeapStatistics(&stats);
207
356
  size_t used = stats.used_heap_size();
208
357
 
209
358
  if(used > softlimit) {
210
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)true);
359
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
211
360
  isolate->TerminateExecution();
212
361
  }
213
362
  }
@@ -232,11 +381,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
232
381
  Local<Value> local_value = v8res.ToLocalChecked();
233
382
  if ((local_value->IsObject() || local_value->IsArray()) &&
234
383
  !local_value->IsDate() && !local_value->IsFunction()) {
235
- Local<Object> JSON = context->Global()->Get(String::NewFromUtf8(isolate, "JSON"))
236
- ->ToObject(context).ToLocalChecked();
384
+ Local<Object> JSON = context->Global()->Get(
385
+ context, String::NewFromUtf8Literal(isolate, "JSON"))
386
+ .ToLocalChecked().As<Object>();
237
387
 
238
- Local<Function> stringify = JSON->Get(v8::String::NewFromUtf8(isolate, "stringify"))
239
- .As<Function>();
388
+ Local<Function> stringify = JSON->Get(
389
+ context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
390
+ .ToLocalChecked().As<Function>();
240
391
 
241
392
  Local<Object> object = local_value->ToObject(context).ToLocalChecked();
242
393
  const unsigned argc = 1;
@@ -290,7 +441,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
290
441
  } else if(trycatch.HasTerminated()) {
291
442
  evalRes.terminated = true;
292
443
  evalRes.message = new Persistent<Value>();
293
- Local<String> tmp = String::NewFromUtf8(isolate, "JavaScript was terminated (either by timeout or explicitly)");
444
+ Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
294
445
  evalRes.message->Reset(isolate, tmp);
295
446
  }
296
447
  if (!trycatch.StackTrace(context).IsEmpty()) {
@@ -307,7 +458,8 @@ nogvl_context_eval(void* arg) {
307
458
 
308
459
  EvalParams* eval_params = (EvalParams*)arg;
309
460
  EvalResult* result = eval_params->result;
310
- Isolate* isolate = eval_params->context_info->isolate_info->isolate;
461
+ IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
462
+ Isolate* isolate = isolate_info->isolate;
311
463
 
312
464
  Isolate::Scope isolate_scope(isolate);
313
465
  HandleScope handle_scope(isolate);
@@ -316,14 +468,15 @@ nogvl_context_eval(void* arg) {
316
468
  Context::Scope context_scope(context);
317
469
  v8::ScriptOrigin *origin = NULL;
318
470
 
319
- // in gvl flag
320
- isolate->SetData(IN_GVL, (void*)false);
321
- // terminate ASAP
322
- isolate->SetData(DO_TERMINATE, (void*)false);
323
- // Memory softlimit
324
- isolate->SetData(MEM_SOFTLIMIT_VALUE, (void*)false);
325
- // Memory softlimit hit flag
326
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
471
+ IsolateData::Init(isolate);
472
+
473
+ if (eval_params->max_memory > 0) {
474
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, eval_params->max_memory);
475
+ if (!isolate_info->added_gc_cb) {
476
+ isolate->AddGCEpilogueCallback(gc_callback);
477
+ isolate_info->added_gc_cb = true;
478
+ }
479
+ }
327
480
 
328
481
  MaybeLocal<Script> parsed_script;
329
482
 
@@ -349,9 +502,8 @@ nogvl_context_eval(void* arg) {
349
502
  result->message->Reset(isolate, trycatch.Exception());
350
503
  } else {
351
504
  // parsing successful
352
- if (eval_params->max_memory > 0) {
353
- isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
354
- isolate->AddGCEpilogueCallback(gc_callback);
505
+ if (eval_params->marshal_stackdepth > 0) {
506
+ StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
355
507
  }
356
508
 
357
509
  maybe_value = parsed_script.ToLocalChecked()->Run(context);
@@ -359,11 +511,17 @@ nogvl_context_eval(void* arg) {
359
511
 
360
512
  prepare_result(maybe_value, trycatch, isolate, context, *result);
361
513
 
362
- isolate->SetData(IN_GVL, (void*)true);
514
+ IsolateData::Set(isolate, IsolateData::IN_GVL, true);
363
515
 
364
516
  return NULL;
365
517
  }
366
518
 
519
+ static VALUE new_empty_failed_conv_obj() {
520
+ // TODO isolate code that translates execption to ruby
521
+ // exception so we can properly return it
522
+ return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
523
+ }
524
+
367
525
  // assumes isolate locking is in place
368
526
  static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
369
527
  Local<Value> value) {
@@ -371,6 +529,18 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
371
529
  Isolate::Scope isolate_scope(isolate);
372
530
  HandleScope scope(isolate);
373
531
 
532
+ StackCounter stackCounter(isolate);
533
+
534
+ if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED)) {
535
+ return Qnil;
536
+ }
537
+
538
+ if (stackCounter.IsTooDeep()) {
539
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
540
+ isolate->TerminateExecution();
541
+ return Qnil;
542
+ }
543
+
374
544
  if (value->IsNull() || value->IsUndefined()){
375
545
  return Qnil;
376
546
  }
@@ -395,8 +565,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
395
565
  VALUE rb_array = rb_ary_new();
396
566
  Local<Array> arr = Local<Array>::Cast(value);
397
567
  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);
568
+ MaybeLocal<Value> element = arr->Get(context, i);
569
+ if (element.IsEmpty()) {
570
+ continue;
571
+ }
572
+ VALUE rb_elem = convert_v8_to_ruby(isolate, context, element.ToLocalChecked());
400
573
  if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
401
574
  return rb_elem;
402
575
  }
@@ -426,18 +599,20 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
426
599
  if (!maybe_props.IsEmpty()) {
427
600
  Local<Array> props = maybe_props.ToLocalChecked();
428
601
  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
602
+ MaybeLocal<Value> key = props->Get(context, i);
603
+ if (key.IsEmpty()) {
604
+ return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
605
+ }
606
+ VALUE rb_key = convert_v8_to_ruby(isolate, context, key.ToLocalChecked());
433
607
 
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(""));
608
+ MaybeLocal<Value> prop_value = object->Get(context, key.ToLocalChecked());
609
+ // this may have failed due to Get raising
610
+ if (prop_value.IsEmpty() || trycatch.HasCaught()) {
611
+ return new_empty_failed_conv_obj();
438
612
  }
439
613
 
440
- VALUE rb_value = convert_v8_to_ruby(isolate, context, prop_value);
614
+ VALUE rb_value = convert_v8_to_ruby(
615
+ isolate, context, prop_value.ToLocalChecked());
441
616
  rb_hash_aset(rb_hash, rb_key, rb_value);
442
617
  }
443
618
  }
@@ -454,7 +629,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
454
629
  rb_enc_find("utf-8")
455
630
  );
456
631
 
457
- return ID2SYM(rb_intern_str(str_symbol));
632
+ return rb_str_intern(str_symbol);
458
633
  }
459
634
 
460
635
  MaybeLocal<String> rstr_maybe = value->ToString(context);
@@ -519,7 +694,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
519
694
  length = RARRAY_LEN(value);
520
695
  array = Array::New(isolate, (int)length);
521
696
  for(i=0; i<length; i++) {
522
- array->Set(i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
697
+ Maybe<bool> success = array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
698
+ (void)(success);
523
699
  }
524
700
  return scope.Escape(array);
525
701
  case T_HASH:
@@ -528,8 +704,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
528
704
  length = RARRAY_LEN(hash_as_array);
529
705
  for(i=0; i<length; i++) {
530
706
  pair = rb_ary_entry(hash_as_array, i);
531
- object->Set(convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
707
+ Maybe<bool> success = object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
532
708
  convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
709
+ (void)(success);
533
710
  }
534
711
  return scope.Escape(object);
535
712
  case T_SYMBOL:
@@ -558,9 +735,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
558
735
  case T_UNDEF:
559
736
  case T_NODE:
560
737
  default:
561
- return scope.Escape(String::NewFromUtf8(isolate, "Undefined Conversion"));
738
+ return scope.Escape(String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
739
+ }
562
740
  }
563
- }
564
741
 
565
742
  static void unblock_eval(void *ptr) {
566
743
  EvalParams* eval = (EvalParams*)ptr;
@@ -571,53 +748,43 @@ static void unblock_eval(void *ptr) {
571
748
  * The implementations of the run_extra_code(), create_snapshot_data_blob() and
572
749
  * warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
573
750
  */
574
- bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
751
+ static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
575
752
  const char *utf8_source, const char *name) {
576
753
  Context::Scope context_scope(context);
577
754
  TryCatch try_catch(isolate);
578
755
  Local<String> source_string;
579
- if (!String::NewFromUtf8(isolate, utf8_source,
580
- NewStringType::kNormal)
581
- .ToLocal(&source_string)) {
756
+ if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
582
757
  return false;
583
758
  }
584
- Local<v8::String> resource_name =
585
- String::NewFromUtf8(isolate, name, NewStringType::kNormal)
586
- .ToLocalChecked();
759
+ Local<String> resource_name =
760
+ String::NewFromUtf8(isolate, name).ToLocalChecked();
587
761
  ScriptOrigin origin(resource_name);
588
762
  ScriptCompiler::Source source(source_string, origin);
589
763
  Local<Script> script;
590
764
  if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
591
765
  return false;
592
- if (script->Run(context).IsEmpty())
593
- return false;
594
- // CHECK(!try_catch.HasCaught());
766
+ if (script->Run(context).IsEmpty()) return false;
595
767
  return true;
596
768
  }
597
769
 
598
- StartupData
770
+ static StartupData
599
771
  create_snapshot_data_blob(const char *embedded_source = nullptr) {
600
- // Create a new isolate and a new context from scratch, optionally run
601
- // a script to embed, and serialize to create a snapshot blob.
602
- StartupData result = {nullptr, 0};
603
- {
604
- SnapshotCreator snapshot_creator;
605
- Isolate *isolate = snapshot_creator.GetIsolate();
772
+ Isolate *isolate = Isolate::Allocate();
773
+
774
+ // Optionally run a script to embed, and serialize to create a snapshot blob.
775
+ SnapshotCreator snapshot_creator(isolate);
606
776
  {
607
777
  HandleScope scope(isolate);
608
- Local<Context> context = Context::New(isolate);
778
+ Local<v8::Context> context = v8::Context::New(isolate);
609
779
  if (embedded_source != nullptr &&
610
- !run_extra_code(isolate, context, embedded_source,
611
- "<embedded>")) {
612
- return result;
780
+ !run_extra_code(isolate, context, embedded_source, "<embedded>")) {
781
+ return {};
613
782
  }
614
783
  snapshot_creator.SetDefaultContext(context);
615
784
  }
616
- result = snapshot_creator.CreateBlob(
785
+ return snapshot_creator.CreateBlob(
617
786
  SnapshotCreator::FunctionCodeHandling::kClear);
618
787
  }
619
- return result;
620
- }
621
788
 
622
789
  StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
623
790
  const char *warmup_source) {
@@ -764,6 +931,29 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
764
931
  return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
765
932
  }
766
933
 
934
+ static VALUE rb_isolate_low_memory_notification(VALUE self) {
935
+ IsolateInfo* isolate_info;
936
+ Data_Get_Struct(self, IsolateInfo, isolate_info);
937
+
938
+ if (current_platform == NULL) return Qfalse;
939
+
940
+ isolate_info->isolate->LowMemoryNotification();
941
+ return Qnil;
942
+ }
943
+
944
+ static VALUE rb_isolate_pump_message_loop(VALUE self) {
945
+ IsolateInfo* isolate_info;
946
+ Data_Get_Struct(self, IsolateInfo, isolate_info);
947
+
948
+ if (current_platform == NULL) return Qfalse;
949
+
950
+ if (platform::PumpMessageLoop(current_platform.get(), isolate_info->isolate)){
951
+ return Qtrue;
952
+ } else {
953
+ return Qfalse;
954
+ }
955
+ }
956
+
767
957
  static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
768
958
  ContextInfo* context_info;
769
959
  Data_Get_Struct(self, ContextInfo, context_info);
@@ -847,9 +1037,14 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
847
1037
  if (!result.executed) {
848
1038
  VALUE ruby_exception = rb_iv_get(self, "@current_exception");
849
1039
  if (ruby_exception == Qnil) {
850
- bool mem_softlimit_reached = (bool)isolate->GetData(MEM_SOFTLIMIT_REACHED);
1040
+ bool mem_softlimit_reached = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED);
1041
+ bool marshal_stack_maxdepth_reached = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED);
851
1042
  // If we were terminated or have the memory softlimit flag set
852
- if (result.terminated || mem_softlimit_reached) {
1043
+ if (marshal_stack_maxdepth_reached) {
1044
+ ruby_exception = rb_eScriptRuntimeError;
1045
+ std::string msg = std::string("Marshal object depth too deep. Script terminated.");
1046
+ message = rb_enc_str_new(msg.c_str(), msg.length(), rb_enc_find("utf-8"));
1047
+ } else if (result.terminated || mem_softlimit_reached) {
853
1048
  ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
854
1049
  } else {
855
1050
  ruby_exception = rb_eScriptRuntimeError;
@@ -884,6 +1079,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
884
1079
  VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
885
1080
  ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
886
1081
  } else {
1082
+ StackCounter::Reset(isolate);
887
1083
  ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
888
1084
  }
889
1085
 
@@ -941,6 +1137,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
941
1137
  eval_params.result = &eval_result;
942
1138
  eval_params.timeout = 0;
943
1139
  eval_params.max_memory = 0;
1140
+ eval_params.marshal_stackdepth = 0;
944
1141
  VALUE timeout = rb_iv_get(self, "@timeout");
945
1142
  if (timeout != Qnil) {
946
1143
  eval_params.timeout = (useconds_t)NUM2LONG(timeout);
@@ -951,6 +1148,11 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
951
1148
  eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
952
1149
  }
953
1150
 
1151
+ VALUE stack_depth = rb_iv_get(self, "@marshal_stack_depth");
1152
+ if (stack_depth != Qnil) {
1153
+ eval_params.marshal_stackdepth = (size_t)NUM2ULONG(stack_depth);
1154
+ }
1155
+
954
1156
  eval_result.message = NULL;
955
1157
  eval_result.backtrace = NULL;
956
1158
 
@@ -1020,6 +1222,7 @@ gvl_ruby_callback(void* data) {
1020
1222
 
1021
1223
  for (int i = 0; i < length; i++) {
1022
1224
  Local<Value> value = ((*args)[i]).As<Value>();
1225
+ StackCounter::Reset(args->GetIsolate());
1023
1226
  VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
1024
1227
  *context_info->context, value);
1025
1228
  rb_ary_push(ruby_args, tmp);
@@ -1033,8 +1236,10 @@ gvl_ruby_callback(void* data) {
1033
1236
  callback_data.ruby_args = ruby_args;
1034
1237
  callback_data.failed = false;
1035
1238
 
1036
- if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1037
- args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Terminated execution during transition from Ruby to JS"));
1239
+ if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
1240
+ args->GetIsolate()->ThrowException(
1241
+ String::NewFromUtf8Literal(args->GetIsolate(),
1242
+ "Terminated execution during transition from Ruby to JS"));
1038
1243
  args->GetIsolate()->TerminateExecution();
1039
1244
  if (length > 0) {
1040
1245
  rb_ary_clear(ruby_args);
@@ -1043,12 +1248,15 @@ gvl_ruby_callback(void* data) {
1043
1248
  return NULL;
1044
1249
  }
1045
1250
 
1046
- result = rb_rescue2((VALUE(*)(...))&protected_callback, (VALUE)(&callback_data),
1047
- (VALUE(*)(...))&rescue_callback, (VALUE)(&callback_data), rb_eException, (VALUE)0);
1251
+ VALUE callback_data_value = (VALUE)&callback_data;
1252
+
1253
+ // TODO: use rb_vrescue2 in Ruby 2.7 and above
1254
+ result = rb_rescue2(RUBY_METHOD_FUNC(protected_callback), callback_data_value,
1255
+ RUBY_METHOD_FUNC(rescue_callback), callback_data_value, rb_eException, (VALUE)0);
1048
1256
 
1049
1257
  if(callback_data.failed) {
1050
1258
  rb_iv_set(parent, "@current_exception", result);
1051
- args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Ruby exception"));
1259
+ args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
1052
1260
  }
1053
1261
  else {
1054
1262
  HandleScope scope(args->GetIsolate());
@@ -1061,23 +1269,22 @@ gvl_ruby_callback(void* data) {
1061
1269
  rb_gc_force_recycle(ruby_args);
1062
1270
  }
1063
1271
 
1064
- if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1065
- Isolate* isolate = args->GetIsolate();
1066
- isolate->TerminateExecution();
1272
+ if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
1273
+ args->GetIsolate()->TerminateExecution();
1067
1274
  }
1068
1275
 
1069
1276
  return NULL;
1070
1277
  }
1071
1278
 
1072
1279
  static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
1073
- bool has_gvl = (bool)args.GetIsolate()->GetData(IN_GVL);
1280
+ bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
1074
1281
 
1075
1282
  if(has_gvl) {
1076
1283
  gvl_ruby_callback((void*)&args);
1077
1284
  } else {
1078
- args.GetIsolate()->SetData(IN_GVL, (void*)true);
1285
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
1079
1286
  rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
1080
- args.GetIsolate()->SetData(IN_GVL, (void*)false);
1287
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
1081
1288
  }
1082
1289
  }
1083
1290
 
@@ -1118,10 +1325,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1118
1325
  Local<Value> external = External::New(isolate, self_copy);
1119
1326
 
1120
1327
  if (parent_object == Qnil) {
1121
- context->Global()->Set(
1122
- v8_str, FunctionTemplate::New(isolate, ruby_callback, external)
1328
+ Maybe<bool> success = context->Global()->Set(
1329
+ context,
1330
+ v8_str,
1331
+ FunctionTemplate::New(isolate, ruby_callback, external)
1123
1332
  ->GetFunction(context)
1124
1333
  .ToLocalChecked());
1334
+ (void)success;
1125
1335
 
1126
1336
  } else {
1127
1337
  Local<String> eval =
@@ -1132,7 +1342,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1132
1342
 
1133
1343
  MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
1134
1344
  if (parsed_script.IsEmpty()) {
1135
- parse_error = true;
1345
+ parse_error = true;
1136
1346
  } else {
1137
1347
  MaybeLocal<Value> maybe_value =
1138
1348
  parsed_script.ToLocalChecked()->Run(context);
@@ -1141,12 +1351,14 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1141
1351
  if (!maybe_value.IsEmpty()) {
1142
1352
  Local<Value> value = maybe_value.ToLocalChecked();
1143
1353
  if (value->IsObject()) {
1144
- value.As<Object>()->Set(
1145
- v8_str, FunctionTemplate::New(
1146
- isolate, ruby_callback, external)
1354
+ Maybe<bool> success = value.As<Object>()->Set(
1355
+ context,
1356
+ v8_str,
1357
+ FunctionTemplate::New(isolate, ruby_callback, external)
1147
1358
  ->GetFunction(context)
1148
1359
  .ToLocalChecked());
1149
- attach_error = false;
1360
+ (void)success;
1361
+ attach_error = false;
1150
1362
  }
1151
1363
  }
1152
1364
  }
@@ -1176,35 +1388,39 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
1176
1388
  return context_info->isolate_info->mutex;
1177
1389
  }
1178
1390
 
1179
- void free_isolate(IsolateInfo* isolate_info) {
1180
-
1181
- if (isolate_info->isolate) {
1182
- Locker lock(isolate_info->isolate);
1183
- }
1184
-
1185
- if (isolate_info->isolate) {
1186
- if (isolate_info->interrupted) {
1187
- 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");
1391
+ IsolateInfo::~IsolateInfo() {
1392
+ if (isolate) {
1393
+ if (this->interrupted) {
1394
+ fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, "
1395
+ "it can not be disposed and memory will not be "
1396
+ "reclaimed till the Ruby process exits.\n");
1188
1397
  } else {
1189
-
1190
- if (isolate_info->pid != getpid()) {
1191
- fprintf(stderr, "WARNING: V8 isolate was forked, it can not be disposed and memory will not be reclaimed till the Ruby process exits.\n");
1398
+ if (this->pid != getpid() && !single_threaded) {
1399
+ fprintf(stderr, "WARNING: V8 isolate was forked, "
1400
+ "it can not be disposed and "
1401
+ "memory will not be reclaimed "
1402
+ "till the Ruby process exits.\n"
1403
+ "It is VERY likely your process will hang.\n"
1404
+ "If you wish to use v8 in forked environment "
1405
+ "please ensure the platform is initialized with:\n"
1406
+ "MiniRacer::Platform.set_flags! :single_threaded\n"
1407
+ );
1192
1408
  } else {
1193
- isolate_info->isolate->Dispose();
1409
+ isolate->Dispose();
1194
1410
  }
1195
1411
  }
1196
- isolate_info->isolate = NULL;
1412
+ isolate = nullptr;
1197
1413
  }
1198
1414
 
1199
- if (isolate_info->startup_data) {
1200
- delete[] isolate_info->startup_data->data;
1201
- delete isolate_info->startup_data;
1415
+ if (startup_data) {
1416
+ delete[] startup_data->data;
1417
+ delete startup_data;
1202
1418
  }
1203
1419
 
1204
- delete isolate_info->allocator;
1420
+ delete allocator;
1205
1421
  }
1206
1422
 
1207
- static void *free_context_raw(void* arg) {
1423
+ static void free_context_raw(void *arg) {
1208
1424
  ContextInfo* context_info = (ContextInfo*)arg;
1209
1425
  IsolateInfo* isolate_info = context_info->isolate_info;
1210
1426
  Persistent<Context>* context = context_info->context;
@@ -1221,6 +1437,20 @@ static void *free_context_raw(void* arg) {
1221
1437
  }
1222
1438
 
1223
1439
  xfree(context_info);
1440
+ }
1441
+
1442
+ static void *free_context_thr(void* arg) {
1443
+ if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
1444
+ return NULL;
1445
+ }
1446
+ if (ruby_exiting) {
1447
+ return NULL;
1448
+ }
1449
+
1450
+ free_context_raw(arg);
1451
+
1452
+ pthread_rwlock_unlock(&exit_lock);
1453
+
1224
1454
  return NULL;
1225
1455
  }
1226
1456
 
@@ -1234,22 +1464,17 @@ static void free_context(ContextInfo* context_info) {
1234
1464
  context_info_copy->context = context_info->context;
1235
1465
 
1236
1466
  if (isolate_info && isolate_info->refs() > 1) {
1237
- pthread_t free_context_thread;
1238
- if (pthread_create(&free_context_thread, NULL, free_context_raw, (void*)context_info_copy)) {
1239
- fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
1240
- }
1241
-
1467
+ pthread_t free_context_thread;
1468
+ if (pthread_create(&free_context_thread, thread_attr_p,
1469
+ free_context_thr, (void*)context_info_copy)) {
1470
+ fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
1471
+ }
1242
1472
  } else {
1243
1473
  free_context_raw(context_info_copy);
1244
1474
  }
1245
1475
 
1246
- if (context_info->context && isolate_info && isolate_info->isolate) {
1247
- context_info->context = NULL;
1248
- }
1249
-
1250
- if (isolate_info) {
1251
- context_info->isolate_info = NULL;
1252
- }
1476
+ context_info->context = NULL;
1477
+ context_info->isolate_info = NULL;
1253
1478
  }
1254
1479
 
1255
1480
  static void deallocate_isolate(void* data) {
@@ -1264,7 +1489,7 @@ static void mark_isolate(void* data) {
1264
1489
  isolate_info->mark();
1265
1490
  }
1266
1491
 
1267
- void deallocate(void* data) {
1492
+ static void deallocate(void* data) {
1268
1493
  ContextInfo* context_info = (ContextInfo*)data;
1269
1494
 
1270
1495
  free_context(context_info);
@@ -1279,22 +1504,22 @@ static void mark_context(void* data) {
1279
1504
  }
1280
1505
  }
1281
1506
 
1282
- void deallocate_external_function(void * data) {
1507
+ static void deallocate_external_function(void * data) {
1283
1508
  xfree(data);
1284
1509
  }
1285
1510
 
1286
- void deallocate_snapshot(void * data) {
1511
+ static void deallocate_snapshot(void * data) {
1287
1512
  SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
1288
1513
  delete[] snapshot_info->data;
1289
1514
  xfree(snapshot_info);
1290
1515
  }
1291
1516
 
1292
- VALUE allocate_external_function(VALUE klass) {
1517
+ static VALUE allocate_external_function(VALUE klass) {
1293
1518
  VALUE* self = ALLOC(VALUE);
1294
1519
  return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
1295
1520
  }
1296
1521
 
1297
- VALUE allocate(VALUE klass) {
1522
+ static VALUE allocate(VALUE klass) {
1298
1523
  ContextInfo* context_info = ALLOC(ContextInfo);
1299
1524
  context_info->isolate_info = NULL;
1300
1525
  context_info->context = NULL;
@@ -1302,7 +1527,7 @@ VALUE allocate(VALUE klass) {
1302
1527
  return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
1303
1528
  }
1304
1529
 
1305
- VALUE allocate_snapshot(VALUE klass) {
1530
+ static VALUE allocate_snapshot(VALUE klass) {
1306
1531
  SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
1307
1532
  snapshot_info->data = NULL;
1308
1533
  snapshot_info->raw_size = 0;
@@ -1310,7 +1535,7 @@ VALUE allocate_snapshot(VALUE klass) {
1310
1535
  return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
1311
1536
  }
1312
1537
 
1313
- VALUE allocate_isolate(VALUE klass) {
1538
+ static VALUE allocate_isolate(VALUE klass) {
1314
1539
  IsolateInfo* isolate_info = new IsolateInfo();
1315
1540
 
1316
1541
  return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
@@ -1407,6 +1632,8 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1407
1632
  FileOutputStream stream(fp);
1408
1633
  snap->Serialize(&stream, HeapSnapshot::kJSON);
1409
1634
 
1635
+ fflush(fp);
1636
+
1410
1637
  const_cast<HeapSnapshot*>(snap)->Delete();
1411
1638
 
1412
1639
  return Qtrue;
@@ -1421,7 +1648,7 @@ rb_context_stop(VALUE self) {
1421
1648
  Isolate* isolate = context_info->isolate_info->isolate;
1422
1649
 
1423
1650
  // flag for termination
1424
- isolate->SetData(DO_TERMINATE, (void*)true);
1651
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
1425
1652
 
1426
1653
  isolate->TerminateExecution();
1427
1654
  rb_funcall(self, rb_intern("stop_attached"), 0);
@@ -1447,12 +1674,24 @@ nogvl_context_call(void *args) {
1447
1674
  if (!call) {
1448
1675
  return NULL;
1449
1676
  }
1450
- Isolate* isolate = call->context_info->isolate_info->isolate;
1677
+ IsolateInfo *isolate_info = call->context_info->isolate_info;
1678
+ Isolate* isolate = isolate_info->isolate;
1679
+
1680
+ IsolateData::Set(isolate, IsolateData::IN_GVL, false);
1681
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
1451
1682
 
1452
- // in gvl flag
1453
- isolate->SetData(IN_GVL, (void*)false);
1454
- // terminate ASAP
1455
- isolate->SetData(DO_TERMINATE, (void*)false);
1683
+ if (call->max_memory > 0) {
1684
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, call->max_memory);
1685
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false);
1686
+ if (!isolate_info->added_gc_cb) {
1687
+ isolate->AddGCEpilogueCallback(gc_callback);
1688
+ isolate_info->added_gc_cb = true;
1689
+ }
1690
+ }
1691
+
1692
+ if (call->marshal_stackdepth > 0) {
1693
+ StackCounter::SetMax(isolate, call->marshal_stackdepth);
1694
+ }
1456
1695
 
1457
1696
  Isolate::Scope isolate_scope(isolate);
1458
1697
  EscapableHandleScope handle_scope(isolate);
@@ -1469,7 +1708,7 @@ nogvl_context_call(void *args) {
1469
1708
  MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
1470
1709
  prepare_result(res, trycatch, isolate, context, eval_res);
1471
1710
 
1472
- isolate->SetData(IN_GVL, (void*)true);
1711
+ IsolateData::Set(isolate, IsolateData::IN_GVL, true);
1473
1712
 
1474
1713
  return NULL;
1475
1714
  }
@@ -1511,6 +1750,20 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1511
1750
  call_argv = argv + 1;
1512
1751
  }
1513
1752
 
1753
+ call.max_memory = 0;
1754
+ VALUE mem_softlimit = rb_iv_get(self, "@max_memory");
1755
+ if (mem_softlimit != Qnil) {
1756
+ unsigned long sl_int = NUM2ULONG(mem_softlimit);
1757
+ call.max_memory = (size_t)sl_int;
1758
+ }
1759
+
1760
+ call.marshal_stackdepth = 0;
1761
+ VALUE marshal_stackdepth = rb_iv_get(self, "@marshal_stack_depth");
1762
+ if (marshal_stackdepth != Qnil) {
1763
+ unsigned long sl_int = NUM2ULONG(marshal_stackdepth);
1764
+ call.marshal_stackdepth = (size_t)sl_int;
1765
+ }
1766
+
1514
1767
  bool missingFunction = false;
1515
1768
  {
1516
1769
  Locker lock(isolate);
@@ -1522,8 +1775,11 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1522
1775
 
1523
1776
  // examples of such usage can be found in
1524
1777
  // https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
1525
- Local<String> fname = String::NewFromUtf8(isolate, call.function_name);
1526
- MaybeLocal<v8::Value> val = context->Global()->Get(fname);
1778
+ MaybeLocal<String> fname = String::NewFromUtf8(isolate, call.function_name);
1779
+ MaybeLocal<v8::Value> val;
1780
+ if (!fname.IsEmpty()) {
1781
+ val = context->Global()->Get(context, fname.ToLocalChecked());
1782
+ }
1527
1783
 
1528
1784
  if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
1529
1785
  missingFunction = true;
@@ -1568,9 +1824,20 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
1568
1824
  return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
1569
1825
  }
1570
1826
 
1827
+ static void set_ruby_exiting(VALUE value) {
1828
+ (void)value;
1829
+
1830
+ int res = pthread_rwlock_wrlock(&exit_lock);
1831
+
1832
+ ruby_exiting = true;
1833
+ if (res == 0) {
1834
+ pthread_rwlock_unlock(&exit_lock);
1835
+ }
1836
+ }
1837
+
1571
1838
  extern "C" {
1572
1839
 
1573
- void Init_mini_racer_extension ( void )
1840
+ __attribute__((visibility("default"))) void Init_mini_racer_extension ( void )
1574
1841
  {
1575
1842
  VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
1576
1843
  rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
@@ -1618,8 +1885,23 @@ extern "C" {
1618
1885
  rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
1619
1886
 
1620
1887
  rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
1888
+ rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
1889
+ rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
1621
1890
  rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
1622
1891
 
1623
1892
  rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
1893
+
1894
+ rb_set_end_proc(set_ruby_exiting, Qnil);
1895
+
1896
+ static pthread_attr_t attr;
1897
+ if (pthread_attr_init(&attr) == 0) {
1898
+ if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0) {
1899
+ thread_attr_p = &attr;
1900
+ }
1901
+ }
1902
+ auto on_fork_for_child = []() {
1903
+ exit_lock = PTHREAD_RWLOCK_INITIALIZER;
1904
+ };
1905
+ pthread_atfork(nullptr, nullptr, on_fork_for_child);
1624
1906
  }
1625
1907
  }