Skip to content
This repository was archived by the owner on Dec 4, 2023. It is now read-only.

Commit d202a2e

Browse files
committed
Merge remote-tracking branch 'samsaffron/master'
2 parents 465306a + 554c1c5 commit d202a2e

5 files changed

Lines changed: 69 additions & 7 deletions

File tree

ext/v8/rr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ class Script : public Ref<v8::Script> {
372372
static void Init();
373373
static VALUE New(int argc, VALUE argv[], VALUE self);
374374
static VALUE Run(VALUE self);
375+
static VALUE RunWithTimeout(VALUE self, VALUE timeout);
375376

376377
inline Script(VALUE value) : Ref<v8::Script>(value) {}
377378
inline Script(v8::Handle<v8::Script> script) : Ref<v8::Script>(script) {}

ext/v8/script.cc

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
#include "rr.h"
2+
#include "pthread.h"
3+
#include "unistd.h"
24

35
namespace rr {
46

57
void Script::Init() {
68
ClassBuilder("Script").
79
defineSingletonMethod("New", &New).
810
defineMethod("Run", &Run).
11+
defineMethod("RunWithTimeout", &RunWithTimeout).
912
store(&Class);
1013
ClassBuilder("ScriptOrigin").
1114
defineSingletonMethod("new", &ScriptOrigin::initialize).
@@ -69,6 +72,38 @@ VALUE Script::Run(VALUE self) {
6972
return Value(Script(self)->Run());
7073
}
7174

75+
typedef struct {
76+
v8::Isolate *isolate;
77+
long timeout;
78+
} timeout_data;
79+
80+
void* breaker(void *d) {
81+
timeout_data* data = (timeout_data*)d;
82+
usleep(data->timeout*1000);
83+
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
84+
v8::V8::TerminateExecution(data->isolate);
85+
return NULL;
86+
}
87+
88+
VALUE Script::RunWithTimeout(VALUE self, VALUE timeout) {
89+
pthread_t breaker_thread;
90+
timeout_data data;
91+
VALUE rval;
92+
void *res;
93+
94+
data.isolate = v8::Isolate::GetCurrent();
95+
data.timeout = NUM2LONG(timeout);
96+
97+
pthread_create(&breaker_thread, NULL, breaker, &data);
98+
99+
rval = Value(Script(self)->Run());
100+
101+
pthread_cancel(breaker_thread);
102+
pthread_join(breaker_thread, &res);
103+
104+
return rval;
105+
}
106+
72107
template <> void Pointer<v8::ScriptData>::unwrap(VALUE value) {
73108
Data_Get_Struct(value, class v8::ScriptData, pointer);
74109
}
@@ -77,4 +112,4 @@ template <> void Pointer<v8::ScriptOrigin>::unwrap(VALUE value) {
77112
Data_Get_Struct(value, class v8::ScriptOrigin, pointer);
78113
}
79114

80-
} //namespace rr
115+
} //namespace rr

lib/v8/context.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ class Context
3939
# @return [V8::C::Context] the underlying C++ object
4040
attr_reader :native
4141

42+
# maximum execution time for script in milliseconds
43+
attr_reader :timeout
44+
4245
# Creates a new context.
4346
#
4447
# If passed the `:with` option, that object will be used as
@@ -50,12 +53,16 @@ class Context
5053
# cxt['hello'] #=> 'Hi'
5154
# end
5255
#
56+
# If passed the `:timeout` option, every eval will timeout once
57+
# N milliseconds elapse
58+
#
5359
# @param [Hash<Symbol, Object>] options initial context configuration
5460
# * :with scope serves as the global scope of the new context
5561
# @yield [V8::Context] the newly created context
5662
def initialize(options = {})
5763
@conversion = Conversion.new
5864
@access = Access.new
65+
@timeout = options[:timeout]
5966
if global = options[:with]
6067
Context.new.enter do
6168
global_template = global.class.to_template.InstanceTemplate()
@@ -84,7 +91,11 @@ def eval(source, filename = '<eval>', line = 1)
8491
end
8592
enter do
8693
script = try { V8::C::Script::New(source.to_s, filename.to_s) }
87-
to_ruby try {script.Run()}
94+
if @timeout
95+
to_ruby try {script.RunWithTimeout(@timeout)}
96+
else
97+
to_ruby try {script.Run()}
98+
end
8899
end
89100
end
90101

@@ -242,4 +253,4 @@ def lock_scope_and_enter
242253
Context.current = current
243254
end
244255
end
245-
end
256+
end

lib/v8/error.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,14 @@ def protect
139139
# @return [V8::Error] the error generated by this try/catch
140140
def self.Error(trycatch)
141141
exception = trycatch.Exception()
142+
142143
value = exception.to_ruby
143144
cause = nil
144-
javascript_backtrace = V8::StackTrace.new(trycatch.Message().GetStackTrace())
145+
message = trycatch.Message()
146+
javascript_backtrace = V8::StackTrace.new(message.GetStackTrace()) if message
147+
145148
message = if !exception.kind_of?(V8::C::Value)
146-
exception.to_s
149+
exception.to_s==""?"Script Timed Out":exception.to_s
147150
elsif exception.IsNativeError()
148151
if cause = exception.GetHiddenValue("rr::Cause")
149152
cause = cause.Value()
@@ -163,4 +166,4 @@ def self.Error(trycatch)
163166
V8::Error.new(message, value, javascript_backtrace, cause)
164167
end
165168
const_set :JSError, Error
166-
end
169+
end

spec/threading_spec.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
require 'spec_helper'
22

3+
describe "Timeouts" do
4+
it "allows for timeout on context" do
5+
ctx = V8::Context.new(:timeout => 10)
6+
lambda {ctx.eval("while(true){}")}.should(raise_error)
7+
8+
# context should not be bust after it exploded once
9+
ctx["x"] = 1;
10+
ctx.eval("x=2;")
11+
ctx["x"].should == 2
12+
end
13+
end
14+
315
describe "using v8 from multiple threads", :threads => true do
416

517
it "creates contexts from within threads" do
@@ -49,4 +61,4 @@
4961
V8::C::Locker::StopPreemption()
5062
end
5163
end
52-
end
64+
end

0 commit comments

Comments
 (0)