Commit 53adf3b1 authored by yangguo@chromium.org's avatar yangguo@chromium.org

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
parent 788de160
...@@ -9,6 +9,7 @@ ARM Ltd. ...@@ -9,6 +9,7 @@ ARM Ltd.
Hewlett-Packard Development Company, LP Hewlett-Packard Development Company, LP
Igalia, S.L. Igalia, S.L.
Joyent, Inc. Joyent, Inc.
Bloomberg Finance L.P.
Akinori MUSHA <knu@FreeBSD.org> Akinori MUSHA <knu@FreeBSD.org>
Alexander Botero-Lowry <alexbl@FreeBSD.org> Alexander Botero-Lowry <alexbl@FreeBSD.org>
......
...@@ -3601,6 +3601,24 @@ class V8EXPORT V8 { ...@@ -3601,6 +3601,24 @@ class V8EXPORT V8 {
*/ */
static bool IsExecutionTerminating(Isolate* isolate = NULL); 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 * Releases any resources used by v8 and stops any utility threads
* that may be running. Note that disposing v8 is permanent, it * that may be running. Note that disposing v8 is permanent, it
...@@ -3711,20 +3729,29 @@ class V8EXPORT TryCatch { ...@@ -3711,20 +3729,29 @@ class V8EXPORT TryCatch {
bool HasCaught() const; bool HasCaught() const;
/** /**
* For certain types of exceptions, it makes no sense to continue * For certain types of exceptions, it makes no sense to continue execution.
* 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 * If CanContinue returns false, the correct action is to perform any C++
* any C++ cleanup needed and then return. * 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; 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 * Throws the exception caught by this TryCatch in a way that avoids
* it being caught again by this same TryCatch. As with ThrowException * it being caught again by this same TryCatch. As with ThrowException
...@@ -3800,6 +3827,7 @@ class V8EXPORT TryCatch { ...@@ -3800,6 +3827,7 @@ class V8EXPORT TryCatch {
bool can_continue_ : 1; bool can_continue_ : 1;
bool capture_message_ : 1; bool capture_message_ : 1;
bool rethrow_ : 1; bool rethrow_ : 1;
bool has_terminated_ : 1;
friend class v8::internal::Isolate; friend class v8::internal::Isolate;
}; };
......
...@@ -1885,7 +1885,8 @@ v8::TryCatch::TryCatch() ...@@ -1885,7 +1885,8 @@ v8::TryCatch::TryCatch()
is_verbose_(false), is_verbose_(false),
can_continue_(true), can_continue_(true),
capture_message_(true), capture_message_(true),
rethrow_(false) { rethrow_(false),
has_terminated_(false) {
isolate_->RegisterTryCatchHandler(this); isolate_->RegisterTryCatchHandler(this);
} }
...@@ -1913,6 +1914,11 @@ bool v8::TryCatch::CanContinue() const { ...@@ -1913,6 +1914,11 @@ bool v8::TryCatch::CanContinue() const {
} }
bool v8::TryCatch::HasTerminated() const {
return has_terminated_;
}
v8::Handle<v8::Value> v8::TryCatch::ReThrow() { v8::Handle<v8::Value> v8::TryCatch::ReThrow() {
if (!HasCaught()) return v8::Local<v8::Value>(); if (!HasCaught()) return v8::Local<v8::Value>();
rethrow_ = true; rethrow_ = true;
...@@ -5956,6 +5962,12 @@ bool V8::IsExecutionTerminating(Isolate* isolate) { ...@@ -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() { Isolate* Isolate::GetCurrent() {
i::Isolate* isolate = i::Isolate::UncheckedCurrent(); i::Isolate* isolate = i::Isolate::UncheckedCurrent();
return reinterpret_cast<Isolate*>(isolate); return reinterpret_cast<Isolate*>(isolate);
......
...@@ -425,6 +425,13 @@ bool StackGuard::IsTerminateExecution() { ...@@ -425,6 +425,13 @@ bool StackGuard::IsTerminateExecution() {
} }
void StackGuard::CancelTerminateExecution() {
ExecutionAccess access(isolate_);
Continue(TERMINATE);
isolate_->CancelTerminateExecution();
}
void StackGuard::TerminateExecution() { void StackGuard::TerminateExecution() {
ExecutionAccess access(isolate_); ExecutionAccess access(isolate_);
thread_local_.interrupt_flags_ |= TERMINATE; thread_local_.interrupt_flags_ |= TERMINATE;
......
...@@ -189,6 +189,7 @@ class StackGuard { ...@@ -189,6 +189,7 @@ class StackGuard {
void Interrupt(); void Interrupt();
bool IsTerminateExecution(); bool IsTerminateExecution();
void TerminateExecution(); void TerminateExecution();
void CancelTerminateExecution();
#ifdef ENABLE_DEBUGGER_SUPPORT #ifdef ENABLE_DEBUGGER_SUPPORT
bool IsDebugBreak(); bool IsDebugBreak();
void DebugBreak(); void DebugBreak();
......
...@@ -1056,6 +1056,23 @@ Failure* Isolate::TerminateExecution() { ...@@ -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) { Failure* Isolate::Throw(Object* exception, MessageLocation* location) {
DoThrow(exception, location); DoThrow(exception, location);
return Failure::Exception(); return Failure::Exception();
...@@ -1960,12 +1977,14 @@ void Isolate::PropagatePendingExceptionToExternalTryCatch() { ...@@ -1960,12 +1977,14 @@ void Isolate::PropagatePendingExceptionToExternalTryCatch() {
} else if (thread_local_top_.pending_exception_ == } else if (thread_local_top_.pending_exception_ ==
heap()->termination_exception()) { heap()->termination_exception()) {
try_catch_handler()->can_continue_ = false; try_catch_handler()->can_continue_ = false;
try_catch_handler()->has_terminated_ = true;
try_catch_handler()->exception_ = heap()->null_value(); try_catch_handler()->exception_ = heap()->null_value();
} else { } else {
// At this point all non-object (failure) exceptions have // At this point all non-object (failure) exceptions have
// been dealt with so this shouldn't fail. // been dealt with so this shouldn't fail.
ASSERT(!pending_exception()->IsFailure()); ASSERT(!pending_exception()->IsFailure());
try_catch_handler()->can_continue_ = true; try_catch_handler()->can_continue_ = true;
try_catch_handler()->has_terminated_ = false;
try_catch_handler()->exception_ = pending_exception(); try_catch_handler()->exception_ = pending_exception();
if (!thread_local_top_.pending_message_obj_->IsTheHole()) { if (!thread_local_top_.pending_message_obj_->IsTheHole()) {
try_catch_handler()->message_ = thread_local_top_.pending_message_obj_; try_catch_handler()->message_ = thread_local_top_.pending_message_obj_;
......
...@@ -779,6 +779,7 @@ class Isolate { ...@@ -779,6 +779,7 @@ class Isolate {
// Out of resource exception helpers. // Out of resource exception helpers.
Failure* StackOverflow(); Failure* StackOverflow();
Failure* TerminateExecution(); Failure* TerminateExecution();
void CancelTerminateExecution();
// Administration // Administration
void Iterate(ObjectVisitor* v); void Iterate(ObjectVisitor* v);
......
...@@ -372,3 +372,40 @@ TEST(TerminateAndReenterFromThreadItself) { ...@@ -372,3 +372,40 @@ TEST(TerminateAndReenterFromThreadItself) {
"f()"))->Run()->IsTrue()); "f()"))->Run()->IsTrue());
context.Dispose(context->GetIsolate()); 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());
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment