Add methods to allow resuming execution after calling TerminateExecution().
Two new methods are added to allow embedders to determine that execution should be resumed at a particular point in the stack without being forced to unwind all JS frames. * V8::CancelTerminateExecution() -- When execution is terminated via a call to V8::TerminateExecution(), this method can be called to clear the termination exception so that the engine can continue to be used. * TryCatch::HasTerminated() -- When a TryCatch has caught a termination exception, HasTerminated() will return true to indicate it is valid to call V8::ResumeExecution() if desired. A test case is added to cctest/test-thread-termination.cc. BUG=v8:2361 Patch from Andrew Paprocki <andrew@ishiboo.com>. Review URL: https://chromiumcodereview.appspot.com/11142013 Patch from Andrew Paprocki <andrew@ishiboo.com>. git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14022 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
788de160c5
commit
53adf3b19c
1
AUTHORS
1
AUTHORS
@ -9,6 +9,7 @@ ARM Ltd.
|
||||
Hewlett-Packard Development Company, LP
|
||||
Igalia, S.L.
|
||||
Joyent, Inc.
|
||||
Bloomberg Finance L.P.
|
||||
|
||||
Akinori MUSHA <knu@FreeBSD.org>
|
||||
Alexander Botero-Lowry <alexbl@FreeBSD.org>
|
||||
|
48
include/v8.h
48
include/v8.h
@ -3601,6 +3601,24 @@ class V8EXPORT V8 {
|
||||
*/
|
||||
static bool IsExecutionTerminating(Isolate* isolate = NULL);
|
||||
|
||||
/**
|
||||
* Resume execution capability in the given isolate, whose execution
|
||||
* was previously forcefully terminated using TerminateExecution().
|
||||
*
|
||||
* When execution is forcefully terminated using TerminateExecution(),
|
||||
* the isolate can not resume execution until all JavaScript frames
|
||||
* have propagated the uncatchable exception which is generated. This
|
||||
* method allows the program embedding the engine to handle the
|
||||
* termination event and resume execution capability, even if
|
||||
* JavaScript frames remain on the stack.
|
||||
*
|
||||
* This method can be used by any thread even if that thread has not
|
||||
* acquired the V8 lock with a Locker object.
|
||||
*
|
||||
* \param isolate The isolate in which to resume execution capability.
|
||||
*/
|
||||
static void CancelTerminateExecution(Isolate* isolate);
|
||||
|
||||
/**
|
||||
* Releases any resources used by v8 and stops any utility threads
|
||||
* that may be running. Note that disposing v8 is permanent, it
|
||||
@ -3711,20 +3729,29 @@ class V8EXPORT TryCatch {
|
||||
bool HasCaught() const;
|
||||
|
||||
/**
|
||||
* For certain types of exceptions, it makes no sense to continue
|
||||
* execution.
|
||||
* For certain types of exceptions, it makes no sense to continue execution.
|
||||
*
|
||||
* Currently, the only type of exception that can be caught by a
|
||||
* TryCatch handler and for which it does not make sense to continue
|
||||
* is termination exception. Such exceptions are thrown when the
|
||||
* TerminateExecution methods are called to terminate a long-running
|
||||
* script.
|
||||
*
|
||||
* If CanContinue returns false, the correct action is to perform
|
||||
* any C++ cleanup needed and then return.
|
||||
* If CanContinue returns false, the correct action is to perform any C++
|
||||
* cleanup needed and then return. If CanContinue returns false and
|
||||
* HasTerminated returns true, it is possible to call
|
||||
* CancelTerminateExecution in order to continue calling into the engine.
|
||||
*/
|
||||
bool CanContinue() const;
|
||||
|
||||
/**
|
||||
* Returns true if an exception has been caught due to script execution
|
||||
* being terminated.
|
||||
*
|
||||
* There is no JavaScript representation of an execution termination
|
||||
* exception. Such exceptions are thrown when the TerminateExecution
|
||||
* methods are called to terminate a long-running script.
|
||||
*
|
||||
* If such an exception has been thrown, HasTerminated will return true,
|
||||
* indicating that it is possible to call CancelTerminateExecution in order
|
||||
* to continue calling into the engine.
|
||||
*/
|
||||
bool HasTerminated() const;
|
||||
|
||||
/**
|
||||
* Throws the exception caught by this TryCatch in a way that avoids
|
||||
* it being caught again by this same TryCatch. As with ThrowException
|
||||
@ -3800,6 +3827,7 @@ class V8EXPORT TryCatch {
|
||||
bool can_continue_ : 1;
|
||||
bool capture_message_ : 1;
|
||||
bool rethrow_ : 1;
|
||||
bool has_terminated_ : 1;
|
||||
|
||||
friend class v8::internal::Isolate;
|
||||
};
|
||||
|
14
src/api.cc
14
src/api.cc
@ -1885,7 +1885,8 @@ v8::TryCatch::TryCatch()
|
||||
is_verbose_(false),
|
||||
can_continue_(true),
|
||||
capture_message_(true),
|
||||
rethrow_(false) {
|
||||
rethrow_(false),
|
||||
has_terminated_(false) {
|
||||
isolate_->RegisterTryCatchHandler(this);
|
||||
}
|
||||
|
||||
@ -1913,6 +1914,11 @@ bool v8::TryCatch::CanContinue() const {
|
||||
}
|
||||
|
||||
|
||||
bool v8::TryCatch::HasTerminated() const {
|
||||
return has_terminated_;
|
||||
}
|
||||
|
||||
|
||||
v8::Handle<v8::Value> v8::TryCatch::ReThrow() {
|
||||
if (!HasCaught()) return v8::Local<v8::Value>();
|
||||
rethrow_ = true;
|
||||
@ -5956,6 +5962,12 @@ bool V8::IsExecutionTerminating(Isolate* isolate) {
|
||||
}
|
||||
|
||||
|
||||
void V8::CancelTerminateExecution(Isolate* isolate) {
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
i_isolate->stack_guard()->CancelTerminateExecution();
|
||||
}
|
||||
|
||||
|
||||
Isolate* Isolate::GetCurrent() {
|
||||
i::Isolate* isolate = i::Isolate::UncheckedCurrent();
|
||||
return reinterpret_cast<Isolate*>(isolate);
|
||||
|
@ -425,6 +425,13 @@ bool StackGuard::IsTerminateExecution() {
|
||||
}
|
||||
|
||||
|
||||
void StackGuard::CancelTerminateExecution() {
|
||||
ExecutionAccess access(isolate_);
|
||||
Continue(TERMINATE);
|
||||
isolate_->CancelTerminateExecution();
|
||||
}
|
||||
|
||||
|
||||
void StackGuard::TerminateExecution() {
|
||||
ExecutionAccess access(isolate_);
|
||||
thread_local_.interrupt_flags_ |= TERMINATE;
|
||||
|
@ -189,6 +189,7 @@ class StackGuard {
|
||||
void Interrupt();
|
||||
bool IsTerminateExecution();
|
||||
void TerminateExecution();
|
||||
void CancelTerminateExecution();
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
bool IsDebugBreak();
|
||||
void DebugBreak();
|
||||
|
@ -1056,6 +1056,23 @@ Failure* Isolate::TerminateExecution() {
|
||||
}
|
||||
|
||||
|
||||
void Isolate::CancelTerminateExecution() {
|
||||
if (try_catch_handler()) {
|
||||
try_catch_handler()->has_terminated_ = false;
|
||||
}
|
||||
if (has_pending_exception() &&
|
||||
pending_exception() == heap_.termination_exception()) {
|
||||
thread_local_top()->external_caught_exception_ = false;
|
||||
clear_pending_exception();
|
||||
}
|
||||
if (has_scheduled_exception() &&
|
||||
scheduled_exception() == heap_.termination_exception()) {
|
||||
thread_local_top()->external_caught_exception_ = false;
|
||||
clear_scheduled_exception();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Failure* Isolate::Throw(Object* exception, MessageLocation* location) {
|
||||
DoThrow(exception, location);
|
||||
return Failure::Exception();
|
||||
@ -1960,12 +1977,14 @@ void Isolate::PropagatePendingExceptionToExternalTryCatch() {
|
||||
} else if (thread_local_top_.pending_exception_ ==
|
||||
heap()->termination_exception()) {
|
||||
try_catch_handler()->can_continue_ = false;
|
||||
try_catch_handler()->has_terminated_ = true;
|
||||
try_catch_handler()->exception_ = heap()->null_value();
|
||||
} else {
|
||||
// At this point all non-object (failure) exceptions have
|
||||
// been dealt with so this shouldn't fail.
|
||||
ASSERT(!pending_exception()->IsFailure());
|
||||
try_catch_handler()->can_continue_ = true;
|
||||
try_catch_handler()->has_terminated_ = false;
|
||||
try_catch_handler()->exception_ = pending_exception();
|
||||
if (!thread_local_top_.pending_message_obj_->IsTheHole()) {
|
||||
try_catch_handler()->message_ = thread_local_top_.pending_message_obj_;
|
||||
|
@ -779,6 +779,7 @@ class Isolate {
|
||||
// Out of resource exception helpers.
|
||||
Failure* StackOverflow();
|
||||
Failure* TerminateExecution();
|
||||
void CancelTerminateExecution();
|
||||
|
||||
// Administration
|
||||
void Iterate(ObjectVisitor* v);
|
||||
|
@ -372,3 +372,40 @@ TEST(TerminateAndReenterFromThreadItself) {
|
||||
"f()"))->Run()->IsTrue());
|
||||
context.Dispose(context->GetIsolate());
|
||||
}
|
||||
|
||||
v8::Handle<v8::Value> DoLoopCancelTerminate(const v8::Arguments& args) {
|
||||
v8::TryCatch try_catch;
|
||||
CHECK(!v8::V8::IsExecutionTerminating());
|
||||
v8::Script::Compile(v8::String::New("var term = true;"
|
||||
"while(true) {"
|
||||
" if (term) terminate();"
|
||||
" term = false;"
|
||||
"}"
|
||||
"fail();"))->Run();
|
||||
CHECK(try_catch.HasCaught());
|
||||
CHECK(try_catch.Exception()->IsNull());
|
||||
CHECK(try_catch.Message().IsEmpty());
|
||||
CHECK(!try_catch.CanContinue());
|
||||
CHECK(v8::V8::IsExecutionTerminating());
|
||||
CHECK(try_catch.HasTerminated());
|
||||
v8::V8::CancelTerminateExecution(v8::Isolate::GetCurrent());
|
||||
CHECK(!v8::V8::IsExecutionTerminating());
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
// Test that a single thread of JavaScript execution can terminate
|
||||
// itself and then resume execution.
|
||||
TEST(TerminateCancelTerminateFromThreadItself) {
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::ObjectTemplate> global =
|
||||
CreateGlobalTemplate(TerminateCurrentThread, DoLoopCancelTerminate);
|
||||
v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
|
||||
v8::Context::Scope context_scope(context);
|
||||
CHECK(!v8::V8::IsExecutionTerminating());
|
||||
v8::Handle<v8::String> source =
|
||||
v8::String::New("try { doloop(); } catch(e) { fail(); } 'completed';");
|
||||
// Check that execution completed with correct return value.
|
||||
CHECK(v8::Script::Compile(source)->Run()->Equals(v8_str("completed")));
|
||||
context.Dispose(context->GetIsolate());
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user