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:
yangguo@chromium.org 2013-03-21 10:47:34 +00:00
parent 788de160c5
commit 53adf3b19c
8 changed files with 117 additions and 11 deletions

View File

@ -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>

View File

@ -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;
};

View File

@ -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);

View File

@ -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;

View File

@ -189,6 +189,7 @@ class StackGuard {
void Interrupt();
bool IsTerminateExecution();
void TerminateExecution();
void CancelTerminateExecution();
#ifdef ENABLE_DEBUGGER_SUPPORT
bool IsDebugBreak();
void DebugBreak();

View File

@ -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_;

View File

@ -779,6 +779,7 @@ class Isolate {
// Out of resource exception helpers.
Failure* StackOverflow();
Failure* TerminateExecution();
void CancelTerminateExecution();
// Administration
void Iterate(ObjectVisitor* v);

View File

@ -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());
}