mirror of
https://github.com/rubyjs/therubyracer
synced 2023-03-27 23:21:42 -04:00
Added support for context timeouts
This commit is contained in:
parent
11ab68e437
commit
885ea08cb0
5 changed files with 68 additions and 7 deletions
|
@ -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) {}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue