Add methods to allow resuming execution after calling TerminateExecution().
authoryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 22 Apr 2013 15:01:45 +0000 (15:01 +0000)
committeryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 22 Apr 2013 15:01:45 +0000 (15:01 +0000)
R=mvstanton@chromium.org
BUG=v8:2361

Review URL: https://chromiumcodereview.appspot.com/14401008

Patch from Andrew Paprocki <andrew@ishiboo.com>.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14378 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

AUTHORS
include/v8.h
src/api.cc
src/execution.cc
src/execution.h
src/isolate.cc
src/isolate.h
test/cctest/test-thread-termination.cc

diff --git a/AUTHORS b/AUTHORS
index d25fc5a..1a927c4 100644 (file)
--- a/AUTHORS
+++ b/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>
index e1c0203..a6b3ef6 100644 (file)
@@ -3676,6 +3676,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
    * cannot be reinitialized.
@@ -3785,21 +3803,30 @@ class V8EXPORT TryCatch {
   bool HasCaught() const;
 
   /**
-   * 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.
+   * For certain types of exceptions, it makes no sense to continue execution.
    *
-   * 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
    * it is illegal to execute any JavaScript operations after calling
@@ -3874,6 +3901,7 @@ class V8EXPORT TryCatch {
   bool can_continue_ : 1;
   bool capture_message_ : 1;
   bool rethrow_ : 1;
+  bool has_terminated_ : 1;
 
   friend class v8::internal::Isolate;
 };
index 724c843..64928ab 100644 (file)
@@ -1909,7 +1909,8 @@ v8::TryCatch::TryCatch()
       is_verbose_(false),
       can_continue_(true),
       capture_message_(true),
-      rethrow_(false) {
+      rethrow_(false),
+      has_terminated_(false) {
   isolate_->RegisterTryCatchHandler(this);
 }
 
@@ -1937,6 +1938,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;
@@ -6116,6 +6122,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);
index 025a256..c67fbc2 100644 (file)
@@ -426,6 +426,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;
index 9cf8ac6..3cdbf63 100644 (file)
@@ -190,6 +190,7 @@ class StackGuard {
   void Interrupt();
   bool IsTerminateExecution();
   void TerminateExecution();
+  void CancelTerminateExecution();
 #ifdef ENABLE_DEBUGGER_SUPPORT
   bool IsDebugBreak();
   void DebugBreak();
index 5d01664..b5bd955 100644 (file)
@@ -1077,6 +1077,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();
@@ -1990,12 +2007,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_;
index 927ad0e..9e0293d 100644 (file)
@@ -783,6 +783,7 @@ class Isolate {
   // Out of resource exception helpers.
   Failure* StackOverflow();
   Failure* TerminateExecution();
+  void CancelTerminateExecution();
 
   // Administration
   void Iterate(ObjectVisitor* v);
index 190fc7b..b22c150 100644 (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());
+}
+