1
0
Fork 0
mirror of https://github.com/rubyjs/mini_racer synced 2023-03-27 23:21:28 -04:00

Add thread safe wrapper around function_call

This commit is contained in:
Jb Aviat 2018-03-13 17:57:25 -07:00
parent 9f358f46dc
commit 9e81a2cce6
2 changed files with 26 additions and 14 deletions

View file

@ -1090,7 +1090,7 @@ rb_context_dispose(VALUE self) {
} }
static void* static void*
nogvl_function_call(void *args) { nogvl_context_function_call(void *args) {
FunctionCall *call = (FunctionCall *) args; FunctionCall *call = (FunctionCall *) args;
if (!call) { if (!call) {
@ -1132,11 +1132,15 @@ static void unblock_function(void *args) {
} }
static VALUE static VALUE
rb_function_call(int argc, VALUE *argv, VALUE self) { rb_context_function_call_unsafe(int argc, VALUE *argv, VALUE self) {
ContextInfo* context_info; ContextInfo* context_info;
FunctionCall call;
VALUE res = Qnil;
VALUE *call_argv = NULL;
Data_Get_Struct(self, ContextInfo, context_info); Data_Get_Struct(self, ContextInfo, context_info);
VALUE res; Isolate* isolate = context_info->isolate_info->isolate;
if (argc < 1) { if (argc < 1) {
rb_raise(rb_eArgError, "need at least one argument %d", argc); rb_raise(rb_eArgError, "need at least one argument %d", argc);
@ -1144,7 +1148,7 @@ rb_function_call(int argc, VALUE *argv, VALUE self) {
VALUE function_name = argv[0]; VALUE function_name = argv[0];
if (TYPE(function_name) != T_STRING) { if (TYPE(function_name) != T_STRING) {
rb_raise(rb_eTypeError, "first arg should be a String"); rb_raise(rb_eTypeError, "first argument should be a String");
} }
char *fname = RSTRING_PTR(function_name); char *fname = RSTRING_PTR(function_name);
@ -1152,31 +1156,26 @@ rb_function_call(int argc, VALUE *argv, VALUE self) {
return Qnil; return Qnil;
} }
FunctionCall call; call.context_info = context_info;
call.error = false; call.error = false;
call.function_name = fname; call.function_name = fname;
call.context_info = context_info;
call.argc = argc - 1; call.argc = argc - 1;
call.argv = NULL; call.argv = NULL;
VALUE *call_argv = NULL;
if (call.argc > 0) { if (call.argc > 0) {
// skip first argument which is the function name // skip first argument which is the function name
call_argv = argv + 1; call_argv = argv + 1;
} }
Isolate* isolate = context_info->isolate_info->isolate;
{ {
Locker lock(isolate); Locker lock(isolate);
Isolate::Scope isolate_scope(isolate); Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate); HandleScope handle_scope(isolate);
TryCatch trycatch(isolate);
Local<Context> context = context_info->context->Get(isolate); Local<Context> context = context_info->context->Get(isolate);
Context::Scope context_scope(context); Context::Scope context_scope(context);
// examples of such usage can be found in // examples of such usage can be found in
// https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711 // https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
Local<v8::Function> fun = Local<v8::Function>::Cast(context->Global()->Get( Local<v8::Function> fun = Local<v8::Function>::Cast(context->Global()->Get(
String::NewFromUtf8(isolate, call.function_name))); String::NewFromUtf8(isolate, call.function_name)));
call.fun = fun; call.fun = fun;
@ -1192,8 +1191,7 @@ rb_function_call(int argc, VALUE *argv, VALUE self) {
} }
} }
rb_thread_call_without_gvl(nogvl_function_call, &call, unblock_function, &call); rb_thread_call_without_gvl(nogvl_context_function_call, &call, unblock_function, &call);
free(call.argv); free(call.argv);
if (!call.error) { if (!call.error) {
@ -1236,7 +1234,8 @@ extern "C" {
rb_define_method(rb_cContext, "stop", (VALUE(*)(...))&rb_context_stop, 0); rb_define_method(rb_cContext, "stop", (VALUE(*)(...))&rb_context_stop, 0);
rb_define_method(rb_cContext, "dispose_unsafe", (VALUE(*)(...))&rb_context_dispose, 0); rb_define_method(rb_cContext, "dispose_unsafe", (VALUE(*)(...))&rb_context_dispose, 0);
rb_define_method(rb_cContext, "heap_stats", (VALUE(*)(...))&rb_heap_stats, 0); rb_define_method(rb_cContext, "heap_stats", (VALUE(*)(...))&rb_heap_stats, 0);
rb_define_method(rb_cContext, "function_call", (VALUE(*)(...))&rb_function_call, -1); rb_define_private_method(rb_cContext, "eval_unsafe",(VALUE(*)(...))&rb_context_eval_unsafe, 2);
rb_define_private_method(rb_cContext, "function_call_unsafe", (VALUE(*)(...))&rb_context_function_call_unsafe, -1);
rb_define_alloc_func(rb_cContext, allocate); rb_define_alloc_func(rb_cContext, allocate);
rb_define_alloc_func(rb_cSnapshot, allocate_snapshot); rb_define_alloc_func(rb_cSnapshot, allocate_snapshot);

View file

@ -186,6 +186,19 @@ module MiniRacer
@eval_thread = nil @eval_thread = nil
end end
def function_call(function_name, *arguments)
raise(ContextDisposedError, 'attempted to call function_call on a disposed context!') if @disposed
@eval_thread = Thread.current
isolate.with_lock do
timeout do
function_call_unsafe(function_name, *arguments)
end
end
ensure
@eval_thread = nil
end
def dispose def dispose
if !@disposed if !@disposed
isolate.with_lock do isolate.with_lock do