Revision: 2723
Author: [email protected]
Date: Wed Aug 19 08:14:11 2009
Log: Add support for forceful termination of JavaScript execution.

The termination is achieved by throwing an exception that is uncatchable by  
JavaScript exception handlers.
Review URL: http://codereview.chromium.org/174056
http://code.google.com/p/v8/source/detail?r=2723

Added:
  /branches/bleeding_edge/test/cctest/test-thread-termination.cc
Modified:
  /branches/bleeding_edge/include/v8.h
  /branches/bleeding_edge/src/api.cc
  /branches/bleeding_edge/src/arm/codegen-arm.cc
  /branches/bleeding_edge/src/codegen.h
  /branches/bleeding_edge/src/execution.cc
  /branches/bleeding_edge/src/execution.h
  /branches/bleeding_edge/src/heap.cc
  /branches/bleeding_edge/src/heap.h
  /branches/bleeding_edge/src/ia32/codegen-ia32.cc
  /branches/bleeding_edge/src/messages.cc
  /branches/bleeding_edge/src/objects-debug.cc
  /branches/bleeding_edge/src/objects-inl.h
  /branches/bleeding_edge/src/top.cc
  /branches/bleeding_edge/src/top.h
  /branches/bleeding_edge/src/v8threads.cc
  /branches/bleeding_edge/src/v8threads.h
  /branches/bleeding_edge/src/x64/codegen-x64.cc
  /branches/bleeding_edge/test/cctest/SConscript
  /branches/bleeding_edge/test/cctest/test-threads.cc

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/cctest/test-thread-termination.cc      Wed Aug 
 
19 08:14:11 2009
@@ -0,0 +1,195 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+#include "platform.h"
+#include "cctest.h"
+
+
+v8::internal::Semaphore* semaphore = NULL;
+
+
+v8::Handle<v8::Value> Signal(const v8::Arguments& args) {
+  semaphore->Signal();
+  return v8::Undefined();
+}
+
+
+v8::Handle<v8::Value> TerminateCurrentThread(const v8::Arguments& args) {
+  v8::V8::TerminateExecution();
+  return v8::Undefined();
+}
+
+
+v8::Handle<v8::Value> Fail(const v8::Arguments& args) {
+  CHECK(false);
+  return v8::Undefined();
+}
+
+
+v8::Handle<v8::Value> Loop(const v8::Arguments& args) {
+  v8::Handle<v8::String> source =
+      v8::String::New("try { doloop(); fail(); } catch(e) { fail(); }");
+  v8::Script::Compile(source)->Run();
+  return v8::Undefined();
+}
+
+
+v8::Handle<v8::Value> DoLoop(const v8::Arguments& args) {
+  v8::TryCatch try_catch;
+  v8::Script::Compile(v8::String::New("function f() {"
+                                      "  var term = true;"
+                                      "  try {"
+                                      "    while(true) {"
+                                      "      if (term) terminate();"
+                                      "      term = false;"
+                                      "    }"
+                                      "    fail();"
+                                      "  } catch(e) {"
+                                      "    fail();"
+                                      "  }"
+                                      "}"
+                                      "f()"))->Run();
+  CHECK(try_catch.HasCaught());
+  CHECK(try_catch.Exception()->IsNull());
+  CHECK(try_catch.Message().IsEmpty());
+  CHECK(!try_catch.CanContinue());
+  return v8::Undefined();
+}
+
+
+v8::Handle<v8::ObjectTemplate> CreateGlobalTemplate(
+    v8::InvocationCallback terminate) {
+  v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
+  global->Set(v8::String::New("terminate"),
+              v8::FunctionTemplate::New(terminate));
+  global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
+  global->Set(v8::String::New("loop"), v8::FunctionTemplate::New(Loop));
+  global->Set(v8::String::New("doloop"),  
v8::FunctionTemplate::New(DoLoop));
+  return global;
+}
+
+
+// Test that a single thread of JavaScript execution can terminate
+// itself.
+TEST(TerminateOnlyV8ThreadFromThreadItself) {
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> global =
+      CreateGlobalTemplate(TerminateCurrentThread);
+  v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
+  v8::Context::Scope context_scope(context);
+  // Run a loop that will be infinite if thread termination does not work.
+  v8::Handle<v8::String> source =
+      v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
+  v8::Script::Compile(source)->Run();
+  // Test that we can run the code again after thread termination.
+  v8::Script::Compile(source)->Run();
+  context.Dispose();
+}
+
+
+class TerminatorThread : public v8::internal::Thread {
+  void Run() {
+    semaphore->Wait();
+    v8::V8::TerminateExecution();
+  }
+};
+
+
+// Test that a single thread of JavaScript execution can be terminated
+// from the side by another thread.
+TEST(TerminateOnlyV8ThreadFromOtherThread) {
+  semaphore = v8::internal::OS::CreateSemaphore(0);
+  TerminatorThread thread;
+  thread.Start();
+
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal);
+  v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
+  v8::Context::Scope context_scope(context);
+  // Run a loop that will be infinite if thread termination does not work.
+  v8::Handle<v8::String> source =
+      v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
+  v8::Script::Compile(source)->Run();
+
+  thread.Join();
+  delete semaphore;
+  semaphore = NULL;
+  context.Dispose();
+}
+
+
+class LoopingThread : public v8::internal::Thread {
+ public:
+  void Run() {
+    v8::Locker locker;
+    v8::HandleScope scope;
+    v8_thread_id_ = v8::V8::GetCurrentThreadId();
+    v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal);
+    v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
+    v8::Context::Scope context_scope(context);
+    // Run a loop that will be infinite if thread termination does not  
work.
+    v8::Handle<v8::String> source =
+        v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
+    v8::Script::Compile(source)->Run();
+    context.Dispose();
+  }
+
+  int GetV8ThreadId() { return v8_thread_id_; }
+
+ private:
+  int v8_thread_id_;
+};
+
+
+// Test that multiple threads using V8 can be terminated from another
+// thread when using Lockers and preemption.
+TEST(TerminateMultipleV8Threads) {
+  {
+    v8::Locker locker;
+    v8::V8::Initialize();
+    v8::Locker::StartPreemption(1);
+    semaphore = v8::internal::OS::CreateSemaphore(0);
+  }
+  LoopingThread thread1;
+  thread1.Start();
+  LoopingThread thread2;
+  thread2.Start();
+  // Wait until both threads have signaled the semaphore.
+  semaphore->Wait();
+  semaphore->Wait();
+  {
+    v8::Locker locker;
+    v8::V8::TerminateExecution(thread1.GetV8ThreadId());
+    v8::V8::TerminateExecution(thread2.GetV8ThreadId());
+  }
+  thread1.Join();
+  thread2.Join();
+
+  delete semaphore;
+  semaphore = NULL;
+}
=======================================
--- /branches/bleeding_edge/include/v8.h        Mon Aug 17 06:34:41 2009
+++ /branches/bleeding_edge/include/v8.h        Wed Aug 19 08:14:11 2009
@@ -2223,6 +2223,47 @@
     */
    static int GetLogLines(int from_pos, char* dest_buf, int max_size);

