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

FIX: issues with timeouts that happen in rubyland

(or timeouts that happen between rubyland and js land)

still imperfect, but an improvement
This commit is contained in:
Sam 2016-09-01 13:25:47 +10:00
parent 671628d99d
commit bcb3027e8f
2 changed files with 61 additions and 6 deletions

View file

@ -110,11 +110,28 @@ static void init_v8() {
platform_lock.unlock();
}
static VALUE
ruby_timeout_thread(VALUE data) {
rb_funcall(data, rb_intern("raise"), 1, rb_eScriptTerminatedError);
return Qnil;
}
void* breaker(void *d) {
EvalParams* data = (EvalParams*)d;
Isolate* isolate = data->context_info->isolate_info->isolate;
usleep(data->timeout*1000);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
V8::TerminateExecution(data->context_info->isolate_info->isolate);
// flag for termination
isolate->SetData(2, (void*)true);
VALUE* ruby_thread = (VALUE*)isolate->GetData(1);
if (ruby_thread == NULL) {
V8::TerminateExecution(isolate);
} else {
rb_thread_create((VALUE(*)(...))&ruby_timeout_thread, (void*)*ruby_thread);
}
return NULL;
}
@ -131,7 +148,12 @@ nogvl_context_eval(void* arg) {
Local<Context> context = eval_params->context_info->context->Get(isolate);
Context::Scope context_scope(context);
// in gvl flag
isolate->SetData(0, (void*)false);
// ruby thread value
isolate->SetData(1, (void*)NULL);
// terminate ASAP
isolate->SetData(2, (void*)false);
MaybeLocal<Script> parsed_script = Script::Compile(context, *eval_params->eval);
result->parsed = !parsed_script.IsEmpty();
@ -645,9 +667,19 @@ gvl_ruby_callback(void* data) {
callback_data.args = ruby_args;
callback_data.failed = false;
if ((bool)args->GetIsolate()->GetData(2) == true) {
args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Terminated execution during tansition from Ruby to JS"));
return NULL;
}
VALUE thread = rb_funcall(rb_cThread, rb_intern("current"), 0);
args->GetIsolate()->SetData(1, (void*)&thread);
result = rb_rescue((VALUE(*)(...))&protected_callback, (VALUE)(&callback_data),
(VALUE(*)(...))&rescue_callback, (VALUE)(&callback_data));
args->GetIsolate()->SetData(1, (void*)NULL);
if(callback_data.failed) {
VALUE parent = rb_iv_get(self, "@parent");
rb_iv_set(parent, "@current_exception", result);
@ -663,6 +695,10 @@ gvl_ruby_callback(void* data) {
xfree(ruby_args);
}
if ((bool)args->GetIsolate()->GetData(2) == true) {
V8::TerminateExecution(args->GetIsolate());
}
return NULL;
}

View file

@ -524,23 +524,42 @@ raise FooError, "I like foos"
def test_ruby_based_property_in_rval
v8 = MiniRacer::Context.new
v8.attach 'print', proc{|x| puts x}
puts v8.eval "var o = {get bar() { print(42); }}; o"
v8.attach 'echo', proc{|x| x}
assert_equal({"bar" => 42}, v8.eval("var o = {get bar() { return echo(42); }}; o"))
end
def test_function_rval
context = MiniRacer::Context.new
context.attach("print", proc{|msg| puts msg})
context.eval "print('foo')"
context.attach("echo", proc{|msg| msg})
assert_equal("foo", context.eval("echo('foo')"))
end
def test_timeout_in_ruby_land
skip("This fails and should be fixed")
context = MiniRacer::Context.new(timeout: 50)
context.attach('sleep', proc{ sleep 0.1 })
assert_raises(MiniRacer::ScriptTerminatedError) do
context.eval('sleep(); "hi";')
end
end
def test_undef_mem
context = MiniRacer::Context.new(timeout: 5)
context.attach("marsh", proc do |a, b, c|
return [a,b,c] if a.is_a?(MiniRacer::FailedV8Conversion) || b.is_a?(MiniRacer::FailedV8Conversion) || c.is_a?(MiniRacer::FailedV8Conversion)
a[rand(10000).to_s] = "a"
b[rand(10000).to_s] = "b"
c[rand(10000).to_s] = "c"
[a,b,c]
end)
assert_raises do
# TODO make it raise the correct exception!
context.eval("var a = [{},{},{}]; while(true) { a = marsh(a[0],a[1],a[2]); }")
end
end
class TestPlatform < MiniRacer::Platform