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

add pre-emption APIS

v8 4.5 has very convenient APIs for interrupting potentially long
running JavaScript code. This exposes those APIs to Ruby code so that it
can place its own limits on the CPU resources consumed by V8.
This commit is contained in:
Charles Lowell 2015-08-14 16:04:27 +03:00
parent 5a1632adba
commit b29c94f087
6 changed files with 113 additions and 37 deletions

View file

@ -22,8 +22,7 @@ before_install:
- gem update --system 2.1.11
script:
- bundle exec rake compile
- bundle exec rspec spec/c
- bundle exec rspec spec/v8/context_spec.rb
- bundle exec rspec spec/c spec/v8/context_spec.rb spec/v8/isolate_spec.rb
- bundle exec rspec spec/mem
sudo: false
addons:

View file

@ -9,6 +9,9 @@ namespace rr {
rb_eval_string("require 'v8/retained_objects'");
ClassBuilder("Isolate").
defineSingletonMethod("New", &New).
defineMethod("TerminateExecution", &TerminateExecution).
defineMethod("IsExecutionTerminating", &IsExecutionTerminating).
defineMethod("CancelTerminateExecution", &CancelTerminateExecution).
defineMethod("ThrowException", &ThrowException).
defineMethod("SetCaptureStackTraceForUncaughtExceptions", &SetCaptureStackTraceForUncaughtExceptions).
defineMethod("IdleNotificationDeadline", &IdleNotificationDeadline).
@ -33,6 +36,19 @@ namespace rr {
return Isolate(isolate);
}
VALUE Isolate::TerminateExecution(VALUE self) {
Isolate(self)->TerminateExecution();
return Qnil;
};
VALUE Isolate::IsExecutionTerminating(VALUE self) {
return Bool(Isolate(self)->IsExecutionTerminating());
}
VALUE Isolate::CancelTerminateExecution(VALUE self) {
Isolate(self)->CancelTerminateExecution();
return Qnil;
}
VALUE Isolate::ThrowException(VALUE self, VALUE error) {
Isolate isolate(self);
Locker lock(isolate);

View file

@ -30,8 +30,11 @@ namespace rr {
static void Init();
static VALUE New(VALUE self);
static VALUE SetCaptureStackTraceForUncaughtExceptions(VALUE self, VALUE capture, VALUE stack_limit, VALUE options);
static VALUE TerminateExecution(VALUE self);
static VALUE IsExecutionTerminating(VALUE self);
static VALUE CancelTerminateExecution(VALUE self);
static VALUE ThrowException(VALUE self, VALUE error);
static VALUE SetCaptureStackTraceForUncaughtExceptions(VALUE self, VALUE capture, VALUE stack_limit, VALUE options);
inline Isolate(IsolateData* data_) : data(data_) {}
inline Isolate(v8::Isolate* isolate) :

View file

@ -1,9 +1,58 @@
module V8
##
# A completely encapsulated JavaScript virtual machine. No state of
# any kind is shared between isolates.
#
# The isolate is the primany API for understanding and configuring
# the runtime profile of the JavaScript interpreter. For example, it
# is through the isolate that you can take heap and stack samples
# for memory and cpu profiling. It is also through the isolate that
# you can place restraints on the amount of memory and CPU that your
# JavaScript execution will consume.
#
# An isolate can have many exceutions contexts that each have their
# own scopes, and so significant resource savings can be had by
# reusing the same isolate for multiple contexts.
class Isolate
# @!attribute [r] native
# @return [V8::C::Isolate] the underlying C++ object
attr_reader :native
##
# Create an new isolate.
def initialize()
@native = V8::C::Isolate::New()
end
##
# Terminates any currently executing JavaScript in this Isolate.
#
# This will throw an uncatchable exception inside the JavaScript
# code which will forcibly unwind the stack.
#
# @return [NilClass] nil
def terminate_execution!
@native.TerminateExecution()
end
##
# Returns true if execution is terminating, but there are still
# JavaScript frames left on the stack.
#
# @return [TrueClass | FalseClass] whether isolate is terminating execution
def execution_terminating?
@native.IsExecutionTerminating()
end
##
# Cancels any scheduled termination of JavaScript. Not really sure
# why you would want to do this, but if you figure out a use-case,
# would love for you to document it here.
#
# @return [NilClass] nil
def cancel_terminate_execution!
@native.CancelTerminateExecution()
end
end
end

View file

@ -1,35 +1 @@
require 'v8'
def rputs(msg)
puts "<pre>#{ERB::Util.h(msg)}</pre>"
$stdout.flush
end
module V8ContextHelpers
module GroupMethods
def requires_v8_context
around(:each) do |example|
bootstrap_v8_context(&example)
end
end
end
def bootstrap_v8_context
V8::C::Locker() do
V8::C::HandleScope() do
@cxt = V8::C::Context::New()
begin
@cxt.Enter()
yield
ensure
@cxt.Exit()
end
end
end
end
end
RSpec.configure do |c|
c.include V8ContextHelpers
c.extend V8ContextHelpers::GroupMethods
end

43
spec/v8/isolate_spec.rb Normal file
View file

@ -0,0 +1,43 @@
require 'spec_helper'
describe V8::Isolate do
let(:isolate) { V8::Isolate.new }
let(:context) { V8::Context.new isolate: isolate }
it "allows you to terminate javascript execution" do
context['doTerminate'] = lambda { isolate.terminate_execution! }
expect(isolate).to_not be_execution_terminating
expect {
context.eval <<-JS
function doesTerminate() {
doTerminate();
}
function returnFive() {
return 5;
}
doesTerminate();
returnFive();
JS
}.to raise_error V8::C::PendingExceptionError
end
it "allows you to cancel terminated javascript executing" do
context['doTerminate'] = lambda do
isolate.terminate_execution!
isolate.cancel_terminate_execution!
end
result = context.eval <<-JS
function doesTerminate() {
doTerminate();
}
function returnFive() {
return 5;
}
doesTerminate();
returnFive();
JS
expect(result).to eql 5
end
end