+  /**
+   * Retrieve the V8 thread id of the calling thread.
+   *
+   * The thread id for a thread should only be retrieved after the V8
+   * lock has been acquired with a Locker object with that thread.
+   */
+  static int GetCurrentThreadId();
+
+  /**
+   * Forcefully terminate execution of a JavaScript thread.  This can
+   * be used to terminate long-running scripts.
+   *
+   * TerminateExecution should only be called when then V8 lock has
+   * been acquired with a Locker object.  Therefore, in order to be
+   * able to terminate long-running threads, preemption must be
+   * enabled to allow the user of TerminateExecution to acquire the
+   * lock.
+   *
+   * The termination is achieved by throwing an exception that is
+   * uncatchable by JavaScript exception handlers.  Termination
+   * exceptions act as if they were caught by a C++ TryCatch exception
+   * handlers.  If forceful termination is used, any C++ TryCatch
+   * exception handler that catches an exception should check if that
+   * exception is a termination exception and immediately return if
+   * that is the case.  Returning immediately in that case will
+   * continue the propagation of the termination exception if needed.
+   *
+   * The thread id passed to TerminateExecution must have been
+   * obtained by calling GetCurrentThreadId on the thread in question.
+   *
+   * \param thread_id The thread id of the thread to terminate.
+   */
+  static void TerminateExecution(int thread_id);
+
+  /**
+   * Forcefully terminate the current thread of JavaScript execution.
+   *
+   * This method can be used by any thread even if that thread has not
+   * acquired the V8 lock with a Locker object.
+   */
+  static void TerminateExecution();

    /**
     * Releases any resources used by v8 and stops any utility threads
@@ -2281,6 +2322,21 @@
     */
    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.
