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 void Init();
|
||||||
static VALUE New(int argc, VALUE argv[], VALUE self);
|
static VALUE New(int argc, VALUE argv[], VALUE self);
|
||||||
static VALUE Run(VALUE self);
|
static VALUE Run(VALUE self);
|
||||||
|
static VALUE RunWithTimeout(VALUE self, VALUE timeout);
|
||||||
|
|
||||||
inline Script(VALUE value) : Ref<v8::Script>(value) {}
|
inline Script(VALUE value) : Ref<v8::Script>(value) {}
|
||||||
inline Script(v8::Handle<v8::Script> script) : Ref<v8::Script>(script) {}
|
inline Script(v8::Handle<v8::Script> script) : Ref<v8::Script>(script) {}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "rr.h"
|
#include "rr.h"
|
||||||
|
#include "pthread.h"
|
||||||
|
#include "unistd.h"
|
||||||
|
|
||||||
namespace rr {
|
namespace rr {
|
||||||
|
|
||||||
|
@ -6,6 +8,7 @@ void Script::Init() {
|
||||||
ClassBuilder("Script").
|
ClassBuilder("Script").
|
||||||
defineSingletonMethod("New", &New).
|
defineSingletonMethod("New", &New).
|
||||||
defineMethod("Run", &Run).
|
defineMethod("Run", &Run).
|
||||||
|
defineMethod("RunWithTimeout", &RunWithTimeout).
|
||||||
store(&Class);
|
store(&Class);
|
||||||
ClassBuilder("ScriptOrigin").
|
ClassBuilder("ScriptOrigin").
|
||||||
defineSingletonMethod("new", &ScriptOrigin::initialize).
|
defineSingletonMethod("new", &ScriptOrigin::initialize).
|
||||||
|
@ -69,6 +72,37 @@ VALUE Script::Run(VALUE self) {
|
||||||
return Value(Script(self)->Run());
|
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) {
|
template <> void Pointer<v8::ScriptData>::unwrap(VALUE value) {
|
||||||
Data_Get_Struct(value, class v8::ScriptData, pointer);
|
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);
|
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
|
# @return [V8::C::Context] the underlying C++ object
|
||||||
attr_reader :native
|
attr_reader :native
|
||||||
|
|
||||||
|
# maximum execution time for script in milliseconds
|
||||||
|
attr_reader :timeout
|
||||||
|
|
||||||
# Creates a new context.
|
# Creates a new context.
|
||||||
#
|
#
|
||||||
# If passed the `:with` option, that object will be used as
|
# If passed the `:with` option, that object will be used as
|
||||||
|
@ -50,12 +53,16 @@ module V8
|
||||||
# cxt['hello'] #=> 'Hi'
|
# cxt['hello'] #=> 'Hi'
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
|
# If passed the `:timeout` option, every eval will timeout once
|
||||||
|
# N milliseconds elapse
|
||||||
|
#
|
||||||
# @param [Hash<Symbol, Object>] options initial context configuration
|
# @param [Hash<Symbol, Object>] options initial context configuration
|
||||||
# * :with scope serves as the global scope of the new context
|
# * :with scope serves as the global scope of the new context
|
||||||
# @yield [V8::Context] the newly created context
|
# @yield [V8::Context] the newly created context
|
||||||
def initialize(options = {})
|
def initialize(options = {})
|
||||||
@conversion = Conversion.new
|
@conversion = Conversion.new
|
||||||
@access = Access.new
|
@access = Access.new
|
||||||
|
@timeout = options[:timeout]
|
||||||
if global = options[:with]
|
if global = options[:with]
|
||||||
Context.new.enter do
|
Context.new.enter do
|
||||||
global_template = global.class.to_template.InstanceTemplate()
|
global_template = global.class.to_template.InstanceTemplate()
|
||||||
|
@ -84,7 +91,11 @@ module V8
|
||||||
end
|
end
|
||||||
enter do
|
enter do
|
||||||
script = try { V8::C::Script::New(source.to_s, filename.to_s) }
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -242,4 +253,4 @@ module V8
|
||||||
Context.current = current
|
Context.current = current
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -139,11 +139,14 @@ module V8
|
||||||
# @return [V8::Error] the error generated by this try/catch
|
# @return [V8::Error] the error generated by this try/catch
|
||||||
def self.Error(trycatch)
|
def self.Error(trycatch)
|
||||||
exception = trycatch.Exception()
|
exception = trycatch.Exception()
|
||||||
|
|
||||||
value = exception.to_ruby
|
value = exception.to_ruby
|
||||||
cause = nil
|
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)
|
message = if !exception.kind_of?(V8::C::Value)
|
||||||
exception.to_s
|
exception.to_s==""?"Script Timed Out":exception.to_s
|
||||||
elsif exception.IsNativeError()
|
elsif exception.IsNativeError()
|
||||||
if cause = exception.GetHiddenValue("rr::Cause")
|
if cause = exception.GetHiddenValue("rr::Cause")
|
||||||
cause = cause.Value()
|
cause = cause.Value()
|
||||||
|
@ -163,4 +166,4 @@ module V8
|
||||||
V8::Error.new(message, value, javascript_backtrace, cause)
|
V8::Error.new(message, value, javascript_backtrace, cause)
|
||||||
end
|
end
|
||||||
const_set :JSError, Error
|
const_set :JSError, Error
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
require 'spec_helper'
|
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
|
describe "using v8 from multiple threads", :threads => true do
|
||||||
|
|
||||||
it "creates contexts from within threads" 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()
|
V8::C::Locker::StopPreemption()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue