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:
parent
5a1632adba
commit
b29c94f087
6 changed files with 113 additions and 37 deletions
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) :
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
43
spec/v8/isolate_spec.rb
Normal 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
|
Loading…
Add table
Reference in a new issue