mini_racer 0.2.4 → 0.4.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.
- checksums.yaml +4 -4
- data/.dockerignore +12 -0
- data/.github/workflows/ci.yml +80 -0
- data/.gitignore +1 -0
- data/.travis.yml +17 -9
- data/CHANGELOG +98 -0
- data/Dockerfile +21 -0
- data/LICENSE.txt +1 -1
- data/README.md +44 -4
- data/Rakefile +42 -0
- data/ext/mini_racer_extension/extconf.rb +16 -3
- data/ext/mini_racer_extension/mini_racer_extension.cc +611 -318
- data/ext/mini_racer_loader/extconf.rb +8 -0
- data/ext/mini_racer_loader/mini_racer_loader.c +123 -0
- data/lib/mini_racer/version.rb +4 -1
- data/lib/mini_racer.rb +113 -16
- data/mini_racer.gemspec +13 -6
- metadata +43 -20
@@ -1,7 +1,9 @@
|
|
1
1
|
#include <stdio.h>
|
2
2
|
#include <ruby.h>
|
3
3
|
#include <ruby/thread.h>
|
4
|
+
#include <ruby/io.h>
|
4
5
|
#include <v8.h>
|
6
|
+
#include <v8-profiler.h>
|
5
7
|
#include <libplatform/libplatform.h>
|
6
8
|
#include <ruby/encoding.h>
|
7
9
|
#include <pthread.h>
|
@@ -23,6 +25,7 @@ public:
|
|
23
25
|
ArrayBuffer::Allocator* allocator;
|
24
26
|
StartupData* startup_data;
|
25
27
|
bool interrupted;
|
28
|
+
bool added_gc_cb;
|
26
29
|
pid_t pid;
|
27
30
|
VALUE mutex;
|
28
31
|
|
@@ -40,15 +43,12 @@ public:
|
|
40
43
|
|
41
44
|
|
42
45
|
IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
|
43
|
-
interrupted(false), pid(getpid()), refs_count(0) {
|
46
|
+
interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
|
44
47
|
VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
|
45
48
|
mutex = rb_class_new_instance(0, nullptr, cMutex);
|
46
49
|
}
|
47
50
|
|
48
|
-
~IsolateInfo()
|
49
|
-
void free_isolate(IsolateInfo*);
|
50
|
-
free_isolate(this);
|
51
|
-
}
|
51
|
+
~IsolateInfo();
|
52
52
|
|
53
53
|
void init(SnapshotInfo* snapshot_info = nullptr);
|
54
54
|
|
@@ -71,7 +71,7 @@ public:
|
|
71
71
|
}
|
72
72
|
|
73
73
|
int refs() {
|
74
|
-
|
74
|
+
return refs_count;
|
75
75
|
}
|
76
76
|
|
77
77
|
static void* operator new(size_t size) {
|
@@ -125,6 +125,7 @@ typedef struct {
|
|
125
125
|
Local<Function> fun;
|
126
126
|
Local<Value> *argv;
|
127
127
|
EvalResult result;
|
128
|
+
size_t max_memory;
|
128
129
|
} FunctionCall;
|
129
130
|
|
130
131
|
enum IsolateFlags {
|
@@ -150,9 +151,14 @@ static VALUE rb_mJSON;
|
|
150
151
|
static VALUE rb_cFailedV8Conversion;
|
151
152
|
static VALUE rb_cDateTime = Qnil;
|
152
153
|
|
153
|
-
static Platform
|
154
|
+
static std::unique_ptr<Platform> current_platform = NULL;
|
154
155
|
static std::mutex platform_lock;
|
155
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
|
+
|
156
162
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
157
163
|
bool platform_already_initialized = false;
|
158
164
|
|
@@ -164,6 +170,9 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
164
170
|
platform_lock.lock();
|
165
171
|
|
166
172
|
if (current_platform == NULL) {
|
173
|
+
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
174
|
+
single_threaded = true;
|
175
|
+
}
|
167
176
|
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
|
168
177
|
} else {
|
169
178
|
platform_already_initialized = true;
|
@@ -187,8 +196,8 @@ static void init_v8() {
|
|
187
196
|
|
188
197
|
if (current_platform == NULL) {
|
189
198
|
V8::InitializeICU();
|
190
|
-
current_platform = platform::
|
191
|
-
V8::InitializePlatform(current_platform);
|
199
|
+
current_platform = platform::NewDefaultPlatform();
|
200
|
+
V8::InitializePlatform(current_platform.get());
|
192
201
|
V8::Initialize();
|
193
202
|
}
|
194
203
|
|
@@ -200,9 +209,9 @@ static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
|
|
200
209
|
|
201
210
|
size_t softlimit = *(size_t*) isolate->GetData(MEM_SOFTLIMIT_VALUE);
|
202
211
|
|
203
|
-
HeapStatistics
|
204
|
-
isolate->GetHeapStatistics(stats);
|
205
|
-
size_t used = stats
|
212
|
+
HeapStatistics stats;
|
213
|
+
isolate->GetHeapStatistics(&stats);
|
214
|
+
size_t used = stats.used_heap_size();
|
206
215
|
|
207
216
|
if(used > softlimit) {
|
208
217
|
isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)true);
|
@@ -231,15 +240,17 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
231
240
|
if ((local_value->IsObject() || local_value->IsArray()) &&
|
232
241
|
!local_value->IsDate() && !local_value->IsFunction()) {
|
233
242
|
Local<Object> JSON = context->Global()->Get(
|
234
|
-
String::
|
243
|
+
context, String::NewFromUtf8Literal(isolate, "JSON"))
|
244
|
+
.ToLocalChecked().As<Object>();
|
235
245
|
|
236
|
-
Local<Function> stringify = JSON->Get(
|
237
|
-
|
246
|
+
Local<Function> stringify = JSON->Get(
|
247
|
+
context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
|
248
|
+
.ToLocalChecked().As<Function>();
|
238
249
|
|
239
|
-
Local<Object> object = local_value->ToObject();
|
250
|
+
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
240
251
|
const unsigned argc = 1;
|
241
252
|
Local<Value> argv[argc] = { object };
|
242
|
-
MaybeLocal<Value> json = stringify->Call(JSON, argc, argv);
|
253
|
+
MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
|
243
254
|
|
244
255
|
if (json.IsEmpty()) {
|
245
256
|
evalRes.executed = false;
|
@@ -263,11 +274,21 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
263
274
|
evalRes.message = new Persistent<Value>();
|
264
275
|
Local<Message> message = trycatch.Message();
|
265
276
|
char buf[1000];
|
266
|
-
int len;
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
277
|
+
int len, line, column;
|
278
|
+
|
279
|
+
if (!message->GetLineNumber(context).To(&line)) {
|
280
|
+
line = 0;
|
281
|
+
}
|
282
|
+
|
283
|
+
if (!message->GetStartColumn(context).To(&column)) {
|
284
|
+
column = 0;
|
285
|
+
}
|
286
|
+
|
287
|
+
len = snprintf(buf, sizeof(buf), "%s at %s:%i:%i", *String::Utf8Value(isolate, message->Get()),
|
288
|
+
*String::Utf8Value(isolate, message->GetScriptResourceName()->ToString(context).ToLocalChecked()),
|
289
|
+
line,
|
290
|
+
column);
|
291
|
+
|
271
292
|
if ((size_t) len >= sizeof(buf)) {
|
272
293
|
len = sizeof(buf) - 1;
|
273
294
|
buf[len] = '\0';
|
@@ -278,12 +299,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
278
299
|
} else if(trycatch.HasTerminated()) {
|
279
300
|
evalRes.terminated = true;
|
280
301
|
evalRes.message = new Persistent<Value>();
|
281
|
-
Local<String> tmp = String::
|
302
|
+
Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
|
282
303
|
evalRes.message->Reset(isolate, tmp);
|
283
304
|
}
|
284
|
-
if (!trycatch.StackTrace().IsEmpty()) {
|
305
|
+
if (!trycatch.StackTrace(context).IsEmpty()) {
|
285
306
|
evalRes.backtrace = new Persistent<Value>();
|
286
|
-
evalRes.backtrace->Reset(isolate,
|
307
|
+
evalRes.backtrace->Reset(isolate,
|
308
|
+
trycatch.StackTrace(context).ToLocalChecked()->ToString(context).ToLocalChecked());
|
287
309
|
}
|
288
310
|
}
|
289
311
|
}
|
@@ -294,7 +316,8 @@ nogvl_context_eval(void* arg) {
|
|
294
316
|
|
295
317
|
EvalParams* eval_params = (EvalParams*)arg;
|
296
318
|
EvalResult* result = eval_params->result;
|
297
|
-
|
319
|
+
IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
|
320
|
+
Isolate* isolate = isolate_info->isolate;
|
298
321
|
|
299
322
|
Isolate::Scope isolate_scope(isolate);
|
300
323
|
HandleScope handle_scope(isolate);
|
@@ -315,13 +338,13 @@ nogvl_context_eval(void* arg) {
|
|
315
338
|
MaybeLocal<Script> parsed_script;
|
316
339
|
|
317
340
|
if (eval_params->filename) {
|
318
|
-
|
341
|
+
origin = new v8::ScriptOrigin(*eval_params->filename);
|
319
342
|
}
|
320
343
|
|
321
344
|
parsed_script = Script::Compile(context, *eval_params->eval, origin);
|
322
345
|
|
323
346
|
if (origin) {
|
324
|
-
|
347
|
+
delete origin;
|
325
348
|
}
|
326
349
|
|
327
350
|
result->parsed = !parsed_script.IsEmpty();
|
@@ -332,13 +355,16 @@ nogvl_context_eval(void* arg) {
|
|
332
355
|
|
333
356
|
MaybeLocal<Value> maybe_value;
|
334
357
|
if (!result->parsed) {
|
335
|
-
|
336
|
-
|
358
|
+
result->message = new Persistent<Value>();
|
359
|
+
result->message->Reset(isolate, trycatch.Exception());
|
337
360
|
} else {
|
338
361
|
// parsing successful
|
339
362
|
if (eval_params->max_memory > 0) {
|
340
363
|
isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
|
364
|
+
if (!isolate_info->added_gc_cb) {
|
341
365
|
isolate->AddGCEpilogueCallback(gc_callback);
|
366
|
+
isolate_info->added_gc_cb = true;
|
367
|
+
}
|
342
368
|
}
|
343
369
|
|
344
370
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -351,6 +377,12 @@ nogvl_context_eval(void* arg) {
|
|
351
377
|
return NULL;
|
352
378
|
}
|
353
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
|
+
|
354
386
|
// assumes isolate locking is in place
|
355
387
|
static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
356
388
|
Local<Value> value) {
|
@@ -359,41 +391,44 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
359
391
|
HandleScope scope(isolate);
|
360
392
|
|
361
393
|
if (value->IsNull() || value->IsUndefined()){
|
362
|
-
|
394
|
+
return Qnil;
|
363
395
|
}
|
364
396
|
|
365
397
|
if (value->IsInt32()) {
|
366
|
-
|
398
|
+
return INT2FIX(value->Int32Value(context).ToChecked());
|
367
399
|
}
|
368
400
|
|
369
401
|
if (value->IsNumber()) {
|
370
|
-
|
402
|
+
return rb_float_new(value->NumberValue(context).ToChecked());
|
371
403
|
}
|
372
404
|
|
373
405
|
if (value->IsTrue()) {
|
374
|
-
|
406
|
+
return Qtrue;
|
375
407
|
}
|
376
408
|
|
377
409
|
if (value->IsFalse()) {
|
378
|
-
|
410
|
+
return Qfalse;
|
379
411
|
}
|
380
412
|
|
381
413
|
if (value->IsArray()) {
|
382
414
|
VALUE rb_array = rb_ary_new();
|
383
415
|
Local<Array> arr = Local<Array>::Cast(value);
|
384
416
|
for(uint32_t i=0; i < arr->Length(); i++) {
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
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());
|
422
|
+
if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
|
423
|
+
return rb_elem;
|
424
|
+
}
|
390
425
|
rb_ary_push(rb_array, rb_elem);
|
391
426
|
}
|
392
427
|
return rb_array;
|
393
428
|
}
|
394
429
|
|
395
430
|
if (value->IsFunction()){
|
396
|
-
|
431
|
+
return rb_funcall(rb_cJavaScriptFunction, rb_intern("new"), 0);
|
397
432
|
}
|
398
433
|
|
399
434
|
if (value->IsDate()){
|
@@ -405,34 +440,55 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
405
440
|
}
|
406
441
|
|
407
442
|
if (value->IsObject()) {
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
443
|
+
VALUE rb_hash = rb_hash_new();
|
444
|
+
TryCatch trycatch(isolate);
|
445
|
+
|
446
|
+
Local<Object> object = value->ToObject(context).ToLocalChecked();
|
447
|
+
auto maybe_props = object->GetOwnPropertyNames(context);
|
448
|
+
if (!maybe_props.IsEmpty()) {
|
449
|
+
Local<Array> props = maybe_props.ToLocalChecked();
|
450
|
+
for(uint32_t i=0; i < props->Length(); i++) {
|
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());
|
456
|
+
|
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();
|
461
|
+
}
|
462
|
+
|
463
|
+
VALUE rb_value = convert_v8_to_ruby(
|
464
|
+
isolate, context, prop_value.ToLocalChecked());
|
465
|
+
rb_hash_aset(rb_hash, rb_key, rb_value);
|
466
|
+
}
|
467
|
+
}
|
468
|
+
return rb_hash;
|
432
469
|
}
|
433
470
|
|
434
|
-
|
435
|
-
|
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
|
+
}
|
436
492
|
}
|
437
493
|
|
438
494
|
static VALUE convert_v8_to_ruby(Isolate* isolate,
|
@@ -453,7 +509,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate,
|
|
453
509
|
Local<Value>::New(isolate, value));
|
454
510
|
}
|
455
511
|
|
456
|
-
static Local<Value> convert_ruby_to_v8(Isolate* isolate, VALUE value) {
|
512
|
+
static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context, VALUE value) {
|
457
513
|
EscapableHandleScope scope(isolate);
|
458
514
|
|
459
515
|
Local<Array> array;
|
@@ -487,7 +543,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, VALUE value) {
|
|
487
543
|
length = RARRAY_LEN(value);
|
488
544
|
array = Array::New(isolate, (int)length);
|
489
545
|
for(i=0; i<length; i++) {
|
490
|
-
|
546
|
+
array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
491
547
|
}
|
492
548
|
return scope.Escape(array);
|
493
549
|
case T_HASH:
|
@@ -496,8 +552,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, VALUE value) {
|
|
496
552
|
length = RARRAY_LEN(hash_as_array);
|
497
553
|
for(i=0; i<length; i++) {
|
498
554
|
pair = rb_ary_entry(hash_as_array, i);
|
499
|
-
|
500
|
-
|
555
|
+
object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
556
|
+
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
501
557
|
}
|
502
558
|
return scope.Escape(object);
|
503
559
|
case T_SYMBOL:
|
@@ -512,7 +568,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, VALUE value) {
|
|
512
568
|
value = rb_funcall(value, rb_intern("to_time"), 0);
|
513
569
|
}
|
514
570
|
value = rb_funcall(value, rb_intern("to_f"), 0);
|
515
|
-
return scope.Escape(Date::New(
|
571
|
+
return scope.Escape(Date::New(context, NUM2DBL(value) * 1000).ToLocalChecked());
|
516
572
|
}
|
517
573
|
case T_OBJECT:
|
518
574
|
case T_CLASS:
|
@@ -526,16 +582,90 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, VALUE value) {
|
|
526
582
|
case T_UNDEF:
|
527
583
|
case T_NODE:
|
528
584
|
default:
|
529
|
-
return scope.Escape(String::
|
585
|
+
return scope.Escape(String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
|
586
|
+
}
|
530
587
|
}
|
531
|
-
|
532
|
-
}
|
533
588
|
|
534
589
|
static void unblock_eval(void *ptr) {
|
535
590
|
EvalParams* eval = (EvalParams*)ptr;
|
536
591
|
eval->context_info->isolate_info->interrupted = true;
|
537
592
|
}
|
538
593
|
|
594
|
+
/*
|
595
|
+
* The implementations of the run_extra_code(), create_snapshot_data_blob() and
|
596
|
+
* warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
|
597
|
+
*/
|
598
|
+
static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
599
|
+
const char *utf8_source, const char *name) {
|
600
|
+
Context::Scope context_scope(context);
|
601
|
+
TryCatch try_catch(isolate);
|
602
|
+
Local<String> source_string;
|
603
|
+
if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
|
604
|
+
return false;
|
605
|
+
}
|
606
|
+
Local<String> resource_name =
|
607
|
+
String::NewFromUtf8(isolate, name).ToLocalChecked();
|
608
|
+
ScriptOrigin origin(resource_name);
|
609
|
+
ScriptCompiler::Source source(source_string, origin);
|
610
|
+
Local<Script> script;
|
611
|
+
if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
|
612
|
+
return false;
|
613
|
+
if (script->Run(context).IsEmpty()) return false;
|
614
|
+
return true;
|
615
|
+
}
|
616
|
+
|
617
|
+
static StartupData
|
618
|
+
create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
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);
|
623
|
+
{
|
624
|
+
HandleScope scope(isolate);
|
625
|
+
Local<v8::Context> context = v8::Context::New(isolate);
|
626
|
+
if (embedded_source != nullptr &&
|
627
|
+
!run_extra_code(isolate, context, embedded_source, "<embedded>")) {
|
628
|
+
return {};
|
629
|
+
}
|
630
|
+
snapshot_creator.SetDefaultContext(context);
|
631
|
+
}
|
632
|
+
return snapshot_creator.CreateBlob(
|
633
|
+
SnapshotCreator::FunctionCodeHandling::kClear);
|
634
|
+
}
|
635
|
+
|
636
|
+
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
637
|
+
const char *warmup_source) {
|
638
|
+
// Use following steps to create a warmed up snapshot blob from a cold one:
|
639
|
+
// - Create a new isolate from the cold snapshot.
|
640
|
+
// - Create a new context to run the warmup script. This will trigger
|
641
|
+
// compilation of executed functions.
|
642
|
+
// - Create a new context. This context will be unpolluted.
|
643
|
+
// - Serialize the isolate and the second context into a new snapshot blob.
|
644
|
+
StartupData result = {nullptr, 0};
|
645
|
+
|
646
|
+
if (cold_snapshot_blob.raw_size > 0 && cold_snapshot_blob.data != nullptr &&
|
647
|
+
warmup_source != NULL) {
|
648
|
+
SnapshotCreator snapshot_creator(nullptr, &cold_snapshot_blob);
|
649
|
+
Isolate *isolate = snapshot_creator.GetIsolate();
|
650
|
+
{
|
651
|
+
HandleScope scope(isolate);
|
652
|
+
Local<Context> context = Context::New(isolate);
|
653
|
+
if (!run_extra_code(isolate, context, warmup_source, "<warm-up>")) {
|
654
|
+
return result;
|
655
|
+
}
|
656
|
+
}
|
657
|
+
{
|
658
|
+
HandleScope handle_scope(isolate);
|
659
|
+
isolate->ContextDisposedNotification(false);
|
660
|
+
Local<Context> context = Context::New(isolate);
|
661
|
+
snapshot_creator.SetDefaultContext(context);
|
662
|
+
}
|
663
|
+
result = snapshot_creator.CreateBlob(
|
664
|
+
SnapshotCreator::FunctionCodeHandling::kKeep);
|
665
|
+
}
|
666
|
+
return result;
|
667
|
+
}
|
668
|
+
|
539
669
|
static VALUE rb_snapshot_size(VALUE self, VALUE str) {
|
540
670
|
SnapshotInfo* snapshot_info;
|
541
671
|
Data_Get_Struct(self, SnapshotInfo, snapshot_info);
|
@@ -554,7 +684,7 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|
554
684
|
|
555
685
|
init_v8();
|
556
686
|
|
557
|
-
StartupData startup_data =
|
687
|
+
StartupData startup_data = create_snapshot_data_blob(RSTRING_PTR(str));
|
558
688
|
|
559
689
|
if (startup_data.data == NULL && startup_data.raw_size == 0) {
|
560
690
|
rb_raise(rb_eSnapshotError, "Could not create snapshot, most likely the source is incorrect");
|
@@ -585,7 +715,7 @@ static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
|
|
585
715
|
init_v8();
|
586
716
|
|
587
717
|
StartupData cold_startup_data = {snapshot_info->data, snapshot_info->raw_size};
|
588
|
-
StartupData warm_startup_data =
|
718
|
+
StartupData warm_startup_data = warm_up_snapshot_data_blob(cold_startup_data, RSTRING_PTR(str));
|
589
719
|
|
590
720
|
if (warm_startup_data.data == NULL && warm_startup_data.raw_size == 0) {
|
591
721
|
rb_raise(rb_eSnapshotError, "Could not warm up snapshot, most likely the source is incorrect");
|
@@ -648,6 +778,29 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
|
648
778
|
return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
|
649
779
|
}
|
650
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
|
+
|
651
804
|
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
652
805
|
ContextInfo* context_info;
|
653
806
|
Data_Get_Struct(self, ContextInfo, context_info);
|
@@ -675,13 +828,13 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
|
675
828
|
// the ruby lock is needed if this isn't a new isolate
|
676
829
|
IsolateInfo::Lock ruby_lock(isolate_info->mutex);
|
677
830
|
Locker lock(isolate_info->isolate);
|
678
|
-
|
679
|
-
|
831
|
+
Isolate::Scope isolate_scope(isolate_info->isolate);
|
832
|
+
HandleScope handle_scope(isolate_info->isolate);
|
680
833
|
|
681
|
-
|
834
|
+
Local<Context> context = Context::New(isolate_info->isolate);
|
682
835
|
|
683
|
-
|
684
|
-
|
836
|
+
context_info->context = new Persistent<Context>();
|
837
|
+
context_info->context->Reset(isolate_info->isolate, context);
|
685
838
|
}
|
686
839
|
|
687
840
|
if (Qnil == rb_cDateTime && rb_funcall(rb_cObject, rb_intern("const_defined?"), 1, rb_str_new2("DateTime")) == Qtrue)
|
@@ -764,8 +917,8 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
764
917
|
Local<Value> tmp = Local<Value>::New(isolate, *result.value);
|
765
918
|
|
766
919
|
if (result.json) {
|
767
|
-
Local<String> rstr = tmp->ToString();
|
768
|
-
VALUE json_string = rb_enc_str_new(*String::Utf8Value(rstr), rstr->Utf8Length(), rb_enc_find("utf-8"));
|
920
|
+
Local<String> rstr = tmp->ToString(p_ctx->Get(isolate)).ToLocalChecked();
|
921
|
+
VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
769
922
|
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
770
923
|
} else {
|
771
924
|
ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
|
@@ -803,42 +956,42 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
803
956
|
}
|
804
957
|
|
805
958
|
{
|
806
|
-
|
807
|
-
|
808
|
-
|
959
|
+
Locker lock(isolate);
|
960
|
+
Isolate::Scope isolate_scope(isolate);
|
961
|
+
HandleScope handle_scope(isolate);
|
809
962
|
|
810
|
-
|
811
|
-
|
963
|
+
Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(str),
|
964
|
+
NewStringType::kNormal, (int)RSTRING_LEN(str)).ToLocalChecked();
|
812
965
|
|
813
|
-
|
966
|
+
Local<String> local_filename;
|
814
967
|
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
968
|
+
if (filename != Qnil) {
|
969
|
+
local_filename = String::NewFromUtf8(isolate, RSTRING_PTR(filename),
|
970
|
+
NewStringType::kNormal, (int)RSTRING_LEN(filename)).ToLocalChecked();
|
971
|
+
eval_params.filename = &local_filename;
|
972
|
+
} else {
|
973
|
+
eval_params.filename = NULL;
|
974
|
+
}
|
822
975
|
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
976
|
+
eval_params.context_info = context_info;
|
977
|
+
eval_params.eval = &eval;
|
978
|
+
eval_params.result = &eval_result;
|
979
|
+
eval_params.timeout = 0;
|
980
|
+
eval_params.max_memory = 0;
|
981
|
+
VALUE timeout = rb_iv_get(self, "@timeout");
|
982
|
+
if (timeout != Qnil) {
|
983
|
+
eval_params.timeout = (useconds_t)NUM2LONG(timeout);
|
984
|
+
}
|
832
985
|
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
986
|
+
VALUE mem_softlimit = rb_iv_get(self, "@max_memory");
|
987
|
+
if (mem_softlimit != Qnil) {
|
988
|
+
eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
|
989
|
+
}
|
837
990
|
|
838
|
-
|
839
|
-
|
991
|
+
eval_result.message = NULL;
|
992
|
+
eval_result.backtrace = NULL;
|
840
993
|
|
841
|
-
|
994
|
+
rb_thread_call_without_gvl(nogvl_context_eval, &eval_params, unblock_eval, &eval_params);
|
842
995
|
}
|
843
996
|
|
844
997
|
return convert_result_to_ruby(self, eval_result);
|
@@ -851,17 +1004,16 @@ typedef struct {
|
|
851
1004
|
bool failed;
|
852
1005
|
} protected_callback_data;
|
853
1006
|
|
854
|
-
static
|
855
|
-
VALUE protected_callback(VALUE rdata) {
|
1007
|
+
static VALUE protected_callback(VALUE rdata) {
|
856
1008
|
protected_callback_data* data = (protected_callback_data*)rdata;
|
857
1009
|
VALUE result;
|
858
1010
|
|
859
1011
|
if (data->length > 0) {
|
860
|
-
|
861
|
-
|
862
|
-
|
1012
|
+
result = rb_funcall2(data->callback, rb_intern("call"), data->length,
|
1013
|
+
RARRAY_PTR(data->ruby_args));
|
1014
|
+
RB_GC_GUARD(data->ruby_args);
|
863
1015
|
} else {
|
864
|
-
|
1016
|
+
result = rb_funcall(data->callback, rb_intern("call"), 0);
|
865
1017
|
}
|
866
1018
|
return result;
|
867
1019
|
}
|
@@ -883,6 +1035,8 @@ gvl_ruby_callback(void* data) {
|
|
883
1035
|
VALUE result;
|
884
1036
|
VALUE self;
|
885
1037
|
VALUE parent;
|
1038
|
+
ContextInfo* context_info;
|
1039
|
+
|
886
1040
|
{
|
887
1041
|
HandleScope scope(args->GetIsolate());
|
888
1042
|
Local<External> external = Local<External>::Cast(args->Data());
|
@@ -895,19 +1049,18 @@ gvl_ruby_callback(void* data) {
|
|
895
1049
|
return NULL;
|
896
1050
|
}
|
897
1051
|
|
898
|
-
ContextInfo* context_info;
|
899
1052
|
Data_Get_Struct(parent, ContextInfo, context_info);
|
900
1053
|
|
901
|
-
|
902
|
-
|
903
|
-
|
1054
|
+
if (length > 0) {
|
1055
|
+
ruby_args = rb_ary_tmp_new(length);
|
1056
|
+
}
|
904
1057
|
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
1058
|
+
for (int i = 0; i < length; i++) {
|
1059
|
+
Local<Value> value = ((*args)[i]).As<Value>();
|
1060
|
+
VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
|
1061
|
+
*context_info->context, value);
|
1062
|
+
rb_ary_push(ruby_args, tmp);
|
1063
|
+
}
|
911
1064
|
}
|
912
1065
|
|
913
1066
|
// may raise exception stay clear of handle scope
|
@@ -918,31 +1071,33 @@ gvl_ruby_callback(void* data) {
|
|
918
1071
|
callback_data.failed = false;
|
919
1072
|
|
920
1073
|
if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
1074
|
+
args->GetIsolate()->ThrowException(
|
1075
|
+
String::NewFromUtf8Literal(args->GetIsolate(),
|
1076
|
+
"Terminated execution during transition from Ruby to JS"));
|
1077
|
+
args->GetIsolate()->TerminateExecution();
|
1078
|
+
if (length > 0) {
|
1079
|
+
rb_ary_clear(ruby_args);
|
1080
|
+
rb_gc_force_recycle(ruby_args);
|
1081
|
+
}
|
1082
|
+
return NULL;
|
928
1083
|
}
|
929
1084
|
|
930
1085
|
result = rb_rescue2((VALUE(*)(...))&protected_callback, (VALUE)(&callback_data),
|
931
|
-
|
1086
|
+
(VALUE(*)(...))&rescue_callback, (VALUE)(&callback_data), rb_eException, (VALUE)0);
|
932
1087
|
|
933
1088
|
if(callback_data.failed) {
|
934
|
-
|
935
|
-
|
1089
|
+
rb_iv_set(parent, "@current_exception", result);
|
1090
|
+
args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
|
936
1091
|
}
|
937
1092
|
else {
|
938
|
-
|
939
|
-
|
940
|
-
|
1093
|
+
HandleScope scope(args->GetIsolate());
|
1094
|
+
Handle<Value> v8_result = convert_ruby_to_v8(args->GetIsolate(), context_info->context->Get(args->GetIsolate()), result);
|
1095
|
+
args->GetReturnValue().Set(v8_result);
|
941
1096
|
}
|
942
1097
|
|
943
1098
|
if (length > 0) {
|
944
|
-
|
945
|
-
|
1099
|
+
rb_ary_clear(ruby_args);
|
1100
|
+
rb_gc_force_recycle(ruby_args);
|
946
1101
|
}
|
947
1102
|
|
948
1103
|
if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
|
@@ -957,11 +1112,11 @@ static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
|
|
957
1112
|
bool has_gvl = (bool)args.GetIsolate()->GetData(IN_GVL);
|
958
1113
|
|
959
1114
|
if(has_gvl) {
|
960
|
-
|
1115
|
+
gvl_ruby_callback((void*)&args);
|
961
1116
|
} else {
|
962
|
-
|
963
|
-
|
964
|
-
|
1117
|
+
args.GetIsolate()->SetData(IN_GVL, (void*)true);
|
1118
|
+
rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
|
1119
|
+
args.GetIsolate()->SetData(IN_GVL, (void*)false);
|
965
1120
|
}
|
966
1121
|
}
|
967
1122
|
|
@@ -982,55 +1137,71 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
982
1137
|
Isolate* isolate = context_info->isolate_info->isolate;
|
983
1138
|
|
984
1139
|
{
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1140
|
+
Locker lock(isolate);
|
1141
|
+
Isolate::Scope isolate_scope(isolate);
|
1142
|
+
HandleScope handle_scope(isolate);
|
1143
|
+
|
1144
|
+
Local<Context> context = context_info->context->Get(isolate);
|
1145
|
+
Context::Scope context_scope(context);
|
1146
|
+
|
1147
|
+
Local<String> v8_str =
|
1148
|
+
String::NewFromUtf8(isolate, RSTRING_PTR(name),
|
1149
|
+
NewStringType::kNormal, (int)RSTRING_LEN(name))
|
1150
|
+
.ToLocalChecked();
|
1151
|
+
|
1152
|
+
// copy self so we can access from v8 external
|
1153
|
+
VALUE* self_copy;
|
1154
|
+
Data_Get_Struct(self, VALUE, self_copy);
|
1155
|
+
*self_copy = self;
|
1156
|
+
|
1157
|
+
Local<Value> external = External::New(isolate, self_copy);
|
1158
|
+
|
1159
|
+
if (parent_object == Qnil) {
|
1160
|
+
context->Global()->Set(
|
1161
|
+
context,
|
1162
|
+
v8_str,
|
1163
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1164
|
+
->GetFunction(context)
|
1165
|
+
.ToLocalChecked());
|
1166
|
+
|
1167
|
+
} else {
|
1168
|
+
Local<String> eval =
|
1169
|
+
String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
|
1170
|
+
NewStringType::kNormal,
|
1171
|
+
(int)RSTRING_LEN(parent_object_eval))
|
1172
|
+
.ToLocalChecked();
|
1173
|
+
|
1174
|
+
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
1175
|
+
if (parsed_script.IsEmpty()) {
|
1176
|
+
parse_error = true;
|
1177
|
+
} else {
|
1178
|
+
MaybeLocal<Value> maybe_value =
|
1179
|
+
parsed_script.ToLocalChecked()->Run(context);
|
1180
|
+
attach_error = true;
|
1181
|
+
|
1182
|
+
if (!maybe_value.IsEmpty()) {
|
1183
|
+
Local<Value> value = maybe_value.ToLocalChecked();
|
1184
|
+
if (value->IsObject()) {
|
1185
|
+
value.As<Object>()->Set(
|
1186
|
+
context,
|
1187
|
+
v8_str,
|
1188
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1189
|
+
->GetFunction(context)
|
1190
|
+
.ToLocalChecked());
|
1191
|
+
attach_error = false;
|
1192
|
+
}
|
1193
|
+
}
|
1194
|
+
}
|
1195
|
+
}
|
1025
1196
|
}
|
1026
1197
|
|
1027
1198
|
// always raise out of V8 context
|
1028
1199
|
if (parse_error) {
|
1029
|
-
|
1200
|
+
rb_raise(rb_eParseError, "Invalid object %" PRIsVALUE, parent_object);
|
1030
1201
|
}
|
1031
1202
|
|
1032
1203
|
if (attach_error) {
|
1033
|
-
|
1204
|
+
rb_raise(rb_eParseError, "Was expecting %" PRIsVALUE" to be an object", parent_object);
|
1034
1205
|
}
|
1035
1206
|
|
1036
1207
|
return Qnil;
|
@@ -1047,35 +1218,39 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
|
|
1047
1218
|
return context_info->isolate_info->mutex;
|
1048
1219
|
}
|
1049
1220
|
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
if (isolate_info->isolate) {
|
1057
|
-
if (isolate_info->interrupted) {
|
1058
|
-
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");
|
1059
1227
|
} else {
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
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
|
+
);
|
1238
|
+
} else {
|
1239
|
+
isolate->Dispose();
|
1240
|
+
}
|
1066
1241
|
}
|
1067
|
-
|
1242
|
+
isolate = nullptr;
|
1068
1243
|
}
|
1069
1244
|
|
1070
|
-
if (
|
1071
|
-
delete[]
|
1072
|
-
delete
|
1245
|
+
if (startup_data) {
|
1246
|
+
delete[] startup_data->data;
|
1247
|
+
delete startup_data;
|
1073
1248
|
}
|
1074
1249
|
|
1075
|
-
delete
|
1250
|
+
delete allocator;
|
1076
1251
|
}
|
1077
1252
|
|
1078
|
-
static void
|
1253
|
+
static void free_context_raw(void *arg) {
|
1079
1254
|
ContextInfo* context_info = (ContextInfo*)arg;
|
1080
1255
|
IsolateInfo* isolate_info = context_info->isolate_info;
|
1081
1256
|
Persistent<Context>* context = context_info->context;
|
@@ -1092,6 +1267,20 @@ static void *free_context_raw(void* arg) {
|
|
1092
1267
|
}
|
1093
1268
|
|
1094
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
|
+
|
1095
1284
|
return NULL;
|
1096
1285
|
}
|
1097
1286
|
|
@@ -1105,22 +1294,17 @@ static void free_context(ContextInfo* context_info) {
|
|
1105
1294
|
context_info_copy->context = context_info->context;
|
1106
1295
|
|
1107
1296
|
if (isolate_info && isolate_info->refs() > 1) {
|
1108
|
-
|
1109
|
-
|
1297
|
+
pthread_t free_context_thread;
|
1298
|
+
if (pthread_create(&free_context_thread, thread_attr_p,
|
1299
|
+
free_context_thr, (void*)context_info_copy)) {
|
1110
1300
|
fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
|
1111
|
-
|
1112
|
-
|
1301
|
+
}
|
1113
1302
|
} else {
|
1114
|
-
|
1303
|
+
free_context_raw(context_info_copy);
|
1115
1304
|
}
|
1116
1305
|
|
1117
|
-
|
1118
|
-
|
1119
|
-
}
|
1120
|
-
|
1121
|
-
if (isolate_info) {
|
1122
|
-
context_info->isolate_info = NULL;
|
1123
|
-
}
|
1306
|
+
context_info->context = NULL;
|
1307
|
+
context_info->isolate_info = NULL;
|
1124
1308
|
}
|
1125
1309
|
|
1126
1310
|
static void deallocate_isolate(void* data) {
|
@@ -1135,7 +1319,7 @@ static void mark_isolate(void* data) {
|
|
1135
1319
|
isolate_info->mark();
|
1136
1320
|
}
|
1137
1321
|
|
1138
|
-
void deallocate(void* data) {
|
1322
|
+
static void deallocate(void* data) {
|
1139
1323
|
ContextInfo* context_info = (ContextInfo*)data;
|
1140
1324
|
|
1141
1325
|
free_context(context_info);
|
@@ -1150,22 +1334,22 @@ static void mark_context(void* data) {
|
|
1150
1334
|
}
|
1151
1335
|
}
|
1152
1336
|
|
1153
|
-
void deallocate_external_function(void * data) {
|
1337
|
+
static void deallocate_external_function(void * data) {
|
1154
1338
|
xfree(data);
|
1155
1339
|
}
|
1156
1340
|
|
1157
|
-
void deallocate_snapshot(void * data) {
|
1341
|
+
static void deallocate_snapshot(void * data) {
|
1158
1342
|
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1159
1343
|
delete[] snapshot_info->data;
|
1160
1344
|
xfree(snapshot_info);
|
1161
1345
|
}
|
1162
1346
|
|
1163
|
-
VALUE allocate_external_function(VALUE klass) {
|
1347
|
+
static VALUE allocate_external_function(VALUE klass) {
|
1164
1348
|
VALUE* self = ALLOC(VALUE);
|
1165
1349
|
return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
|
1166
1350
|
}
|
1167
1351
|
|
1168
|
-
VALUE allocate(VALUE klass) {
|
1352
|
+
static VALUE allocate(VALUE klass) {
|
1169
1353
|
ContextInfo* context_info = ALLOC(ContextInfo);
|
1170
1354
|
context_info->isolate_info = NULL;
|
1171
1355
|
context_info->context = NULL;
|
@@ -1173,7 +1357,7 @@ VALUE allocate(VALUE klass) {
|
|
1173
1357
|
return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
|
1174
1358
|
}
|
1175
1359
|
|
1176
|
-
VALUE allocate_snapshot(VALUE klass) {
|
1360
|
+
static VALUE allocate_snapshot(VALUE klass) {
|
1177
1361
|
SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
|
1178
1362
|
snapshot_info->data = NULL;
|
1179
1363
|
snapshot_info->raw_size = 0;
|
@@ -1181,7 +1365,7 @@ VALUE allocate_snapshot(VALUE klass) {
|
|
1181
1365
|
return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
|
1182
1366
|
}
|
1183
1367
|
|
1184
|
-
VALUE allocate_isolate(VALUE klass) {
|
1368
|
+
static VALUE allocate_isolate(VALUE klass) {
|
1185
1369
|
IsolateInfo* isolate_info = new IsolateInfo();
|
1186
1370
|
|
1187
1371
|
return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
|
@@ -1201,25 +1385,90 @@ rb_heap_stats(VALUE self) {
|
|
1201
1385
|
|
1202
1386
|
if (!isolate) {
|
1203
1387
|
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1388
|
+
rb_hash_aset(rval, ID2SYM(rb_intern("total_physical_size")), ULONG2NUM(0));
|
1389
|
+
rb_hash_aset(rval, ID2SYM(rb_intern("total_heap_size_executable")), ULONG2NUM(0));
|
1390
|
+
rb_hash_aset(rval, ID2SYM(rb_intern("total_heap_size")), ULONG2NUM(0));
|
1391
|
+
rb_hash_aset(rval, ID2SYM(rb_intern("used_heap_size")), ULONG2NUM(0));
|
1392
|
+
rb_hash_aset(rval, ID2SYM(rb_intern("heap_size_limit")), ULONG2NUM(0));
|
1209
1393
|
|
1210
1394
|
} else {
|
1211
|
-
|
1395
|
+
isolate->GetHeapStatistics(&stats);
|
1212
1396
|
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1397
|
+
rb_hash_aset(rval, ID2SYM(rb_intern("total_physical_size")), ULONG2NUM(stats.total_physical_size()));
|
1398
|
+
rb_hash_aset(rval, ID2SYM(rb_intern("total_heap_size_executable")), ULONG2NUM(stats.total_heap_size_executable()));
|
1399
|
+
rb_hash_aset(rval, ID2SYM(rb_intern("total_heap_size")), ULONG2NUM(stats.total_heap_size()));
|
1400
|
+
rb_hash_aset(rval, ID2SYM(rb_intern("used_heap_size")), ULONG2NUM(stats.used_heap_size()));
|
1401
|
+
rb_hash_aset(rval, ID2SYM(rb_intern("heap_size_limit")), ULONG2NUM(stats.heap_size_limit()));
|
1218
1402
|
}
|
1219
1403
|
|
1220
1404
|
return rval;
|
1221
1405
|
}
|
1222
1406
|
|
1407
|
+
// https://github.com/bnoordhuis/node-heapdump/blob/master/src/heapdump.cc
|
1408
|
+
class FileOutputStream : public OutputStream {
|
1409
|
+
public:
|
1410
|
+
FileOutputStream(FILE* stream) : stream_(stream) {}
|
1411
|
+
|
1412
|
+
virtual int GetChunkSize() {
|
1413
|
+
return 65536;
|
1414
|
+
}
|
1415
|
+
|
1416
|
+
virtual void EndOfStream() {}
|
1417
|
+
|
1418
|
+
virtual WriteResult WriteAsciiChunk(char* data, int size) {
|
1419
|
+
const size_t len = static_cast<size_t>(size);
|
1420
|
+
size_t off = 0;
|
1421
|
+
|
1422
|
+
while (off < len && !feof(stream_) && !ferror(stream_))
|
1423
|
+
off += fwrite(data + off, 1, len - off, stream_);
|
1424
|
+
|
1425
|
+
return off == len ? kContinue : kAbort;
|
1426
|
+
}
|
1427
|
+
|
1428
|
+
private:
|
1429
|
+
FILE* stream_;
|
1430
|
+
};
|
1431
|
+
|
1432
|
+
|
1433
|
+
static VALUE
|
1434
|
+
rb_heap_snapshot(VALUE self, VALUE file) {
|
1435
|
+
|
1436
|
+
rb_io_t *fptr;
|
1437
|
+
|
1438
|
+
fptr = RFILE(file)->fptr;
|
1439
|
+
|
1440
|
+
if (!fptr) return Qfalse;
|
1441
|
+
|
1442
|
+
FILE* fp;
|
1443
|
+
fp = fdopen(fptr->fd, "w");
|
1444
|
+
if (fp == NULL) return Qfalse;
|
1445
|
+
|
1446
|
+
|
1447
|
+
ContextInfo* context_info;
|
1448
|
+
Data_Get_Struct(self, ContextInfo, context_info);
|
1449
|
+
Isolate* isolate;
|
1450
|
+
isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
|
1451
|
+
|
1452
|
+
if (!isolate) return Qfalse;
|
1453
|
+
|
1454
|
+
Locker lock(isolate);
|
1455
|
+
Isolate::Scope isolate_scope(isolate);
|
1456
|
+
HandleScope handle_scope(isolate);
|
1457
|
+
|
1458
|
+
HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
1459
|
+
|
1460
|
+
const HeapSnapshot* const snap = heap_profiler->TakeHeapSnapshot();
|
1461
|
+
|
1462
|
+
FileOutputStream stream(fp);
|
1463
|
+
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
1464
|
+
|
1465
|
+
fflush(fp);
|
1466
|
+
|
1467
|
+
const_cast<HeapSnapshot*>(snap)->Delete();
|
1468
|
+
|
1469
|
+
return Qtrue;
|
1470
|
+
}
|
1471
|
+
|
1223
1472
|
static VALUE
|
1224
1473
|
rb_context_stop(VALUE self) {
|
1225
1474
|
|
@@ -1255,13 +1504,23 @@ nogvl_context_call(void *args) {
|
|
1255
1504
|
if (!call) {
|
1256
1505
|
return NULL;
|
1257
1506
|
}
|
1258
|
-
|
1507
|
+
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1508
|
+
Isolate* isolate = isolate_info->isolate;
|
1259
1509
|
|
1260
1510
|
// in gvl flag
|
1261
1511
|
isolate->SetData(IN_GVL, (void*)false);
|
1262
1512
|
// terminate ASAP
|
1263
1513
|
isolate->SetData(DO_TERMINATE, (void*)false);
|
1264
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
|
+
|
1265
1524
|
Isolate::Scope isolate_scope(isolate);
|
1266
1525
|
EscapableHandleScope handle_scope(isolate);
|
1267
1526
|
TryCatch trycatch(isolate);
|
@@ -1287,8 +1546,7 @@ static void unblock_function(void *args) {
|
|
1287
1546
|
call->context_info->isolate_info->interrupted = true;
|
1288
1547
|
}
|
1289
1548
|
|
1290
|
-
static VALUE
|
1291
|
-
rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
1549
|
+
static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
1292
1550
|
ContextInfo* context_info;
|
1293
1551
|
FunctionCall call;
|
1294
1552
|
VALUE *call_argv = NULL;
|
@@ -1320,8 +1578,14 @@ rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1320
1578
|
call_argv = argv + 1;
|
1321
1579
|
}
|
1322
1580
|
|
1323
|
-
|
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
|
+
}
|
1324
1587
|
|
1588
|
+
bool missingFunction = false;
|
1325
1589
|
{
|
1326
1590
|
Locker lock(isolate);
|
1327
1591
|
Isolate::Scope isolate_scope(isolate);
|
@@ -1332,35 +1596,37 @@ rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1332
1596
|
|
1333
1597
|
// examples of such usage can be found in
|
1334
1598
|
// https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
} else {
|
1341
|
-
|
1342
|
-
Local<v8::Function> fun = Local<v8::Function>::Cast(val.ToLocalChecked());
|
1343
|
-
call.fun = fun;
|
1344
|
-
int fun_argc = call.argc;
|
1345
|
-
|
1346
|
-
if (fun_argc > 0) {
|
1347
|
-
call.argv = (v8::Local<Value> *) malloc(sizeof(void *) * fun_argc);
|
1348
|
-
if (!call.argv) {
|
1349
|
-
return Qnil;
|
1350
|
-
}
|
1351
|
-
for(int i=0; i < fun_argc; i++) {
|
1352
|
-
call.argv[i] = convert_ruby_to_v8(isolate, call_argv[i]);
|
1353
|
-
}
|
1354
|
-
}
|
1355
|
-
|
1356
|
-
rb_thread_call_without_gvl(nogvl_context_call, &call, unblock_function, &call);
|
1357
|
-
free(call.argv);
|
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
|
+
}
|
1358
1604
|
|
1359
|
-
|
1605
|
+
if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
|
1606
|
+
missingFunction = true;
|
1607
|
+
} else {
|
1608
|
+
|
1609
|
+
Local<v8::Function> fun = Local<v8::Function>::Cast(val.ToLocalChecked());
|
1610
|
+
call.fun = fun;
|
1611
|
+
int fun_argc = call.argc;
|
1612
|
+
|
1613
|
+
if (fun_argc > 0) {
|
1614
|
+
call.argv = (v8::Local<Value> *) malloc(sizeof(void *) * fun_argc);
|
1615
|
+
if (!call.argv) {
|
1616
|
+
return Qnil;
|
1617
|
+
}
|
1618
|
+
for(int i=0; i < fun_argc; i++) {
|
1619
|
+
call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
|
1620
|
+
}
|
1621
|
+
}
|
1622
|
+
rb_thread_call_without_gvl(nogvl_context_call, &call, unblock_function, &call);
|
1623
|
+
free(call.argv);
|
1624
|
+
|
1625
|
+
}
|
1360
1626
|
}
|
1361
1627
|
|
1362
1628
|
if (missingFunction) {
|
1363
|
-
|
1629
|
+
rb_raise(rb_eScriptRuntimeError, "Unknown JavaScript method invoked");
|
1364
1630
|
}
|
1365
1631
|
|
1366
1632
|
return convert_result_to_ruby(self, call.result);
|
@@ -1379,57 +1645,84 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1379
1645
|
return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
|
1380
1646
|
}
|
1381
1647
|
|
1382
|
-
|
1383
|
-
|
1384
|
-
void Init_mini_racer_extension ( void )
|
1385
|
-
{
|
1386
|
-
VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
|
1387
|
-
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
1388
|
-
rb_cSnapshot = rb_define_class_under(rb_mMiniRacer, "Snapshot", rb_cObject);
|
1389
|
-
rb_cIsolate = rb_define_class_under(rb_mMiniRacer, "Isolate", rb_cObject);
|
1390
|
-
VALUE rb_cPlatform = rb_define_class_under(rb_mMiniRacer, "Platform", rb_cObject);
|
1391
|
-
|
1392
|
-
VALUE rb_eError = rb_define_class_under(rb_mMiniRacer, "Error", rb_eStandardError);
|
1393
|
-
|
1394
|
-
VALUE rb_eEvalError = rb_define_class_under(rb_mMiniRacer, "EvalError", rb_eError);
|
1395
|
-
rb_eScriptTerminatedError = rb_define_class_under(rb_mMiniRacer, "ScriptTerminatedError", rb_eEvalError);
|
1396
|
-
rb_eV8OutOfMemoryError = rb_define_class_under(rb_mMiniRacer, "V8OutOfMemoryError", rb_eEvalError);
|
1397
|
-
rb_eParseError = rb_define_class_under(rb_mMiniRacer, "ParseError", rb_eEvalError);
|
1398
|
-
rb_eScriptRuntimeError = rb_define_class_under(rb_mMiniRacer, "RuntimeError", rb_eEvalError);
|
1648
|
+
static void set_ruby_exiting(VALUE value) {
|
1649
|
+
(void)value;
|
1399
1650
|
|
1400
|
-
|
1401
|
-
rb_eSnapshotError = rb_define_class_under(rb_mMiniRacer, "SnapshotError", rb_eError);
|
1402
|
-
rb_ePlatformAlreadyInitializedError = rb_define_class_under(rb_mMiniRacer, "PlatformAlreadyInitialized", rb_eError);
|
1403
|
-
rb_cFailedV8Conversion = rb_define_class_under(rb_mMiniRacer, "FailedV8Conversion", rb_cObject);
|
1404
|
-
rb_mJSON = rb_define_module("JSON");
|
1651
|
+
int res = pthread_rwlock_wrlock(&exit_lock);
|
1405
1652
|
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
rb_define_private_method(rb_cContext, "create_isolate_value",(VALUE(*)(...))&rb_context_create_isolate_value, 0);
|
1412
|
-
rb_define_private_method(rb_cContext, "eval_unsafe",(VALUE(*)(...))&rb_context_eval_unsafe, 2);
|
1413
|
-
rb_define_private_method(rb_cContext, "call_unsafe", (VALUE(*)(...))&rb_context_call_unsafe, -1);
|
1414
|
-
rb_define_private_method(rb_cContext, "isolate_mutex", (VALUE(*)(...))&rb_context_isolate_mutex, 0);
|
1415
|
-
rb_define_private_method(rb_cContext, "init_unsafe",(VALUE(*)(...))&rb_context_init_unsafe, 2);
|
1416
|
-
|
1417
|
-
rb_define_alloc_func(rb_cContext, allocate);
|
1418
|
-
rb_define_alloc_func(rb_cSnapshot, allocate_snapshot);
|
1419
|
-
rb_define_alloc_func(rb_cIsolate, allocate_isolate);
|
1420
|
-
|
1421
|
-
rb_define_private_method(rb_cExternalFunction, "notify_v8", (VALUE(*)(...))&rb_external_function_notify_v8, 0);
|
1422
|
-
rb_define_alloc_func(rb_cExternalFunction, allocate_external_function);
|
1423
|
-
|
1424
|
-
rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0);
|
1425
|
-
rb_define_method(rb_cSnapshot, "dump", (VALUE(*)(...))&rb_snapshot_dump, 0);
|
1426
|
-
rb_define_method(rb_cSnapshot, "warmup_unsafe!", (VALUE(*)(...))&rb_snapshot_warmup_unsafe, 1);
|
1427
|
-
rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
|
1653
|
+
ruby_exiting = true;
|
1654
|
+
if (res == 0) {
|
1655
|
+
pthread_rwlock_unlock(&exit_lock);
|
1656
|
+
}
|
1657
|
+
}
|
1428
1658
|
|
1429
|
-
|
1430
|
-
rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
|
1659
|
+
extern "C" {
|
1431
1660
|
|
1432
|
-
|
1661
|
+
__attribute__((visibility("default"))) void Init_mini_racer_extension ( void )
|
1662
|
+
{
|
1663
|
+
VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
|
1664
|
+
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
1665
|
+
rb_cSnapshot = rb_define_class_under(rb_mMiniRacer, "Snapshot", rb_cObject);
|
1666
|
+
rb_cIsolate = rb_define_class_under(rb_mMiniRacer, "Isolate", rb_cObject);
|
1667
|
+
VALUE rb_cPlatform = rb_define_class_under(rb_mMiniRacer, "Platform", rb_cObject);
|
1668
|
+
|
1669
|
+
VALUE rb_eError = rb_define_class_under(rb_mMiniRacer, "Error", rb_eStandardError);
|
1670
|
+
|
1671
|
+
VALUE rb_eEvalError = rb_define_class_under(rb_mMiniRacer, "EvalError", rb_eError);
|
1672
|
+
rb_eScriptTerminatedError = rb_define_class_under(rb_mMiniRacer, "ScriptTerminatedError", rb_eEvalError);
|
1673
|
+
rb_eV8OutOfMemoryError = rb_define_class_under(rb_mMiniRacer, "V8OutOfMemoryError", rb_eEvalError);
|
1674
|
+
rb_eParseError = rb_define_class_under(rb_mMiniRacer, "ParseError", rb_eEvalError);
|
1675
|
+
rb_eScriptRuntimeError = rb_define_class_under(rb_mMiniRacer, "RuntimeError", rb_eEvalError);
|
1676
|
+
|
1677
|
+
rb_cJavaScriptFunction = rb_define_class_under(rb_mMiniRacer, "JavaScriptFunction", rb_cObject);
|
1678
|
+
rb_eSnapshotError = rb_define_class_under(rb_mMiniRacer, "SnapshotError", rb_eError);
|
1679
|
+
rb_ePlatformAlreadyInitializedError = rb_define_class_under(rb_mMiniRacer, "PlatformAlreadyInitialized", rb_eError);
|
1680
|
+
rb_cFailedV8Conversion = rb_define_class_under(rb_mMiniRacer, "FailedV8Conversion", rb_cObject);
|
1681
|
+
rb_mJSON = rb_define_module("JSON");
|
1682
|
+
|
1683
|
+
VALUE rb_cExternalFunction = rb_define_class_under(rb_cContext, "ExternalFunction", rb_cObject);
|
1684
|
+
|
1685
|
+
rb_define_method(rb_cContext, "stop", (VALUE(*)(...))&rb_context_stop, 0);
|
1686
|
+
rb_define_method(rb_cContext, "dispose_unsafe", (VALUE(*)(...))&rb_context_dispose, 0);
|
1687
|
+
rb_define_method(rb_cContext, "heap_stats", (VALUE(*)(...))&rb_heap_stats, 0);
|
1688
|
+
rb_define_method(rb_cContext, "write_heap_snapshot_unsafe", (VALUE(*)(...))&rb_heap_snapshot, 1);
|
1689
|
+
|
1690
|
+
rb_define_private_method(rb_cContext, "create_isolate_value",(VALUE(*)(...))&rb_context_create_isolate_value, 0);
|
1691
|
+
rb_define_private_method(rb_cContext, "eval_unsafe",(VALUE(*)(...))&rb_context_eval_unsafe, 2);
|
1692
|
+
rb_define_private_method(rb_cContext, "call_unsafe", (VALUE(*)(...))&rb_context_call_unsafe, -1);
|
1693
|
+
rb_define_private_method(rb_cContext, "isolate_mutex", (VALUE(*)(...))&rb_context_isolate_mutex, 0);
|
1694
|
+
rb_define_private_method(rb_cContext, "init_unsafe",(VALUE(*)(...))&rb_context_init_unsafe, 2);
|
1695
|
+
|
1696
|
+
rb_define_alloc_func(rb_cContext, allocate);
|
1697
|
+
rb_define_alloc_func(rb_cSnapshot, allocate_snapshot);
|
1698
|
+
rb_define_alloc_func(rb_cIsolate, allocate_isolate);
|
1699
|
+
|
1700
|
+
rb_define_private_method(rb_cExternalFunction, "notify_v8", (VALUE(*)(...))&rb_external_function_notify_v8, 0);
|
1701
|
+
rb_define_alloc_func(rb_cExternalFunction, allocate_external_function);
|
1702
|
+
|
1703
|
+
rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0);
|
1704
|
+
rb_define_method(rb_cSnapshot, "dump", (VALUE(*)(...))&rb_snapshot_dump, 0);
|
1705
|
+
rb_define_method(rb_cSnapshot, "warmup_unsafe!", (VALUE(*)(...))&rb_snapshot_warmup_unsafe, 1);
|
1706
|
+
rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
|
1707
|
+
|
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);
|
1711
|
+
rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
|
1712
|
+
|
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);
|
1433
1727
|
}
|
1434
|
-
|
1435
1728
|
}
|