Added support for context timeouts

This commit is contained in:
Sam 2013-12-09 12:15:03 +11:00
parent 11ab68e437
commit 885ea08cb0
5 changed files with 68 additions and 7 deletions

View File

@ -372,6 +372,7 @@ public:
static void Init();
static VALUE New(int argc, VALUE argv[], VALUE self);
static VALUE Run(VALUE self);
static VALUE RunWithTimeout(VALUE self, VALUE timeout);
inline Script(VALUE value) : Ref<v8::Script>(value) {}
inline Script(v8::Handle<v8::Script> script) : Ref<v8::Script>(script) {}

View File

@ -1,4 +1,6 @@
#include "rr.h"
#include "pthread.h"
#include "unistd.h"
namespace rr {
@ -6,6 +8,7 @@ void Script::Init() {
ClassBuilder("Script").
defineSingletonMethod("New", &New).
defineMethod("Run", &Run).
defineMethod("RunWithTimeout", &RunWithTimeout).
store(&Class);
ClassBuilder("ScriptOrigin").
defineSingletonMethod("new", &ScriptOrigin::initialize).
@ -69,6 +72,37 @@ VALUE Script::Run(VALUE self) {
return Value(Script(self)->Run());
}
typedef struct {
v8::Isolate *isolate;
long timeout;
} timeout_data;
void* breaker(void *d) {
timeout_data* data = (timeout_data*)d;
usleep(data->timeout*1000);
v8::V8::TerminateExecution(data->isolate);
return NULL;
}
VALUE Script::RunWithTimeout(VALUE self, VALUE timeout) {
pthread_t breaker_thread;
timeout_data data;
VALUE rval;
void *res;
data.isolate = v8::Isolate::GetCurrent();
data.timeout = NUM2LONG(timeout);
pthread_create(&breaker_thread, NULL, breaker, &data);
rval = Value(Script(self)->Run());
pthread_cancel(breaker_thread);
pthread_join(breaker_thread, &res);
return rval;
}
template <> void Pointer<v8::ScriptData>::unwrap(VALUE value) {
Data_Get_Struct(value, class v8::ScriptData, pointer);
}
@ -77,4 +111,4 @@ template <> void Pointer<v8::ScriptOrigin>::unwrap(VALUE value) {
Data_Get_Struct(value, class v8::ScriptOrigin, pointer);
}
} //namespace rr
} //namespace rr

View File

@ -39,6 +39,9 @@ module V8
# @return [V8::C::Context] the underlying C++ object
attr_reader :native
# maximum execution time for script in milliseconds
attr_reader :timeout
# Creates a new context.
#
# If passed the `:with` option, that object will be used as
@ -50,12 +53,16 @@ module V8
# cxt['hello'] #=> 'Hi'
# end
#
# If passed the `:timeout` option, every eval will timeout once
# N milliseconds elapse
#
# @param [Hash<Symbol, Object>] options initial context configuration
# * :with scope serves as the global scope of the new context
# @yield [V8::Context] the newly created context
def initialize(options = {})
@conversion = Conversion.new
@access = Access.new
@timeout = options[:timeout]
if global = options[:with]
Context.new.enter do
global_template = global.class.to_template.InstanceTemplate()
@ -84,7 +91,11 @@ module V8
end
enter do
script = try { V8::C::Script::New(source.to_s, filename.to_s) }
to_ruby try {script.Run()}
if @timeout
to_ruby try {script.RunWithTimeout(@timeout)}
else
to_ruby try {script.Run()}
end
end
end
@ -242,4 +253,4 @@ module V8
Context.current = current
end
end
end
end

View File

@ -139,11 +139,14 @@ module V8
# @return [V8::Error] the error generated by this try/catch
def self.Error(trycatch)
exception = trycatch.Exception()
value = exception.to_ruby
cause = nil
javascript_backtrace = V8::StackTrace.new(trycatch.Message().GetStackTrace())
message = trycatch.Message()
javascript_backtrace = V8::StackTrace.new(message.GetStackTrace()) if message
message = if !exception.kind_of?(V8::C::Value)
exception.to_s
exception.to_s==""?"Script Timed Out":exception.to_s
elsif exception.IsNativeError()
if cause = exception.GetHiddenValue("rr::Cause")
cause = cause.Value()
@ -163,4 +166,4 @@ module V8
V8::Error.new(message, value, javascript_backtrace, cause)
end
const_set :JSError, Error
end
end

View File

@ -1,5 +1,17 @@
require 'spec_helper'
describe "Timeouts" do
it "allows for timeout on context" do
ctx = V8::Context.new(:timeout => 10)
lambda {ctx.eval("while(true){}")}.should(raise_error)
# context should not be bust after it exploded once
ctx["x"] = 1;
ctx.eval("x=2;")
ctx["x"].should == 2
end
end
describe "using v8 from multiple threads", :threads => true do
it "creates contexts from within threads" do
@ -49,4 +61,4 @@ describe "using v8 from multiple threads", :threads => true do
V8::C::Locker::StopPreemption()
end
end
end
end