+   *
+   * If CanContinue returns false, the correct action is to perform
+   * any C++ cleanup needed and then return.
+   */
+  bool CanContinue() const;
+
    /**
     * Returns the exception caught by this try/catch block.  If no  
exception has
     * been caught an empty handle is returned.
@@ -2337,6 +2393,7 @@
    void* exception_;
    void* message_;
    bool is_verbose_;
+  bool can_continue_;
    bool capture_message_;
    void* js_handler_;
  };
=======================================
--- /branches/bleeding_edge/src/api.cc  Tue Aug 18 00:14:02 2009
+++ /branches/bleeding_edge/src/api.cc  Wed Aug 19 08:14:11 2009
@@ -75,7 +75,7 @@
             
i::V8::FatalProcessOutOfMemory(NULL);                                \
         
}                                                                        \
        bool call_depth_is_zero =  
thread_local.CallDepthIsZero();                \
-       
i::Top::optional_reschedule_exception(call_depth_is_zero);               \
+      i::Top::OptionalRescheduleException(call_depth_is_zero,  
false);        \
        return  
value;                                                            \
       
}                                                                          \
    } while (false)
@@ -1206,6 +1206,11 @@
  bool v8::TryCatch::HasCaught() const {
    return !reinterpret_cast<i::Object*>(exception_)->IsTheHole();
  }
+
+
+bool v8::TryCatch::CanContinue() const {
+  return can_continue_;
+}


  v8::Local<Value> v8::TryCatch::Exception() const {
@@ -3353,6 +3358,34 @@
  #endif
    return 0;
  }
+
+
+int V8::GetCurrentThreadId() {
+  API_ENTRY_CHECK("V8::GetCurrentThreadId()");
+  EnsureInitialized("V8::GetCurrentThreadId()");
+  return i::Top::thread_id();
+}
+
+
+void V8::TerminateExecution(int thread_id) {
+  if (!i::V8::IsRunning()) return;
+  API_ENTRY_CHECK("V8::GetCurrentThreadId()");
+  // If the thread_id identifies the current thread just terminate
+  // execution right away.  Otherwise, ask the thread manager to
+  // terminate the thread with the given id if any.
+  if (thread_id == i::Top::thread_id()) {
+    i::StackGuard::TerminateExecution();
+  } else {
+    i::ThreadManager::TerminateExecution(thread_id);
+  }
+}
+
+
+void V8::TerminateExecution() {
+  if (!i::V8::IsRunning()) return;
+  i::StackGuard::TerminateExecution();
+}
+

  String::Utf8Value::Utf8Value(v8::Handle<v8::Value> obj) {
    EnsureInitialized("v8::String::Utf8Value::Utf8Value()");
=======================================
--- /branches/bleeding_edge/src/arm/codegen-arm.cc      Tue Aug 18 03:52:14 2009
+++ /branches/bleeding_edge/src/arm/codegen-arm.cc      Wed Aug 19 08:14:11 2009
@@ -5701,7 +5701,8 @@
  }


-void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) {
+void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
+                                          UncatchableExceptionType type) {
    // Adjust this code if not the case.
    ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);

@@ -5725,20 +5726,22 @@

    // Set the top handler address to next handler past the current ENTRY  
handler.
    ASSERT(StackHandlerConstants::kNextOffset == 0);
-  __ pop(r0);
-  __ str(r0, MemOperand(r3));
-
-  // Set external caught exception to false.
-  ExternalReference  
external_caught(Top::k_external_caught_exception_address);
-  __ mov(r0, Operand(false));
-  __ mov(r2, Operand(external_caught));
-  __ str(r0, MemOperand(r2));
-
-  // Set pending exception and r0 to out of memory exception.
-  Failure* out_of_memory = Failure::OutOfMemoryException();
-  __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
-  __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address)));
-  __ str(r0, MemOperand(r2));
+  __ pop(r2);
+  __ str(r2, MemOperand(r3));
+
+  if (type == OUT_OF_MEMORY) {
+    // Set external caught exception to false.
+    ExternalReference  
external_caught(Top::k_external_caught_exception_address);
+    __ mov(r0, Operand(false));
+    __ mov(r2, Operand(external_caught));
+    __ str(r0, MemOperand(r2));
+
+    // Set pending exception and r0 to out of memory exception.
+    Failure* out_of_memory = Failure::OutOfMemoryException();
+    __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
+    __ mov(r2,  
Operand(ExternalReference(Top::k_pending_exception_address)));
+    __ str(r0, MemOperand(r2));
+  }

    // Stack layout at this point. See also StackHandlerConstants.
    // sp ->   state (ENTRY)
@@ -5768,6 +5771,7 @@

  void CEntryStub::GenerateCore(MacroAssembler* masm,
                                Label* throw_normal_exception,
+                              Label* throw_termination_exception,
                                Label* throw_out_of_memory_exception,
                                StackFrame::Type frame_type,
                                bool do_gc,
@@ -5838,10 +5842,10 @@
    __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
    __ b(eq, &retry);

-  Label continue_exception;
-  // If the returned failure is EXCEPTION then promote  
Top::pending_exception().
-  __ cmp(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception())));
-  __ b(ne, &continue_exception);
+  // Special handling of out of memory exceptions.
+  Failure* out_of_memory = Failure::OutOfMemoryException();
+  __ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
+  __ b(eq, throw_out_of_memory_exception);

    // Retrieve the pending exception and clear the variable.
    __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
@@ -5850,11 +5854,10 @@
    __ ldr(r0, MemOperand(ip));
    __ str(r3, MemOperand(ip));

-  __ bind(&continue_exception);
-  // Special handling of out of memory exception.
-  Failure* out_of_memory = Failure::OutOfMemoryException();
-  __ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
-  __ b(eq, throw_out_of_memory_exception);
+  // Special handling of termination exceptions which are uncatchable
+  // by javascript code.
+  __ cmp(r0, Operand(Factory::termination_exception()));
+  __ b(eq, throw_termination_exception);

    // Handle normal exception.
    __ jmp(throw_normal_exception);
@@ -5887,11 +5890,14 @@
    // r5: pointer to builtin function (C callee-saved)
    // r6: pointer to first argument (C callee-saved)

-  Label throw_out_of_memory_exception;
    Label throw_normal_exception;
+  Label throw_termination_exception;
+  Label throw_out_of_memory_exception;

    // Call into the runtime system.
-  GenerateCore(masm, &throw_normal_exception,
+  GenerateCore(masm,
+               &throw_normal_exception,
+               &throw_termination_exception,
                 &throw_out_of_memory_exception,
                 frame_type,
                 false,
@@ -5900,6 +5906,7 @@
    // Do space-specific GC and retry runtime call.
    GenerateCore(masm,
                 &throw_normal_exception,
+               &throw_termination_exception,
                 &throw_out_of_memory_exception,
                 frame_type,
                 true,
@@ -5910,14 +5917,17 @@
    __ mov(r0, Operand(reinterpret_cast<int32_t>(failure)));
    GenerateCore(masm,
                 &throw_normal_exception,
+               &throw_termination_exception,
                 &throw_out_of_memory_exception,
                 frame_type,
                 true,
                 true);

    __ bind(&throw_out_of_memory_exception);
-  GenerateThrowOutOfMemory(masm);
-  // control flow for generated will not return.
+  GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
+
+  __ bind(&throw_termination_exception);
+  GenerateThrowUncatchable(masm, TERMINATION);

    __ bind(&throw_normal_exception);
    GenerateThrowTOS(masm);
=======================================
--- /branches/bleeding_edge/src/codegen.h       Mon Jun 29 10:07:30 2009
+++ /branches/bleeding_edge/src/codegen.h       Wed Aug 19 08:14:11 2009
@@ -70,6 +70,9 @@
  // Mode to overwrite BinaryExpression values.
  enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };

+// Types of uncatchable exceptions.
+enum UncatchableExceptionType { OUT_OF_MEMORY, TERMINATION };
+

  #if V8_TARGET_ARCH_IA32
  #include "ia32/codegen-ia32.h"
@@ -291,12 +294,14 @@
    void GenerateBody(MacroAssembler* masm, bool is_debug_break);
    void GenerateCore(MacroAssembler* masm,
                      Label* throw_normal_exception,
+                    Label* throw_termination_exception,
                      Label* throw_out_of_memory_exception,
                      StackFrame::Type frame_type,
                      bool do_gc,
                      bool always_allocate_scope);
    void GenerateThrowTOS(MacroAssembler* masm);
-  void GenerateThrowOutOfMemory(MacroAssembler* masm);
+  void GenerateThrowUncatchable(MacroAssembler* masm,
+                                UncatchableExceptionType type);

   private:
    Major MajorKey() { return CEntry; }
=======================================
--- /branches/bleeding_edge/src/execution.cc    Mon Aug 17 03:19:00 2009
+++ /branches/bleeding_edge/src/execution.cc    Wed Aug 19 08:14:11 2009
@@ -156,7 +156,8 @@
      ASSERT(catcher.HasCaught());
      ASSERT(Top::has_pending_exception());
      ASSERT(Top::external_caught_exception());
-    Top::optional_reschedule_exception(true);
+    bool is_bottom_call =  
HandleScopeImplementer::instance()->CallDepthIsZero();
+    Top::OptionalRescheduleException(is_bottom_call, true);
      result = v8::Utils::OpenHandle(*catcher.Exception());
    }

@@ -326,6 +327,19 @@
    thread_local_.interrupt_flags_ |= PREEMPT;
    set_limits(kInterruptLimit, access);
  }
+
+
+bool StackGuard::IsTerminateExecution() {
+  ExecutionAccess access;
+  return thread_local_.interrupt_flags_ & TERMINATE;
+}
+
+
+void StackGuard::TerminateExecution() {
+  ExecutionAccess access;
+  thread_local_.interrupt_flags_ |= TERMINATE;
+  set_limits(kInterruptLimit, access);
+}


  #ifdef ENABLE_DEBUGGER_SUPPORT
@@ -638,6 +652,10 @@
    }
  #endif
    if (StackGuard::IsPreempted()) RuntimePreempt();
+  if (StackGuard::IsTerminateExecution()) {
+    StackGuard::Continue(TERMINATE);
+    return Top::TerminateExecution();
+  }
    if (StackGuard::IsInterrupted()) {
      // interrupt
      StackGuard::Continue(INTERRUPT);
=======================================
--- /branches/bleeding_edge/src/execution.h     Wed Aug 19 03:18:30 2009
+++ /branches/bleeding_edge/src/execution.h     Wed Aug 19 08:14:11 2009
@@ -37,7 +37,8 @@
    INTERRUPT = 1 << 0,
    DEBUGBREAK = 1 << 1,
    DEBUGCOMMAND = 1 << 2,
-  PREEMPT = 1 << 3
+  PREEMPT = 1 << 3,
+  TERMINATE = 1 << 4
  };

  class Execution : public AllStatic {
@@ -164,13 +165,15 @@
    static void Preempt();
    static bool IsInterrupted();
    static void Interrupt();
-  static void Continue(InterruptFlag after_what);
+  static bool IsTerminateExecution();
+  static void TerminateExecution();
  #ifdef ENABLE_DEBUGGER_SUPPORT
-  static void DebugBreak();
-  static void DebugCommand();
    static bool IsDebugBreak();
+  static void DebugBreak();
    static bool IsDebugCommand();
+  static void DebugCommand();
  #endif
+  static void Continue(InterruptFlag after_what);

   private:
    // You should hold the ExecutionAccess lock when calling this method.
=======================================
--- /branches/bleeding_edge/src/heap.cc Wed Aug 19 03:36:19 2009
+++ /branches/bleeding_edge/src/heap.cc Wed Aug 19 08:14:11 2009
@@ -1412,6 +1412,9 @@
    if (obj->IsFailure()) return false;
    set_no_interceptor_result_sentinel(obj);

+  obj = CreateOddball(oddball_map(), "termination_exception",  
Smi::FromInt(-3));
+  if (obj->IsFailure()) return false;
+  set_termination_exception(obj);

    // Allocate the empty string.
    obj = AllocateRawAsciiString(0, TENURED);
=======================================
--- /branches/bleeding_edge/src/heap.h  Tue Aug 18 04:26:14 2009
+++ /branches/bleeding_edge/src/heap.h  Wed Aug 19 08:14:11 2009
@@ -111,6 +111,7 @@
    V(Object, nan_value,  
NanValue)                                               \
    V(Object, undefined_value,  
UndefinedValue)                                   \
    V(Object, no_interceptor_result_sentinel,  
NoInterceptorResultSentinel)       \
+  V(Object, termination_exception,  
TerminationException)                       \
    V(Object, minus_zero_value,  
MinusZeroValue)                                  \
    V(Object, null_value,  
NullValue)                                             \
    V(Object, true_value,  
TrueValue)                                             \
=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.cc    Tue Aug 18 03:52:14  
2009
+++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc    Wed Aug 19 08:14:11  
2009
@@ -7505,6 +7505,7 @@

  void CEntryStub::GenerateCore(MacroAssembler* masm,
                                Label* throw_normal_exception,
+                              Label* throw_termination_exception,
                                Label* throw_out_of_memory_exception,
                                StackFrame::Type frame_type,
                                bool do_gc,
@@ -7568,10 +7569,9 @@
    __ test(eax, Immediate(((1 << kFailureTypeTagSize) - 1) <<  
kFailureTagSize));
    __ j(zero, &retry, taken);

-  Label continue_exception;
-  // If the returned failure is EXCEPTION then promote  
Top::pending_exception().
-  __ cmp(eax, reinterpret_cast<int32_t>(Failure::Exception()));
-  __ j(not_equal, &continue_exception);
+  // Special handling of out of memory exceptions.
+  __ cmp(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException()));
+  __ j(equal, throw_out_of_memory_exception);

    // Retrieve the pending exception and clear the variable.
    ExternalReference  
pending_exception_address(Top::k_pending_exception_address);
@@ -7580,10 +7580,10 @@
            
Operand::StaticVariable(ExternalReference::the_hole_value_location()));
    __ mov(Operand::StaticVariable(pending_exception_address), edx);

-  __ bind(&continue_exception);
-  // Special handling of out of memory exception.
-  __ cmp(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException()));
-  __ j(equal, throw_out_of_memory_exception);
+  // Special handling of termination exceptions which are uncatchable
+  // by javascript code.
+  __ cmp(eax, Factory::termination_exception());
+  __ j(equal, throw_termination_exception);

    // Handle normal exception.
    __ jmp(throw_normal_exception);
@@ -7593,7 +7593,8 @@
  }


-void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) {
+void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
+                                          UncatchableExceptionType type) {
    // Adjust this code if not the case.
    ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);

@@ -7618,17 +7619,19 @@
    ASSERT(StackHandlerConstants::kNextOffset == 0);
    __ pop(Operand::StaticVariable(handler_address));

-  // Set external caught exception to false.
-  ExternalReference  
external_caught(Top::k_external_caught_exception_address);
-  __ mov(eax, false);
-  __ mov(Operand::StaticVariable(external_caught), eax);
-
-  // Set pending exception and eax to out of memory exception.
-  ExternalReference pending_exception(Top::k_pending_exception_address);
-  __ mov(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException()));
-  __ mov(Operand::StaticVariable(pending_exception), eax);
-
-  // Clear the context pointer;
+  if (type == OUT_OF_MEMORY) {
+    // Set external caught exception to false.
+    ExternalReference  
external_caught(Top::k_external_caught_exception_address);
+    __ mov(eax, false);
+    __ mov(Operand::StaticVariable(external_caught), eax);
+
+    // Set pending exception and eax to out of memory exception.
+    ExternalReference pending_exception(Top::k_pending_exception_address);
+    __ mov(eax,  
reinterpret_cast<int32_t>(Failure::OutOfMemoryException()));
+    __ mov(Operand::StaticVariable(pending_exception), eax);
+  }
+
+  // Clear the context pointer.
    __ xor_(esi, Operand(esi));

    // Restore fp from handler and discard handler state.
@@ -7667,11 +7670,14 @@
    // edi: number of arguments including receiver (C callee-saved)
    // esi: argv pointer (C callee-saved)

-  Label throw_out_of_memory_exception;
    Label throw_normal_exception;
+  Label throw_termination_exception;
+  Label throw_out_of_memory_exception;

    // Call into the runtime system.
-  GenerateCore(masm, &throw_normal_exception,
+  GenerateCore(masm,
+               &throw_normal_exception,
+               &throw_termination_exception,
                 &throw_out_of_memory_exception,
                 frame_type,
                 false,
@@ -7680,6 +7686,7 @@
    // Do space-specific GC and retry runtime call.
    GenerateCore(masm,
                 &throw_normal_exception,
+               &throw_termination_exception,
                 &throw_out_of_memory_exception,
                 frame_type,
                 true,
@@ -7690,14 +7697,17 @@
    __ mov(eax, Immediate(reinterpret_cast<int32_t>(failure)));
    GenerateCore(masm,
                 &throw_normal_exception,
+               &throw_termination_exception,
                 &throw_out_of_memory_exception,
                 frame_type,
                 true,
                 true);

    __ bind(&throw_out_of_memory_exception);
-  GenerateThrowOutOfMemory(masm);
-  // control flow for generated will not return.
+  GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
+
+  __ bind(&throw_termination_exception);
+  GenerateThrowUncatchable(masm, TERMINATION);

    __ bind(&throw_normal_exception);
    GenerateThrowTOS(masm);
=======================================
--- /branches/bleeding_edge/src/messages.cc     Mon May 25 03:05:56 2009
+++ /branches/bleeding_edge/src/messages.cc     Wed Aug 19 08:14:11 2009
@@ -147,14 +147,12 @@
    Handle<String> fmt_str = Factory::LookupAsciiSymbol("FormatMessage");
    Handle<JSFunction> fun =
        Handle<JSFunction>(
-          JSFunction::cast(
-              Top::builtins()->GetProperty(*fmt_str)));
+          JSFunction::cast(Top::builtins()->GetProperty(*fmt_str)));
    Object** argv[1] = { data.location() };

    bool caught_exception;
    Handle<Object> result =
-      Execution::TryCall(fun, Top::builtins(), 1, argv,
-                         &caught_exception);
+      Execution::TryCall(fun, Top::builtins(), 1, argv, &caught_exception);

    if (caught_exception || !result->IsString()) {
      return Factory::LookupAsciiSymbol("<error>");
=======================================
--- /branches/bleeding_edge/src/objects-debug.cc        Wed Aug 19 00:30:20 2009
+++ /branches/bleeding_edge/src/objects-debug.cc        Wed Aug 19 08:14:11 2009
@@ -705,7 +705,8 @@
    } else {
      ASSERT(number->IsSmi());
      int value = Smi::cast(number)->value();
-    ASSERT(value == 0 || value == 1 || value == -1 || value == -2);
+    ASSERT(value == 0 || value == 1 || value == -1 ||
+           value == -2 || value == -3);
    }
  }

=======================================
--- /branches/bleeding_edge/src/objects-inl.h   Wed Aug 19 00:30:20 2009
+++ /branches/bleeding_edge/src/objects-inl.h   Wed Aug 19 08:14:11 2009
@@ -787,6 +787,7 @@
  Failure* Failure::Exception() {
    return Construct(EXCEPTION);
  }
+

  Failure* Failure::OutOfMemoryException() {
    return Construct(OUT_OF_MEMORY_EXCEPTION);
=======================================
--- /branches/bleeding_edge/src/top.cc  Fri Jun 26 06:09:50 2009
+++ /branches/bleeding_edge/src/top.cc  Wed Aug 19 08:14:11 2009
@@ -98,6 +98,7 @@
    thread_local_.stack_is_cooked_ = false;
    thread_local_.try_catch_handler_ = NULL;
    thread_local_.context_ = NULL;
+  thread_local_.thread_id_ = ThreadManager::kInvalidId;
    thread_local_.external_caught_exception_ = false;
    thread_local_.failed_access_check_callback_ = NULL;
    clear_pending_exception();
@@ -596,6 +597,12 @@
    DoThrow(*exception, NULL, kStackOverflowMessage);
    return Failure::Exception();
  }
+
+
+Failure* Top::TerminateExecution() {
+  DoThrow(Heap::termination_exception(), NULL, NULL);
+  return Failure::Exception();
+}


  Failure* Top::Throw(Object* exception, MessageLocation* location) {
@@ -694,7 +701,8 @@
  }


-bool Top::ShouldReportException(bool* is_caught_externally) {
+bool Top::ShouldReturnException(bool* is_caught_externally,
+                                bool catchable_by_javascript) {
    // Find the top-most try-catch handler.
    StackHandler* handler =
        StackHandler::FromAddress(Top::handler(Top::GetCurrentThread()));
@@ -712,7 +720,8 @@
    //
    // See comments in RegisterTryCatchHandler for details.
    *is_caught_externally = try_catch != NULL &&
-      (handler == NULL || handler == try_catch->js_handler_);
+      (handler == NULL || handler == try_catch->js_handler_ ||
+       !catchable_by_javascript);

    if (*is_caught_externally) {
      // Only report the exception if the external handler is verbose.
@@ -735,12 +744,17 @@
    // Determine reporting and whether the exception is caught externally.
    bool is_caught_externally = false;
    bool is_out_of_memory = exception == Failure::OutOfMemoryException();
-  bool should_return_exception =  
ShouldReportException(&is_caught_externally);
-  bool report_exception = !is_out_of_memory && should_return_exception;
+  bool is_termination_exception = exception ==  
Heap::termination_exception();
+  bool catchable_by_javascript = !is_termination_exception  
&& !is_out_of_memory;
+  bool should_return_exception =
+      ShouldReturnException(&is_caught_externally,  
catchable_by_javascript);
+  bool report_exception = catchable_by_javascript &&  
should_return_exception;

  #ifdef ENABLE_DEBUGGER_SUPPORT
    // Notify debugger of exception.
-  Debugger::OnException(exception_handle, report_exception);
+  if (catchable_by_javascript) {
+    Debugger::OnException(exception_handle, report_exception);
+  }
  #endif

    // Generate the message.
@@ -791,14 +805,21 @@
    // the global context.  Note: We have to mark the global context here
    // since the GenerateThrowOutOfMemory stub cannot make a RuntimeCall to
    // set it.
+  bool external_caught = thread_local_.external_caught_exception_;
    HandleScope scope;
    if (thread_local_.pending_exception_ == Failure::OutOfMemoryException())  
{
      context()->mark_out_of_memory();
+  } else if (thread_local_.pending_exception_ ==
+             Heap::termination_exception()) {
+    if (external_caught) {
+      thread_local_.try_catch_handler_->can_continue_ = false;
+      thread_local_.try_catch_handler_->exception_ = Heap::null_value();
+    }
    } else {
      Handle<Object> exception(pending_exception());
-    bool external_caught = thread_local_.external_caught_exception_;
      thread_local_.external_caught_exception_ = false;
      if (external_caught) {
+      thread_local_.try_catch_handler_->can_continue_ = true;
        thread_local_.try_catch_handler_->exception_ =
          thread_local_.pending_exception_;
        if (!thread_local_.pending_message_obj_->IsTheHole()) {
@@ -834,16 +855,30 @@
  }


-bool Top::optional_reschedule_exception(bool is_bottom_call) {
+bool Top::OptionalRescheduleException(bool is_bottom_call,
+                                      bool force_clear_catchable) {
    // Allways reschedule out of memory exceptions.
    if (!is_out_of_memory()) {
-    // Never reschedule the exception if this is the bottom call.
-    bool clear_exception = is_bottom_call;
-
-    // If the exception is externally caught, clear it if there are no
-    // JavaScript frames on the way to the C++ frame that has the
-    // external handler.
-    if (thread_local_.external_caught_exception_) {
+    bool is_termination_exception =
+        pending_exception() == Heap::termination_exception();
+
+    // Do not reschedule the exception if this is the bottom call or
+    // if we are asked to clear catchable exceptions.  Termination
+    // exceptions are not catchable and are only cleared if this is
+    // the bottom call.
+    bool clear_exception = is_bottom_call ||
+        (force_clear_catchable && !is_termination_exception);
+
+    if (is_termination_exception) {
+      thread_local_.external_caught_exception_ = false;
+      if (is_bottom_call) {
+        clear_pending_exception();
+        return false;
+      }
+    } else if (thread_local_.external_caught_exception_) {
+      // If the exception is externally caught, clear it if there are no
+      // JavaScript frames on the way to the C++ frame that has the
+      // external handler.
        ASSERT(thread_local_.try_catch_handler_ != NULL);
        Address external_handler_address =
            reinterpret_cast<Address>(thread_local_.try_catch_handler_);
=======================================
--- /branches/bleeding_edge/src/top.h   Fri Jun 26 06:09:50 2009
+++ /branches/bleeding_edge/src/top.h   Wed Aug 19 08:14:11 2009
@@ -46,6 +46,7 @@
    // The context where the current execution method is created and for  
variable
    // lookups.
    Context* context_;
+  int thread_id_;
    Object* pending_exception_;
    bool has_pending_message_;
    const char* pending_message_;
@@ -117,6 +118,10 @@
    static void set_save_context(SaveContext* save) {
      thread_local_.save_context_ = save;
    }
+
+  // Access to current thread id.
+  static int thread_id() { return thread_local_.thread_id_; }
+  static void set_thread_id(int id) { thread_local_.thread_id_ = id; }

    // Interface to pending exception.
    static Object* pending_exception() {
@@ -152,7 +157,8 @@
    // exceptions.  If an exception was thrown and not handled by an external
    // handler the exception is scheduled to be rethrown when we return to  
running
    // JavaScript code.  If an exception is scheduled true is returned.
-  static bool optional_reschedule_exception(bool is_bottom_call);
+  static bool OptionalRescheduleException(bool is_bottom_call,
+                                          bool force_clear_catchable);

    static bool* external_caught_exception_address() {
      return &thread_local_.external_caught_exception_;
@@ -246,7 +252,8 @@
    static void DoThrow(Object* exception,
                        MessageLocation* location,
                        const char* message);
-  static bool ShouldReportException(bool* is_caught_externally);
+  static bool ShouldReturnException(bool* is_caught_externally,
+                                    bool catchable_by_javascript);
    static void ReportUncaughtException(Handle<Object> exception,
                                        MessageLocation* location,
                                        Handle<String> stack_trace);
@@ -260,6 +267,7 @@

    // Out of resource exception helpers.
    static Failure* StackOverflow();
+  static Failure* TerminateExecution();

    // Administration
    static void Initialize();
=======================================
--- /branches/bleeding_edge/src/v8threads.cc    Wed May 20 01:05:12 2009
+++ /branches/bleeding_edge/src/v8threads.cc    Wed Aug 19 08:14:11 2009
@@ -151,6 +151,10 @@
    from = RegExpStack::RestoreStack(from);
    from = Bootstrapper::RestoreState(from);
    Thread::SetThreadLocal(thread_state_key, NULL);
+  if (state->terminate_on_restore()) {
+    StackGuard::TerminateExecution();
+    state->set_terminate_on_restore(false);
+  }
    state->set_id(kInvalidId);
    state->Unlink();
    state->LinkInto(ThreadState::FREE_LIST);
@@ -188,6 +192,7 @@


  ThreadState::ThreadState() : id_(ThreadManager::kInvalidId),
+                             terminate_on_restore_(false),
                               next_(this), previous_(this) {
  }

@@ -317,7 +322,21 @@

  void ThreadManager::AssignId() {
    if (!Thread::HasThreadLocal(thread_id_key)) {
-    Thread::SetThreadLocalInt(thread_id_key, next_id_++);
+    ASSERT(Locker::IsLocked());
+    int thread_id = next_id_++;
+    Thread::SetThreadLocalInt(thread_id_key, thread_id);
+    Top::set_thread_id(thread_id);
+  }
+}
+
+
+void ThreadManager::TerminateExecution(int thread_id) {
+  for (ThreadState* state = ThreadState::FirstInUse();
+       state != NULL;
+       state = state->Next()) {
+    if (thread_id == state->id()) {
+      state->set_terminate_on_restore(true);
+    }
    }
  }

=======================================
--- /branches/bleeding_edge/src/v8threads.h     Mon May 25 03:05:56 2009
+++ /branches/bleeding_edge/src/v8threads.h     Wed Aug 19 08:14:11 2009
@@ -49,6 +49,12 @@
    // Id of thread.
    void set_id(int id) { id_ = id; }
    int id() { return id_; }
+
+  // Should the thread be terminated when it is restored?
+  bool terminate_on_restore() { return terminate_on_restore_; }
+  void set_terminate_on_restore(bool terminate_on_restore) {
+    terminate_on_restore_ = terminate_on_restore;
+  }

    // Get data area for archiving a thread.
    char* data() { return data_; }
@@ -58,6 +64,7 @@
    void AllocateSpace();

    int id_;
+  bool terminate_on_restore_;
    char* data_;
    ThreadState* next_;
    ThreadState* previous_;
@@ -88,6 +95,8 @@
    static int CurrentId();
    static void AssignId();

+  static void TerminateExecution(int thread_id);
+
    static const int kInvalidId = -1;
   private:
    static void EagerlyArchiveThread();
=======================================
--- /branches/bleeding_edge/src/x64/codegen-x64.cc      Wed Aug 19 03:18:30 2009
+++ /branches/bleeding_edge/src/x64/codegen-x64.cc      Wed Aug 19 08:14:11 2009
@@ -6747,6 +6747,7 @@

  void CEntryStub::GenerateCore(MacroAssembler* masm,
                                Label* throw_normal_exception,
+                              Label* throw_termination_exception,
                                Label* throw_out_of_memory_exception,
                                StackFrame::Type frame_type,
                                bool do_gc,
@@ -6819,11 +6820,10 @@
    __ testl(rax, Immediate(((1 << kFailureTypeTagSize) - 1) <<  
kFailureTagSize));
    __ j(zero, &retry);

-  Label continue_exception;
-  // If the returned failure is EXCEPTION then promote  
Top::pending_exception().
-  __ movq(kScratchRegister, Failure::Exception(), RelocInfo::NONE);
+  // Special handling of out of memory exceptions.
+  __ movq(kScratchRegister, Failure::OutOfMemoryException(),  
RelocInfo::NONE);
    __ cmpq(rax, kScratchRegister);
-  __ j(not_equal, &continue_exception);
+  __ j(equal, throw_out_of_memory_exception);

    // Retrieve the pending exception and clear the variable.
    ExternalReference  
pending_exception_address(Top::k_pending_exception_address);
@@ -6833,11 +6833,10 @@
    __ movq(rdx, Operand(rdx, 0));
    __ movq(Operand(kScratchRegister, 0), rdx);

-  __ bind(&continue_exception);
-  // Special handling of out of memory exception.
-  __ movq(kScratchRegister, Failure::OutOfMemoryException(),  
RelocInfo::NONE);
-  __ cmpq(rax, kScratchRegister);
-  __ j(equal, throw_out_of_memory_exception);
+  // Special handling of termination exceptions which are uncatchable
+  // by javascript code.
+  __ Cmp(rax, Factory::termination_exception());
+  __ j(equal, throw_termination_exception);

    // Handle normal exception.
    __ jmp(throw_normal_exception);
@@ -6847,7 +6846,8 @@
  }


-void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) {
+void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
+                                          UncatchableExceptionType type) {
    // Fetch top stack handler.
    ExternalReference handler_address(Top::k_handler_address);
    __ movq(kScratchRegister, handler_address);
@@ -6857,30 +6857,32 @@
    Label loop, done;
    __ bind(&loop);
    // Load the type of the current stack handler.
-  __ cmpq(Operand(rsp, StackHandlerConstants::kStateOffset),
-         Immediate(StackHandler::ENTRY));
+  const int kStateOffset = StackHandlerConstants::kStateOffset;
+  __ cmpq(Operand(rsp, kStateOffset), Immediate(StackHandler::ENTRY));
    __ j(equal, &done);
    // Fetch the next handler in the list.
-  ASSERT(StackHandlerConstants::kNextOffset == 0);
-  __ pop(rsp);
+  const int kNextOffset = StackHandlerConstants::kNextOffset;
+  __ movq(rsp, Operand(rsp, kNextOffset));
    __ jmp(&loop);
    __ bind(&done);

    // Set the top handler address to next handler past the current ENTRY  
handler.
-  __ pop(rax);
-  __ store_rax(handler_address);
-
-  // Set external caught exception to false.
-  __ movq(rax, Immediate(false));
-  ExternalReference  
external_caught(Top::k_external_caught_exception_address);
-  __ store_rax(external_caught);
-
-  // Set pending exception and rax to out of memory exception.
-  __ movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE);
-  ExternalReference pending_exception(Top::k_pending_exception_address);
-  __ store_rax(pending_exception);
-
-  // Clear the context pointer;
+  __ movq(kScratchRegister, handler_address);
+  __ pop(Operand(kScratchRegister, 0));
+
+  if (type == OUT_OF_MEMORY) {
+    // Set external caught exception to false.
+    ExternalReference  
external_caught(Top::k_external_caught_exception_address);
+    __ movq(rax, Immediate(false));
+    __ store_rax(external_caught);
+
+    // Set pending exception and rax to out of memory exception.
+    ExternalReference pending_exception(Top::k_pending_exception_address);
+    __ movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE);
+    __ store_rax(pending_exception);
+  }
+
+  // Clear the context pointer.
    __ xor_(rsi, rsi);

    // Restore registers from handler.
@@ -6956,12 +6958,14 @@
    // r14: number of arguments including receiver (C callee-saved).
    // r15: argv pointer (C callee-saved).

-  Label throw_out_of_memory_exception;
    Label throw_normal_exception;
+  Label throw_termination_exception;
+  Label throw_out_of_memory_exception;

    // Call into the runtime system.
    GenerateCore(masm,
                 &throw_normal_exception,
+               &throw_termination_exception,
                 &throw_out_of_memory_exception,
                 frame_type,
                 false,
@@ -6970,6 +6974,7 @@
    // Do space-specific GC and retry runtime call.
    GenerateCore(masm,
                 &throw_normal_exception,
+               &throw_termination_exception,
                 &throw_out_of_memory_exception,
                 frame_type,
                 true,
@@ -6980,14 +6985,17 @@
    __ movq(rax, failure, RelocInfo::NONE);
    GenerateCore(masm,
                 &throw_normal_exception,
+               &throw_termination_exception,
                 &throw_out_of_memory_exception,
                 frame_type,
                 true,
                 true);

    __ bind(&throw_out_of_memory_exception);
-  GenerateThrowOutOfMemory(masm);
-  // control flow for generated will not return.
+  GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
+
+  __ bind(&throw_termination_exception);
+  GenerateThrowUncatchable(masm, TERMINATION);

    __ bind(&throw_normal_exception);
    GenerateThrowTOS(masm);
=======================================
--- /branches/bleeding_edge/test/cctest/SConscript      Fri Jul 31 04:07:05 2009
+++ /branches/bleeding_edge/test/cctest/SConscript      Wed Aug 19 08:14:11 2009
@@ -56,6 +56,7 @@
      'test-spaces.cc',
      'test-strings.cc',
      'test-threads.cc',
+    'test-thread-termination.cc',
      'test-utils.cc',
      'test-version.cc'
    ],
=======================================
--- /branches/bleeding_edge/test/cctest/test-threads.cc Wed Dec 17 09:40:02  
2008
+++ /branches/bleeding_edge/test/cctest/test-threads.cc Wed Aug 19 08:14:11  
2009
@@ -50,5 +50,3 @@

    script->Run();
  }
-
-

--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---

Reply via email to