sq_mini_racer 0.2.5.0.2 → 0.3.1.0.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/.gitignore +9 -0
- data/CHANGELOG +73 -0
- data/Dockerfile +0 -1
- data/README.md +17 -2
- data/Rakefile +44 -62
- data/azure-pipelines.yml +3 -3
- data/azure-template.yml +10 -1
- data/ext/mini_racer_extension/extconf.rb +62 -55
- data/ext/mini_racer_extension/mini_racer_extension.cc +239 -97
- data/ext/mini_racer_loader/extconf.rb +8 -0
- data/ext/{prv_ext_loader/prv_ext_loader.c → mini_racer_loader/mini_racer_loader.c} +24 -5
- data/lib/sqreen/mini_racer.rb +90 -24
- data/lib/sqreen/mini_racer/version.rb +3 -3
- data/mini_racer.gemspec +14 -5
- data/valgrind.supp +74 -0
- metadata +68 -16
- data/.travis.yml +0 -73
- data/ext/prv_ext_loader/extconf.rb +0 -5
@@ -36,6 +36,8 @@
|
|
36
36
|
#include "compat.hpp"
|
37
37
|
#include "simdutf8check.h"
|
38
38
|
|
39
|
+
#include <time.h>
|
40
|
+
|
39
41
|
using namespace v8;
|
40
42
|
|
41
43
|
typedef struct {
|
@@ -49,6 +51,7 @@ public:
|
|
49
51
|
ArrayBuffer::Allocator* allocator;
|
50
52
|
StartupData* startup_data;
|
51
53
|
bool interrupted;
|
54
|
+
bool added_gc_cb;
|
52
55
|
pid_t pid;
|
53
56
|
VALUE mutex;
|
54
57
|
|
@@ -66,15 +69,12 @@ public:
|
|
66
69
|
|
67
70
|
|
68
71
|
IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
|
69
|
-
interrupted(false), pid(getpid()), refs_count(0) {
|
72
|
+
interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
|
70
73
|
VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
|
71
74
|
mutex = rb_class_new_instance(0, nullptr, cMutex);
|
72
75
|
}
|
73
76
|
|
74
|
-
~IsolateInfo()
|
75
|
-
void free_isolate(IsolateInfo*);
|
76
|
-
free_isolate(this);
|
77
|
-
}
|
77
|
+
~IsolateInfo();
|
78
78
|
|
79
79
|
void init(SnapshotInfo* snapshot_info = nullptr);
|
80
80
|
|
@@ -151,6 +151,7 @@ typedef struct {
|
|
151
151
|
Local<Function> fun;
|
152
152
|
Local<Value> *argv;
|
153
153
|
EvalResult result;
|
154
|
+
size_t max_memory;
|
154
155
|
} FunctionCall;
|
155
156
|
|
156
157
|
enum IsolateFlags {
|
@@ -179,6 +180,11 @@ static VALUE rb_cDateTime = Qnil;
|
|
179
180
|
static std::unique_ptr<Platform> current_platform = NULL;
|
180
181
|
static std::mutex platform_lock;
|
181
182
|
|
183
|
+
static pthread_attr_t *thread_attr_p;
|
184
|
+
static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
185
|
+
static bool ruby_exiting = false; // guarded by exit_lock
|
186
|
+
static bool single_threaded = false;
|
187
|
+
|
182
188
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
183
189
|
bool platform_already_initialized = false;
|
184
190
|
|
@@ -190,6 +196,9 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
190
196
|
platform_lock.lock();
|
191
197
|
|
192
198
|
if (current_platform == NULL) {
|
199
|
+
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
200
|
+
single_threaded = true;
|
201
|
+
}
|
193
202
|
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
|
194
203
|
} else {
|
195
204
|
platform_already_initialized = true;
|
@@ -256,11 +265,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
256
265
|
Local<Value> local_value = v8res.ToLocalChecked();
|
257
266
|
if ((local_value->IsObject() || local_value->IsArray()) &&
|
258
267
|
!local_value->IsDate() && !local_value->IsFunction()) {
|
259
|
-
Local<Object> JSON = context->Global()->Get(
|
260
|
-
|
268
|
+
Local<Object> JSON = context->Global()->Get(
|
269
|
+
context, String::NewFromUtf8Literal(isolate, "JSON"))
|
270
|
+
.ToLocalChecked().As<Object>();
|
261
271
|
|
262
|
-
Local<Function> stringify = JSON->Get(
|
263
|
-
|
272
|
+
Local<Function> stringify = JSON->Get(
|
273
|
+
context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
|
274
|
+
.ToLocalChecked().As<Function>();
|
264
275
|
|
265
276
|
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
266
277
|
const unsigned argc = 1;
|
@@ -314,7 +325,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
314
325
|
} else if(trycatch.HasTerminated()) {
|
315
326
|
evalRes.terminated = true;
|
316
327
|
evalRes.message = new Persistent<Value>();
|
317
|
-
Local<String> tmp = String::
|
328
|
+
Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
|
318
329
|
evalRes.message->Reset(isolate, tmp);
|
319
330
|
}
|
320
331
|
if (!trycatch.StackTrace(context).IsEmpty()) {
|
@@ -335,7 +346,8 @@ nogvl_context_eval(void* arg) {
|
|
335
346
|
|
336
347
|
EvalParams* eval_params = (EvalParams*)arg;
|
337
348
|
EvalResult* result = eval_params->result;
|
338
|
-
|
349
|
+
IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
|
350
|
+
Isolate* isolate = isolate_info->isolate;
|
339
351
|
|
340
352
|
Isolate::Scope isolate_scope(isolate);
|
341
353
|
HandleScope handle_scope(isolate);
|
@@ -379,7 +391,10 @@ nogvl_context_eval(void* arg) {
|
|
379
391
|
// parsing successful
|
380
392
|
if (eval_params->max_memory > 0) {
|
381
393
|
isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
|
394
|
+
if (!isolate_info->added_gc_cb) {
|
382
395
|
isolate->AddGCEpilogueCallback(gc_callback);
|
396
|
+
isolate_info->added_gc_cb = true;
|
397
|
+
}
|
383
398
|
}
|
384
399
|
|
385
400
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -392,6 +407,12 @@ nogvl_context_eval(void* arg) {
|
|
392
407
|
return 0;
|
393
408
|
}
|
394
409
|
|
410
|
+
static VALUE new_empty_failed_conv_obj() {
|
411
|
+
// TODO isolate code that translates execption to ruby
|
412
|
+
// exception so we can properly return it
|
413
|
+
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
414
|
+
}
|
415
|
+
|
395
416
|
// assumes isolate locking is in place
|
396
417
|
static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
397
418
|
Local<Value> value) {
|
@@ -423,8 +444,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
423
444
|
VALUE rb_array = rb_ary_new();
|
424
445
|
Local<Array> arr = Local<Array>::Cast(value);
|
425
446
|
for(uint32_t i=0; i < arr->Length(); i++) {
|
426
|
-
|
427
|
-
|
447
|
+
MaybeLocal<Value> element = arr->Get(context, i);
|
448
|
+
if (element.IsEmpty()) {
|
449
|
+
continue;
|
450
|
+
}
|
451
|
+
VALUE rb_elem = convert_v8_to_ruby(isolate, context, element.ToLocalChecked());
|
428
452
|
if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
|
429
453
|
return rb_elem;
|
430
454
|
}
|
@@ -454,26 +478,47 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
454
478
|
if (!maybe_props.IsEmpty()) {
|
455
479
|
Local<Array> props = maybe_props.ToLocalChecked();
|
456
480
|
for(uint32_t i=0; i < props->Length(); i++) {
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
481
|
+
MaybeLocal<Value> key = props->Get(context, i);
|
482
|
+
if (key.IsEmpty()) {
|
483
|
+
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
484
|
+
}
|
485
|
+
VALUE rb_key = convert_v8_to_ruby(isolate, context, key.ToLocalChecked());
|
461
486
|
|
462
|
-
|
463
|
-
//
|
464
|
-
|
465
|
-
|
487
|
+
MaybeLocal<Value> prop_value = object->Get(context, key.ToLocalChecked());
|
488
|
+
// this may have failed due to Get raising
|
489
|
+
if (prop_value.IsEmpty() || trycatch.HasCaught()) {
|
490
|
+
return new_empty_failed_conv_obj();
|
466
491
|
}
|
467
492
|
|
468
|
-
VALUE rb_value = convert_v8_to_ruby(
|
493
|
+
VALUE rb_value = convert_v8_to_ruby(
|
494
|
+
isolate, context, prop_value.ToLocalChecked());
|
469
495
|
rb_hash_aset(rb_hash, rb_key, rb_value);
|
470
496
|
}
|
471
497
|
}
|
472
498
|
return rb_hash;
|
473
499
|
}
|
474
500
|
|
475
|
-
|
476
|
-
|
501
|
+
if (value->IsSymbol()) {
|
502
|
+
v8::String::Utf8Value symbol_name(isolate,
|
503
|
+
Local<Symbol>::Cast(value)->Name());
|
504
|
+
|
505
|
+
VALUE str_symbol = rb_enc_str_new(
|
506
|
+
*symbol_name,
|
507
|
+
symbol_name.length(),
|
508
|
+
rb_enc_find("utf-8")
|
509
|
+
);
|
510
|
+
|
511
|
+
return ID2SYM(rb_intern_str(str_symbol));
|
512
|
+
}
|
513
|
+
|
514
|
+
MaybeLocal<String> rstr_maybe = value->ToString(context);
|
515
|
+
|
516
|
+
if (rstr_maybe.IsEmpty()) {
|
517
|
+
return Qnil;
|
518
|
+
} else {
|
519
|
+
Local<String> rstr = rstr_maybe.ToLocalChecked();
|
520
|
+
return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
521
|
+
}
|
477
522
|
}
|
478
523
|
|
479
524
|
static VALUE convert_v8_to_ruby(Isolate* isolate,
|
@@ -608,7 +653,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
608
653
|
length = RARRAY_LEN(value);
|
609
654
|
array = Array::New(isolate, (int)length);
|
610
655
|
for(i=0; i<length; i++) {
|
611
|
-
|
656
|
+
array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
612
657
|
}
|
613
658
|
return scope.Escape(array);
|
614
659
|
}
|
@@ -619,7 +664,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
619
664
|
length = RARRAY_LEN(hash_as_array);
|
620
665
|
for(i=0; i<length; i++) {
|
621
666
|
pair = rb_ary_entry(hash_as_array, i);
|
622
|
-
|
667
|
+
object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
623
668
|
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
624
669
|
}
|
625
670
|
return scope.Escape(object);
|
@@ -661,8 +706,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
661
706
|
value = rb_funcall(value, rb_intern("to_s"), 0);
|
662
707
|
return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
|
663
708
|
}
|
664
|
-
|
665
|
-
|
709
|
+
return scope.Escape(
|
710
|
+
String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
|
711
|
+
}
|
666
712
|
}
|
667
713
|
}
|
668
714
|
|
@@ -675,53 +721,43 @@ static void unblock_eval(void *ptr) {
|
|
675
721
|
* The implementations of the run_extra_code(), create_snapshot_data_blob() and
|
676
722
|
* warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
|
677
723
|
*/
|
678
|
-
bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
724
|
+
static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
679
725
|
const char *utf8_source, const char *name) {
|
680
726
|
Context::Scope context_scope(context);
|
681
727
|
TryCatch try_catch(isolate);
|
682
728
|
Local<String> source_string;
|
683
|
-
if (!String::NewFromUtf8(isolate, utf8_source
|
684
|
-
NewStringType::kNormal)
|
685
|
-
.ToLocal(&source_string)) {
|
729
|
+
if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
|
686
730
|
return false;
|
687
731
|
}
|
688
|
-
Local<
|
689
|
-
|
690
|
-
.ToLocalChecked();
|
732
|
+
Local<String> resource_name =
|
733
|
+
String::NewFromUtf8(isolate, name).ToLocalChecked();
|
691
734
|
ScriptOrigin origin(resource_name);
|
692
735
|
ScriptCompiler::Source source(source_string, origin);
|
693
736
|
Local<Script> script;
|
694
737
|
if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
|
695
738
|
return false;
|
696
|
-
if (script->Run(context).IsEmpty())
|
697
|
-
return false;
|
698
|
-
// CHECK(!try_catch.HasCaught());
|
739
|
+
if (script->Run(context).IsEmpty()) return false;
|
699
740
|
return true;
|
700
741
|
}
|
701
742
|
|
702
|
-
StartupData
|
743
|
+
static StartupData
|
703
744
|
create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
704
|
-
|
705
|
-
|
706
|
-
|
745
|
+
Isolate *isolate = Isolate::Allocate();
|
746
|
+
|
747
|
+
// Optionally run a script to embed, and serialize to create a snapshot blob.
|
748
|
+
SnapshotCreator snapshot_creator(isolate);
|
707
749
|
{
|
708
|
-
SnapshotCreator snapshot_creator;
|
709
|
-
Isolate *isolate = snapshot_creator.GetIsolate();
|
710
|
-
{
|
711
750
|
HandleScope scope(isolate);
|
712
|
-
|
751
|
+
Local<v8::Context> context = v8::Context::New(isolate);
|
713
752
|
if (embedded_source != nullptr &&
|
714
|
-
!run_extra_code(isolate, context, embedded_source,
|
715
|
-
|
716
|
-
return result;
|
753
|
+
!run_extra_code(isolate, context, embedded_source, "<embedded>")) {
|
754
|
+
return {};
|
717
755
|
}
|
718
756
|
snapshot_creator.SetDefaultContext(context);
|
719
757
|
}
|
720
|
-
|
758
|
+
return snapshot_creator.CreateBlob(
|
721
759
|
SnapshotCreator::FunctionCodeHandling::kClear);
|
722
760
|
}
|
723
|
-
return result;
|
724
|
-
}
|
725
761
|
|
726
762
|
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
727
763
|
const char *warmup_source) {
|
@@ -868,6 +904,29 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
|
868
904
|
return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
|
869
905
|
}
|
870
906
|
|
907
|
+
static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
908
|
+
IsolateInfo* isolate_info;
|
909
|
+
Data_Get_Struct(self, IsolateInfo, isolate_info);
|
910
|
+
|
911
|
+
if (current_platform == NULL) return Qfalse;
|
912
|
+
|
913
|
+
isolate_info->isolate->LowMemoryNotification();
|
914
|
+
return Qnil;
|
915
|
+
}
|
916
|
+
|
917
|
+
static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
918
|
+
IsolateInfo* isolate_info;
|
919
|
+
Data_Get_Struct(self, IsolateInfo, isolate_info);
|
920
|
+
|
921
|
+
if (current_platform == NULL) return Qfalse;
|
922
|
+
|
923
|
+
if (platform::PumpMessageLoop(current_platform.get(), isolate_info->isolate)){
|
924
|
+
return Qtrue;
|
925
|
+
} else {
|
926
|
+
return Qfalse;
|
927
|
+
}
|
928
|
+
}
|
929
|
+
|
871
930
|
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
872
931
|
ContextInfo* context_info;
|
873
932
|
Data_Get_Struct(self, ContextInfo, context_info);
|
@@ -1142,7 +1201,9 @@ gvl_ruby_callback(void* data) {
|
|
1142
1201
|
callback_data.failed = false;
|
1143
1202
|
|
1144
1203
|
if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
|
1145
|
-
args->GetIsolate()->ThrowException(
|
1204
|
+
args->GetIsolate()->ThrowException(
|
1205
|
+
String::NewFromUtf8Literal(args->GetIsolate(),
|
1206
|
+
"Terminated execution during transition from Ruby to JS"));
|
1146
1207
|
args->GetIsolate()->TerminateExecution();
|
1147
1208
|
if (length > 0) {
|
1148
1209
|
rb_ary_clear(ruby_args);
|
@@ -1156,7 +1217,7 @@ gvl_ruby_callback(void* data) {
|
|
1156
1217
|
|
1157
1218
|
if(callback_data.failed) {
|
1158
1219
|
rb_iv_set(parent, "@current_exception", result);
|
1159
|
-
args->GetIsolate()->ThrowException(String::
|
1220
|
+
args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
|
1160
1221
|
}
|
1161
1222
|
else {
|
1162
1223
|
HandleScope scope(args->GetIsolate());
|
@@ -1231,7 +1292,9 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1231
1292
|
|
1232
1293
|
if (parent_object == Qnil) {
|
1233
1294
|
context->Global()->Set(
|
1234
|
-
|
1295
|
+
context,
|
1296
|
+
v8_str,
|
1297
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1235
1298
|
->GetFunction(context)
|
1236
1299
|
.ToLocalChecked());
|
1237
1300
|
|
@@ -1244,7 +1307,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1244
1307
|
|
1245
1308
|
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
1246
1309
|
if (parsed_script.IsEmpty()) {
|
1247
|
-
|
1310
|
+
parse_error = true;
|
1248
1311
|
} else {
|
1249
1312
|
MaybeLocal<Value> maybe_value =
|
1250
1313
|
parsed_script.ToLocalChecked()->Run(context);
|
@@ -1254,11 +1317,12 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1254
1317
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1255
1318
|
if (value->IsObject()) {
|
1256
1319
|
value.As<Object>()->Set(
|
1257
|
-
|
1258
|
-
|
1320
|
+
context,
|
1321
|
+
v8_str,
|
1322
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1259
1323
|
->GetFunction(context)
|
1260
1324
|
.ToLocalChecked());
|
1261
|
-
|
1325
|
+
attach_error = false;
|
1262
1326
|
}
|
1263
1327
|
}
|
1264
1328
|
}
|
@@ -1288,35 +1352,39 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
|
|
1288
1352
|
return context_info->isolate_info->mutex;
|
1289
1353
|
}
|
1290
1354
|
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
if (isolate_info->isolate) {
|
1298
|
-
if (isolate_info->interrupted) {
|
1299
|
-
fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, it can not be disposed and memory will not be reclaimed till the Ruby process exits.\n");
|
1355
|
+
IsolateInfo::~IsolateInfo() {
|
1356
|
+
if (isolate) {
|
1357
|
+
if (this->interrupted) {
|
1358
|
+
fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, "
|
1359
|
+
"it can not be disposed and memory will not be "
|
1360
|
+
"reclaimed till the Ruby process exits.\n");
|
1300
1361
|
} else {
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1362
|
+
if (this->pid != getpid() && !single_threaded) {
|
1363
|
+
fprintf(stderr, "WARNING: V8 isolate was forked, "
|
1364
|
+
"it can not be disposed and "
|
1365
|
+
"memory will not be reclaimed "
|
1366
|
+
"till the Ruby process exits.\n"
|
1367
|
+
"It is VERY likely your process will hang.\n"
|
1368
|
+
"If you wish to use v8 in forked environment "
|
1369
|
+
"please ensure the platform is initialized with:\n"
|
1370
|
+
"MiniRacer::Platform.set_flags! :single_threaded\n"
|
1371
|
+
);
|
1304
1372
|
} else {
|
1305
|
-
|
1373
|
+
isolate->Dispose();
|
1306
1374
|
}
|
1307
1375
|
}
|
1308
|
-
|
1376
|
+
isolate = nullptr;
|
1309
1377
|
}
|
1310
1378
|
|
1311
|
-
if (
|
1312
|
-
delete[]
|
1313
|
-
delete
|
1379
|
+
if (startup_data) {
|
1380
|
+
delete[] startup_data->data;
|
1381
|
+
delete startup_data;
|
1314
1382
|
}
|
1315
1383
|
|
1316
|
-
delete
|
1384
|
+
delete allocator;
|
1317
1385
|
}
|
1318
1386
|
|
1319
|
-
static void
|
1387
|
+
static void free_context_raw(void *arg) {
|
1320
1388
|
ContextInfo* context_info = (ContextInfo*)arg;
|
1321
1389
|
IsolateInfo* isolate_info = context_info->isolate_info;
|
1322
1390
|
Persistent<Context>* context = context_info->context;
|
@@ -1333,6 +1401,20 @@ static void *free_context_raw(void* arg) {
|
|
1333
1401
|
}
|
1334
1402
|
|
1335
1403
|
xfree(context_info);
|
1404
|
+
}
|
1405
|
+
|
1406
|
+
static void *free_context_thr(void* arg) {
|
1407
|
+
if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
|
1408
|
+
return NULL;
|
1409
|
+
}
|
1410
|
+
if (ruby_exiting) {
|
1411
|
+
return NULL;
|
1412
|
+
}
|
1413
|
+
|
1414
|
+
free_context_raw(arg);
|
1415
|
+
|
1416
|
+
pthread_rwlock_unlock(&exit_lock);
|
1417
|
+
|
1336
1418
|
return NULL;
|
1337
1419
|
}
|
1338
1420
|
|
@@ -1347,22 +1429,17 @@ static void free_context(ContextInfo* context_info) {
|
|
1347
1429
|
|
1348
1430
|
if (isolate_info && isolate_info->refs() > 1) {
|
1349
1431
|
pthread_t free_context_thread;
|
1350
|
-
|
1432
|
+
if (pthread_create(&free_context_thread, thread_attr_p,
|
1433
|
+
free_context_thr, (void*)context_info_copy)) {
|
1351
1434
|
fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
|
1352
1435
|
}
|
1353
|
-
|
1354
1436
|
} else {
|
1355
1437
|
free_context_raw(context_info_copy);
|
1356
1438
|
}
|
1357
1439
|
|
1358
|
-
if (context_info->context && isolate_info && isolate_info->isolate) {
|
1359
1440
|
context_info->context = NULL;
|
1360
|
-
}
|
1361
|
-
|
1362
|
-
if (isolate_info) {
|
1363
1441
|
context_info->isolate_info = NULL;
|
1364
1442
|
}
|
1365
|
-
}
|
1366
1443
|
|
1367
1444
|
static void deallocate_isolate(void* data) {
|
1368
1445
|
|
@@ -1376,7 +1453,7 @@ static void mark_isolate(void* data) {
|
|
1376
1453
|
isolate_info->mark();
|
1377
1454
|
}
|
1378
1455
|
|
1379
|
-
void deallocate(void* data) {
|
1456
|
+
static void deallocate(void* data) {
|
1380
1457
|
ContextInfo* context_info = (ContextInfo*)data;
|
1381
1458
|
|
1382
1459
|
free_context(context_info);
|
@@ -1391,22 +1468,22 @@ static void mark_context(void* data) {
|
|
1391
1468
|
}
|
1392
1469
|
}
|
1393
1470
|
|
1394
|
-
void deallocate_external_function(void * data) {
|
1471
|
+
static void deallocate_external_function(void * data) {
|
1395
1472
|
xfree(data);
|
1396
1473
|
}
|
1397
1474
|
|
1398
|
-
void deallocate_snapshot(void * data) {
|
1475
|
+
static void deallocate_snapshot(void * data) {
|
1399
1476
|
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1400
1477
|
delete[] snapshot_info->data;
|
1401
1478
|
xfree(snapshot_info);
|
1402
1479
|
}
|
1403
1480
|
|
1404
|
-
VALUE allocate_external_function(VALUE klass) {
|
1481
|
+
static VALUE allocate_external_function(VALUE klass) {
|
1405
1482
|
VALUE* self = ALLOC(VALUE);
|
1406
1483
|
return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
|
1407
1484
|
}
|
1408
1485
|
|
1409
|
-
VALUE allocate(VALUE klass) {
|
1486
|
+
static VALUE allocate(VALUE klass) {
|
1410
1487
|
ContextInfo* context_info = ALLOC(ContextInfo);
|
1411
1488
|
context_info->isolate_info = NULL;
|
1412
1489
|
context_info->context = NULL;
|
@@ -1414,7 +1491,7 @@ VALUE allocate(VALUE klass) {
|
|
1414
1491
|
return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
|
1415
1492
|
}
|
1416
1493
|
|
1417
|
-
VALUE allocate_snapshot(VALUE klass) {
|
1494
|
+
static VALUE allocate_snapshot(VALUE klass) {
|
1418
1495
|
SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
|
1419
1496
|
snapshot_info->data = NULL;
|
1420
1497
|
snapshot_info->raw_size = 0;
|
@@ -1422,7 +1499,7 @@ VALUE allocate_snapshot(VALUE klass) {
|
|
1422
1499
|
return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
|
1423
1500
|
}
|
1424
1501
|
|
1425
|
-
VALUE allocate_isolate(VALUE klass) {
|
1502
|
+
static VALUE allocate_isolate(VALUE klass) {
|
1426
1503
|
IsolateInfo* isolate_info = new IsolateInfo();
|
1427
1504
|
|
1428
1505
|
return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
|
@@ -1519,6 +1596,8 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
1519
1596
|
FileOutputStream stream(fp);
|
1520
1597
|
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
1521
1598
|
|
1599
|
+
fflush(fp);
|
1600
|
+
|
1522
1601
|
const_cast<HeapSnapshot*>(snap)->Delete();
|
1523
1602
|
|
1524
1603
|
return Qtrue;
|
@@ -1576,13 +1655,23 @@ nogvl_context_call(void *args) {
|
|
1576
1655
|
if (!call) {
|
1577
1656
|
return 0;
|
1578
1657
|
}
|
1579
|
-
|
1658
|
+
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1659
|
+
Isolate* isolate = isolate_info->isolate;
|
1580
1660
|
|
1581
1661
|
// in gvl flag
|
1582
1662
|
isolate->SetData(IN_GVL, (void*)false);
|
1583
1663
|
// terminate ASAP
|
1584
1664
|
isolate->SetData(DO_TERMINATE, (void*)false);
|
1585
1665
|
|
1666
|
+
if (call->max_memory > 0) {
|
1667
|
+
isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
|
1668
|
+
isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
|
1669
|
+
if (!isolate_info->added_gc_cb) {
|
1670
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
1671
|
+
isolate_info->added_gc_cb = true;
|
1672
|
+
}
|
1673
|
+
}
|
1674
|
+
|
1586
1675
|
Isolate::Scope isolate_scope(isolate);
|
1587
1676
|
EscapableHandleScope handle_scope(isolate);
|
1588
1677
|
TryCatch trycatch(isolate);
|
@@ -1640,6 +1729,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1640
1729
|
call_argv = argv + 1;
|
1641
1730
|
}
|
1642
1731
|
|
1732
|
+
call.max_memory = 0;
|
1733
|
+
VALUE mem_softlimit = rb_iv_get(self, "@max_memory");
|
1734
|
+
if (mem_softlimit != Qnil) {
|
1735
|
+
unsigned long sl_int = NUM2ULONG(mem_softlimit);
|
1736
|
+
call.max_memory = (size_t)sl_int;
|
1737
|
+
}
|
1738
|
+
|
1643
1739
|
bool missingFunction = false;
|
1644
1740
|
{
|
1645
1741
|
Locker lock(isolate);
|
@@ -1651,8 +1747,11 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1651
1747
|
|
1652
1748
|
// examples of such usage can be found in
|
1653
1749
|
// https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
|
1654
|
-
|
1655
|
-
MaybeLocal<v8::Value> val
|
1750
|
+
MaybeLocal<String> fname = String::NewFromUtf8(isolate, call.function_name);
|
1751
|
+
MaybeLocal<v8::Value> val;
|
1752
|
+
if (!fname.IsEmpty()) {
|
1753
|
+
val = context->Global()->Get(context, fname.ToLocalChecked());
|
1754
|
+
}
|
1656
1755
|
|
1657
1756
|
if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
|
1658
1757
|
missingFunction = true;
|
@@ -1701,12 +1800,44 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1701
1800
|
return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
|
1702
1801
|
}
|
1703
1802
|
|
1803
|
+
static void set_ruby_exiting(VALUE value) {
|
1804
|
+
(void)value;
|
1805
|
+
|
1806
|
+
int res = pthread_rwlock_wrlock(&exit_lock);
|
1807
|
+
|
1808
|
+
ruby_exiting = true;
|
1809
|
+
if (res == 0) {
|
1810
|
+
pthread_rwlock_unlock(&exit_lock);
|
1811
|
+
}
|
1812
|
+
}
|
1813
|
+
|
1814
|
+
static VALUE rb_monotime(VALUE self) {
|
1815
|
+
struct timespec ts;
|
1816
|
+
if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
|
1817
|
+
return INT2FIX(-1);
|
1818
|
+
}
|
1819
|
+
|
1820
|
+
return DBL2NUM(
|
1821
|
+
(double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0);
|
1822
|
+
}
|
1823
|
+
|
1704
1824
|
extern "C" {
|
1705
1825
|
|
1706
|
-
void Init_sq_mini_racer_extension ( void )
|
1826
|
+
__attribute__((visibility("default"))) void Init_sq_mini_racer_extension ( void )
|
1707
1827
|
{
|
1708
|
-
|
1828
|
+
ID sqreen_id = rb_intern("Sqreen");
|
1829
|
+
VALUE rb_mSqreen;
|
1830
|
+
if (rb_const_defined(rb_cObject, sqreen_id)) {
|
1831
|
+
rb_mSqreen = rb_const_get(rb_cObject, sqreen_id);
|
1832
|
+
if (TYPE(rb_mSqreen) != T_MODULE) {
|
1833
|
+
rb_raise(rb_eTypeError, "Sqreen is not a module");
|
1834
|
+
return;
|
1835
|
+
}
|
1836
|
+
} else {
|
1837
|
+
rb_mSqreen = rb_define_module("Sqreen");
|
1838
|
+
}
|
1709
1839
|
VALUE rb_mMiniRacer = rb_define_module_under(rb_mSqreen, "MiniRacer");
|
1840
|
+
rb_define_module_function(rb_mMiniRacer, "monotime", (VALUE(*)(...))&rb_monotime, 0);
|
1710
1841
|
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
1711
1842
|
rb_cSnapshot = rb_define_class_under(rb_mMiniRacer, "Snapshot", rb_cObject);
|
1712
1843
|
rb_cIsolate = rb_define_class_under(rb_mMiniRacer, "Isolate", rb_cObject);
|
@@ -1753,8 +1884,19 @@ extern "C" {
|
|
1753
1884
|
rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
|
1754
1885
|
|
1755
1886
|
rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
|
1887
|
+
rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
|
1888
|
+
rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
|
1756
1889
|
rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
|
1757
1890
|
|
1758
1891
|
rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
|
1892
|
+
|
1893
|
+
rb_set_end_proc(set_ruby_exiting, Qnil);
|
1894
|
+
|
1895
|
+
static pthread_attr_t attr;
|
1896
|
+
if (pthread_attr_init(&attr) == 0) {
|
1897
|
+
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0) {
|
1898
|
+
thread_attr_p = &attr;
|
1899
|
+
}
|
1900
|
+
}
|
1759
1901
|
}
|
1760
1902
|
}
|