Commit 0efbd40b authored by ager@chromium.org's avatar ager@chromium.org

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2723 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 8cc5048a
...@@ -2223,6 +2223,47 @@ class V8EXPORT V8 { ...@@ -2223,6 +2223,47 @@ class V8EXPORT V8 {
*/ */
static int GetLogLines(int from_pos, char* dest_buf, int max_size); 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 * Releases any resources used by v8 and stops any utility threads
...@@ -2281,6 +2322,21 @@ class V8EXPORT TryCatch { ...@@ -2281,6 +2322,21 @@ class V8EXPORT TryCatch {
*/ */
bool HasCaught() const; 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 * Returns the exception caught by this try/catch block. If no exception has
* been caught an empty handle is returned. * been caught an empty handle is returned.
...@@ -2337,6 +2393,7 @@ class V8EXPORT TryCatch { ...@@ -2337,6 +2393,7 @@ class V8EXPORT TryCatch {
void* exception_; void* exception_;
void* message_; void* message_;
bool is_verbose_; bool is_verbose_;
bool can_continue_;
bool capture_message_; bool capture_message_;
void* js_handler_; void* js_handler_;
}; };
......
...@@ -75,7 +75,7 @@ namespace v8 { ...@@ -75,7 +75,7 @@ namespace v8 {
i::V8::FatalProcessOutOfMemory(NULL); \ i::V8::FatalProcessOutOfMemory(NULL); \
} \ } \
bool call_depth_is_zero = thread_local.CallDepthIsZero(); \ 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; \ return value; \
} \ } \
} while (false) } while (false)
...@@ -1208,6 +1208,11 @@ bool v8::TryCatch::HasCaught() const { ...@@ -1208,6 +1208,11 @@ bool v8::TryCatch::HasCaught() const {
} }
bool v8::TryCatch::CanContinue() const {
return can_continue_;
}
v8::Local<Value> v8::TryCatch::Exception() const { v8::Local<Value> v8::TryCatch::Exception() const {
if (HasCaught()) { if (HasCaught()) {
// Check for out of memory exception. // Check for out of memory exception.
...@@ -3354,6 +3359,34 @@ int V8::GetLogLines(int from_pos, char* dest_buf, int max_size) { ...@@ -3354,6 +3359,34 @@ int V8::GetLogLines(int from_pos, char* dest_buf, int max_size) {
return 0; 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) { String::Utf8Value::Utf8Value(v8::Handle<v8::Value> obj) {
EnsureInitialized("v8::String::Utf8Value::Utf8Value()"); EnsureInitialized("v8::String::Utf8Value::Utf8Value()");
if (obj.IsEmpty()) { if (obj.IsEmpty()) {
......
...@@ -5701,7 +5701,8 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { ...@@ -5701,7 +5701,8 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
} }
void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
UncatchableExceptionType type) {
// Adjust this code if not the case. // Adjust this code if not the case.
ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
...@@ -5725,9 +5726,10 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { ...@@ -5725,9 +5726,10 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) {
// Set the top handler address to next handler past the current ENTRY handler. // Set the top handler address to next handler past the current ENTRY handler.
ASSERT(StackHandlerConstants::kNextOffset == 0); ASSERT(StackHandlerConstants::kNextOffset == 0);
__ pop(r0); __ pop(r2);
__ str(r0, MemOperand(r3)); __ str(r2, MemOperand(r3));
if (type == OUT_OF_MEMORY) {
// Set external caught exception to false. // Set external caught exception to false.
ExternalReference external_caught(Top::k_external_caught_exception_address); ExternalReference external_caught(Top::k_external_caught_exception_address);
__ mov(r0, Operand(false)); __ mov(r0, Operand(false));
...@@ -5739,6 +5741,7 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { ...@@ -5739,6 +5741,7 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) {
__ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory))); __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
__ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address))); __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address)));
__ str(r0, MemOperand(r2)); __ str(r0, MemOperand(r2));
}
// Stack layout at this point. See also StackHandlerConstants. // Stack layout at this point. See also StackHandlerConstants.
// sp -> state (ENTRY) // sp -> state (ENTRY)
...@@ -5768,6 +5771,7 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { ...@@ -5768,6 +5771,7 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) {
void CEntryStub::GenerateCore(MacroAssembler* masm, void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_normal_exception, Label* throw_normal_exception,
Label* throw_termination_exception,
Label* throw_out_of_memory_exception, Label* throw_out_of_memory_exception,
StackFrame::Type frame_type, StackFrame::Type frame_type,
bool do_gc, bool do_gc,
...@@ -5838,10 +5842,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, ...@@ -5838,10 +5842,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
__ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize)); __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
__ b(eq, &retry); __ b(eq, &retry);
Label continue_exception; // Special handling of out of memory exceptions.
// If the returned failure is EXCEPTION then promote Top::pending_exception(). Failure* out_of_memory = Failure::OutOfMemoryException();
__ cmp(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception()))); __ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
__ b(ne, &continue_exception); __ b(eq, throw_out_of_memory_exception);
// Retrieve the pending exception and clear the variable. // Retrieve the pending exception and clear the variable.
__ mov(ip, Operand(ExternalReference::the_hole_value_location())); __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
...@@ -5850,11 +5854,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, ...@@ -5850,11 +5854,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
__ ldr(r0, MemOperand(ip)); __ ldr(r0, MemOperand(ip));
__ str(r3, MemOperand(ip)); __ str(r3, MemOperand(ip));
__ bind(&continue_exception); // Special handling of termination exceptions which are uncatchable
// Special handling of out of memory exception. // by javascript code.
Failure* out_of_memory = Failure::OutOfMemoryException(); __ cmp(r0, Operand(Factory::termination_exception()));
__ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory))); __ b(eq, throw_termination_exception);
__ b(eq, throw_out_of_memory_exception);
// Handle normal exception. // Handle normal exception.
__ jmp(throw_normal_exception); __ jmp(throw_normal_exception);
...@@ -5887,11 +5890,14 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { ...@@ -5887,11 +5890,14 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
// r5: pointer to builtin function (C callee-saved) // r5: pointer to builtin function (C callee-saved)
// r6: pointer to first argument (C callee-saved) // r6: pointer to first argument (C callee-saved)
Label throw_out_of_memory_exception;
Label throw_normal_exception; Label throw_normal_exception;
Label throw_termination_exception;
Label throw_out_of_memory_exception;
// Call into the runtime system. // Call into the runtime system.
GenerateCore(masm, &throw_normal_exception, GenerateCore(masm,
&throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception, &throw_out_of_memory_exception,
frame_type, frame_type,
false, false,
...@@ -5900,6 +5906,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { ...@@ -5900,6 +5906,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
// Do space-specific GC and retry runtime call. // Do space-specific GC and retry runtime call.
GenerateCore(masm, GenerateCore(masm,
&throw_normal_exception, &throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception, &throw_out_of_memory_exception,
frame_type, frame_type,
true, true,
...@@ -5910,14 +5917,17 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { ...@@ -5910,14 +5917,17 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
__ mov(r0, Operand(reinterpret_cast<int32_t>(failure))); __ mov(r0, Operand(reinterpret_cast<int32_t>(failure)));
GenerateCore(masm, GenerateCore(masm,
&throw_normal_exception, &throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception, &throw_out_of_memory_exception,
frame_type, frame_type,
true, true,
true); true);
__ bind(&throw_out_of_memory_exception); __ bind(&throw_out_of_memory_exception);
GenerateThrowOutOfMemory(masm); GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
// control flow for generated will not return.
__ bind(&throw_termination_exception);
GenerateThrowUncatchable(masm, TERMINATION);
__ bind(&throw_normal_exception); __ bind(&throw_normal_exception);
GenerateThrowTOS(masm); GenerateThrowTOS(masm);
......
...@@ -70,6 +70,9 @@ ...@@ -70,6 +70,9 @@
// Mode to overwrite BinaryExpression values. // Mode to overwrite BinaryExpression values.
enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT }; enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };
// Types of uncatchable exceptions.
enum UncatchableExceptionType { OUT_OF_MEMORY, TERMINATION };
#if V8_TARGET_ARCH_IA32 #if V8_TARGET_ARCH_IA32
#include "ia32/codegen-ia32.h" #include "ia32/codegen-ia32.h"
...@@ -291,12 +294,14 @@ class CEntryStub : public CodeStub { ...@@ -291,12 +294,14 @@ class CEntryStub : public CodeStub {
void GenerateBody(MacroAssembler* masm, bool is_debug_break); void GenerateBody(MacroAssembler* masm, bool is_debug_break);
void GenerateCore(MacroAssembler* masm, void GenerateCore(MacroAssembler* masm,
Label* throw_normal_exception, Label* throw_normal_exception,
Label* throw_termination_exception,
Label* throw_out_of_memory_exception, Label* throw_out_of_memory_exception,
StackFrame::Type frame_type, StackFrame::Type frame_type,
bool do_gc, bool do_gc,
bool always_allocate_scope); bool always_allocate_scope);
void GenerateThrowTOS(MacroAssembler* masm); void GenerateThrowTOS(MacroAssembler* masm);
void GenerateThrowOutOfMemory(MacroAssembler* masm); void GenerateThrowUncatchable(MacroAssembler* masm,
UncatchableExceptionType type);
private: private:
Major MajorKey() { return CEntry; } Major MajorKey() { return CEntry; }
......
...@@ -156,7 +156,8 @@ Handle<Object> Execution::TryCall(Handle<JSFunction> func, ...@@ -156,7 +156,8 @@ Handle<Object> Execution::TryCall(Handle<JSFunction> func,
ASSERT(catcher.HasCaught()); ASSERT(catcher.HasCaught());
ASSERT(Top::has_pending_exception()); ASSERT(Top::has_pending_exception());
ASSERT(Top::external_caught_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()); result = v8::Utils::OpenHandle(*catcher.Exception());
} }
...@@ -328,6 +329,19 @@ void StackGuard::Preempt() { ...@@ -328,6 +329,19 @@ void StackGuard::Preempt() {
} }
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 #ifdef ENABLE_DEBUGGER_SUPPORT
bool StackGuard::IsDebugBreak() { bool StackGuard::IsDebugBreak() {
ExecutionAccess access; ExecutionAccess access;
...@@ -638,6 +652,10 @@ Object* Execution::HandleStackGuardInterrupt() { ...@@ -638,6 +652,10 @@ Object* Execution::HandleStackGuardInterrupt() {
} }
#endif #endif
if (StackGuard::IsPreempted()) RuntimePreempt(); if (StackGuard::IsPreempted()) RuntimePreempt();
if (StackGuard::IsTerminateExecution()) {
StackGuard::Continue(TERMINATE);
return Top::TerminateExecution();
}
if (StackGuard::IsInterrupted()) { if (StackGuard::IsInterrupted()) {
// interrupt // interrupt
StackGuard::Continue(INTERRUPT); StackGuard::Continue(INTERRUPT);
......
...@@ -37,7 +37,8 @@ enum InterruptFlag { ...@@ -37,7 +37,8 @@ enum InterruptFlag {
INTERRUPT = 1 << 0, INTERRUPT = 1 << 0,
DEBUGBREAK = 1 << 1, DEBUGBREAK = 1 << 1,
DEBUGCOMMAND = 1 << 2, DEBUGCOMMAND = 1 << 2,
PREEMPT = 1 << 3 PREEMPT = 1 << 3,
TERMINATE = 1 << 4
}; };
class Execution : public AllStatic { class Execution : public AllStatic {
...@@ -164,13 +165,15 @@ class StackGuard BASE_EMBEDDED { ...@@ -164,13 +165,15 @@ class StackGuard BASE_EMBEDDED {
static void Preempt(); static void Preempt();
static bool IsInterrupted(); static bool IsInterrupted();
static void Interrupt(); static void Interrupt();
static void Continue(InterruptFlag after_what); static bool IsTerminateExecution();
static void TerminateExecution();
#ifdef ENABLE_DEBUGGER_SUPPORT #ifdef ENABLE_DEBUGGER_SUPPORT
static void DebugBreak();
static void DebugCommand();
static bool IsDebugBreak(); static bool IsDebugBreak();
static void DebugBreak();
static bool IsDebugCommand(); static bool IsDebugCommand();
static void DebugCommand();
#endif #endif
static void Continue(InterruptFlag after_what);
private: private:
// You should hold the ExecutionAccess lock when calling this method. // You should hold the ExecutionAccess lock when calling this method.
......
...@@ -1412,6 +1412,9 @@ bool Heap::CreateInitialObjects() { ...@@ -1412,6 +1412,9 @@ bool Heap::CreateInitialObjects() {
if (obj->IsFailure()) return false; if (obj->IsFailure()) return false;
set_no_interceptor_result_sentinel(obj); 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. // Allocate the empty string.
obj = AllocateRawAsciiString(0, TENURED); obj = AllocateRawAsciiString(0, TENURED);
......
...@@ -111,6 +111,7 @@ namespace internal { ...@@ -111,6 +111,7 @@ namespace internal {
V(Object, nan_value, NanValue) \ V(Object, nan_value, NanValue) \
V(Object, undefined_value, UndefinedValue) \ V(Object, undefined_value, UndefinedValue) \
V(Object, no_interceptor_result_sentinel, NoInterceptorResultSentinel) \ V(Object, no_interceptor_result_sentinel, NoInterceptorResultSentinel) \
V(Object, termination_exception, TerminationException) \
V(Object, minus_zero_value, MinusZeroValue) \ V(Object, minus_zero_value, MinusZeroValue) \
V(Object, null_value, NullValue) \ V(Object, null_value, NullValue) \
V(Object, true_value, TrueValue) \ V(Object, true_value, TrueValue) \
......
...@@ -7505,6 +7505,7 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { ...@@ -7505,6 +7505,7 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
void CEntryStub::GenerateCore(MacroAssembler* masm, void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_normal_exception, Label* throw_normal_exception,
Label* throw_termination_exception,
Label* throw_out_of_memory_exception, Label* throw_out_of_memory_exception,
StackFrame::Type frame_type, StackFrame::Type frame_type,
bool do_gc, bool do_gc,
...@@ -7568,10 +7569,9 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, ...@@ -7568,10 +7569,9 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
__ test(eax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize)); __ test(eax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
__ j(zero, &retry, taken); __ j(zero, &retry, taken);
Label continue_exception; // Special handling of out of memory exceptions.
// If the returned failure is EXCEPTION then promote Top::pending_exception(). __ cmp(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException()));
__ cmp(eax, reinterpret_cast<int32_t>(Failure::Exception())); __ j(equal, throw_out_of_memory_exception);
__ j(not_equal, &continue_exception);
// Retrieve the pending exception and clear the variable. // Retrieve the pending exception and clear the variable.
ExternalReference pending_exception_address(Top::k_pending_exception_address); ExternalReference pending_exception_address(Top::k_pending_exception_address);
...@@ -7580,10 +7580,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, ...@@ -7580,10 +7580,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
Operand::StaticVariable(ExternalReference::the_hole_value_location())); Operand::StaticVariable(ExternalReference::the_hole_value_location()));
__ mov(Operand::StaticVariable(pending_exception_address), edx); __ mov(Operand::StaticVariable(pending_exception_address), edx);
__ bind(&continue_exception); // Special handling of termination exceptions which are uncatchable
// Special handling of out of memory exception. // by javascript code.
__ cmp(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException())); __ cmp(eax, Factory::termination_exception());
__ j(equal, throw_out_of_memory_exception); __ j(equal, throw_termination_exception);
// Handle normal exception. // Handle normal exception.
__ jmp(throw_normal_exception); __ jmp(throw_normal_exception);
...@@ -7593,7 +7593,8 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, ...@@ -7593,7 +7593,8 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
} }
void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
UncatchableExceptionType type) {
// Adjust this code if not the case. // Adjust this code if not the case.
ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
...@@ -7618,6 +7619,7 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { ...@@ -7618,6 +7619,7 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) {
ASSERT(StackHandlerConstants::kNextOffset == 0); ASSERT(StackHandlerConstants::kNextOffset == 0);
__ pop(Operand::StaticVariable(handler_address)); __ pop(Operand::StaticVariable(handler_address));
if (type == OUT_OF_MEMORY) {
// Set external caught exception to false. // Set external caught exception to false.
ExternalReference external_caught(Top::k_external_caught_exception_address); ExternalReference external_caught(Top::k_external_caught_exception_address);
__ mov(eax, false); __ mov(eax, false);
...@@ -7627,8 +7629,9 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { ...@@ -7627,8 +7629,9 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) {
ExternalReference pending_exception(Top::k_pending_exception_address); ExternalReference pending_exception(Top::k_pending_exception_address);
__ mov(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException())); __ mov(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException()));
__ mov(Operand::StaticVariable(pending_exception), eax); __ mov(Operand::StaticVariable(pending_exception), eax);
}
// Clear the context pointer; // Clear the context pointer.
__ xor_(esi, Operand(esi)); __ xor_(esi, Operand(esi));
// Restore fp from handler and discard handler state. // Restore fp from handler and discard handler state.
...@@ -7667,11 +7670,14 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { ...@@ -7667,11 +7670,14 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
// edi: number of arguments including receiver (C callee-saved) // edi: number of arguments including receiver (C callee-saved)
// esi: argv pointer (C callee-saved) // esi: argv pointer (C callee-saved)
Label throw_out_of_memory_exception;
Label throw_normal_exception; Label throw_normal_exception;
Label throw_termination_exception;
Label throw_out_of_memory_exception;
// Call into the runtime system. // Call into the runtime system.
GenerateCore(masm, &throw_normal_exception, GenerateCore(masm,
&throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception, &throw_out_of_memory_exception,
frame_type, frame_type,
false, false,
...@@ -7680,6 +7686,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { ...@@ -7680,6 +7686,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
// Do space-specific GC and retry runtime call. // Do space-specific GC and retry runtime call.
GenerateCore(masm, GenerateCore(masm,
&throw_normal_exception, &throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception, &throw_out_of_memory_exception,
frame_type, frame_type,
true, true,
...@@ -7690,14 +7697,17 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { ...@@ -7690,14 +7697,17 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
__ mov(eax, Immediate(reinterpret_cast<int32_t>(failure))); __ mov(eax, Immediate(reinterpret_cast<int32_t>(failure)));
GenerateCore(masm, GenerateCore(masm,
&throw_normal_exception, &throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception, &throw_out_of_memory_exception,
frame_type, frame_type,
true, true,
true); true);
__ bind(&throw_out_of_memory_exception); __ bind(&throw_out_of_memory_exception);
GenerateThrowOutOfMemory(masm); GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
// control flow for generated will not return.
__ bind(&throw_termination_exception);
GenerateThrowUncatchable(masm, TERMINATION);
__ bind(&throw_normal_exception); __ bind(&throw_normal_exception);
GenerateThrowTOS(masm); GenerateThrowTOS(masm);
......
...@@ -147,14 +147,12 @@ Handle<String> MessageHandler::GetMessage(Handle<Object> data) { ...@@ -147,14 +147,12 @@ Handle<String> MessageHandler::GetMessage(Handle<Object> data) {
Handle<String> fmt_str = Factory::LookupAsciiSymbol("FormatMessage"); Handle<String> fmt_str = Factory::LookupAsciiSymbol("FormatMessage");
Handle<JSFunction> fun = Handle<JSFunction> fun =
Handle<JSFunction>( Handle<JSFunction>(
JSFunction::cast( JSFunction::cast(Top::builtins()->GetProperty(*fmt_str)));
Top::builtins()->GetProperty(*fmt_str)));
Object** argv[1] = { data.location() }; Object** argv[1] = { data.location() };
bool caught_exception; bool caught_exception;
Handle<Object> result = Handle<Object> result =
Execution::TryCall(fun, Top::builtins(), 1, argv, Execution::TryCall(fun, Top::builtins(), 1, argv, &caught_exception);
&caught_exception);
if (caught_exception || !result->IsString()) { if (caught_exception || !result->IsString()) {
return Factory::LookupAsciiSymbol("<error>"); return Factory::LookupAsciiSymbol("<error>");
......
...@@ -705,7 +705,8 @@ void Oddball::OddballVerify() { ...@@ -705,7 +705,8 @@ void Oddball::OddballVerify() {
} else { } else {
ASSERT(number->IsSmi()); ASSERT(number->IsSmi());
int value = Smi::cast(number)->value(); 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);
} }
} }
......
...@@ -788,6 +788,7 @@ Failure* Failure::Exception() { ...@@ -788,6 +788,7 @@ Failure* Failure::Exception() {
return Construct(EXCEPTION); return Construct(EXCEPTION);
} }
Failure* Failure::OutOfMemoryException() { Failure* Failure::OutOfMemoryException() {
return Construct(OUT_OF_MEMORY_EXCEPTION); return Construct(OUT_OF_MEMORY_EXCEPTION);
} }
......
...@@ -98,6 +98,7 @@ void Top::InitializeThreadLocal() { ...@@ -98,6 +98,7 @@ void Top::InitializeThreadLocal() {
thread_local_.stack_is_cooked_ = false; thread_local_.stack_is_cooked_ = false;
thread_local_.try_catch_handler_ = NULL; thread_local_.try_catch_handler_ = NULL;
thread_local_.context_ = NULL; thread_local_.context_ = NULL;
thread_local_.thread_id_ = ThreadManager::kInvalidId;
thread_local_.external_caught_exception_ = false; thread_local_.external_caught_exception_ = false;
thread_local_.failed_access_check_callback_ = NULL; thread_local_.failed_access_check_callback_ = NULL;
clear_pending_exception(); clear_pending_exception();
...@@ -598,6 +599,12 @@ Failure* Top::StackOverflow() { ...@@ -598,6 +599,12 @@ Failure* Top::StackOverflow() {
} }
Failure* Top::TerminateExecution() {
DoThrow(Heap::termination_exception(), NULL, NULL);
return Failure::Exception();
}
Failure* Top::Throw(Object* exception, MessageLocation* location) { Failure* Top::Throw(Object* exception, MessageLocation* location) {
DoThrow(exception, location, NULL); DoThrow(exception, location, NULL);
return Failure::Exception(); return Failure::Exception();
...@@ -694,7 +701,8 @@ void Top::ReportUncaughtException(Handle<Object> exception, ...@@ -694,7 +701,8 @@ void Top::ReportUncaughtException(Handle<Object> exception,
} }
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. // Find the top-most try-catch handler.
StackHandler* handler = StackHandler* handler =
StackHandler::FromAddress(Top::handler(Top::GetCurrentThread())); StackHandler::FromAddress(Top::handler(Top::GetCurrentThread()));
...@@ -712,7 +720,8 @@ bool Top::ShouldReportException(bool* is_caught_externally) { ...@@ -712,7 +720,8 @@ bool Top::ShouldReportException(bool* is_caught_externally) {
// //
// See comments in RegisterTryCatchHandler for details. // See comments in RegisterTryCatchHandler for details.
*is_caught_externally = try_catch != NULL && *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) { if (*is_caught_externally) {
// Only report the exception if the external handler is verbose. // Only report the exception if the external handler is verbose.
...@@ -735,12 +744,17 @@ void Top::DoThrow(Object* exception, ...@@ -735,12 +744,17 @@ void Top::DoThrow(Object* exception,
// Determine reporting and whether the exception is caught externally. // Determine reporting and whether the exception is caught externally.
bool is_caught_externally = false; bool is_caught_externally = false;
bool is_out_of_memory = exception == Failure::OutOfMemoryException(); bool is_out_of_memory = exception == Failure::OutOfMemoryException();
bool should_return_exception = ShouldReportException(&is_caught_externally); bool is_termination_exception = exception == Heap::termination_exception();
bool report_exception = !is_out_of_memory && should_return_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 #ifdef ENABLE_DEBUGGER_SUPPORT
// Notify debugger of exception. // Notify debugger of exception.
if (catchable_by_javascript) {
Debugger::OnException(exception_handle, report_exception); Debugger::OnException(exception_handle, report_exception);
}
#endif #endif
// Generate the message. // Generate the message.
...@@ -791,14 +805,21 @@ void Top::ReportPendingMessages() { ...@@ -791,14 +805,21 @@ void Top::ReportPendingMessages() {
// the global context. Note: We have to mark the global context here // the global context. Note: We have to mark the global context here
// since the GenerateThrowOutOfMemory stub cannot make a RuntimeCall to // since the GenerateThrowOutOfMemory stub cannot make a RuntimeCall to
// set it. // set it.
bool external_caught = thread_local_.external_caught_exception_;
HandleScope scope; HandleScope scope;
if (thread_local_.pending_exception_ == Failure::OutOfMemoryException()) { if (thread_local_.pending_exception_ == Failure::OutOfMemoryException()) {
context()->mark_out_of_memory(); 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 { } else {
Handle<Object> exception(pending_exception()); Handle<Object> exception(pending_exception());
bool external_caught = thread_local_.external_caught_exception_;
thread_local_.external_caught_exception_ = false; thread_local_.external_caught_exception_ = false;
if (external_caught) { if (external_caught) {
thread_local_.try_catch_handler_->can_continue_ = true;
thread_local_.try_catch_handler_->exception_ = thread_local_.try_catch_handler_->exception_ =
thread_local_.pending_exception_; thread_local_.pending_exception_;
if (!thread_local_.pending_message_obj_->IsTheHole()) { if (!thread_local_.pending_message_obj_->IsTheHole()) {
...@@ -834,16 +855,30 @@ void Top::TraceException(bool flag) { ...@@ -834,16 +855,30 @@ void Top::TraceException(bool flag) {
} }
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. // Allways reschedule out of memory exceptions.
if (!is_out_of_memory()) { if (!is_out_of_memory()) {
// Never reschedule the exception if this is the bottom call. bool is_termination_exception =
bool clear_exception = is_bottom_call; 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 // If the exception is externally caught, clear it if there are no
// JavaScript frames on the way to the C++ frame that has the // JavaScript frames on the way to the C++ frame that has the
// external handler. // external handler.
if (thread_local_.external_caught_exception_) {
ASSERT(thread_local_.try_catch_handler_ != NULL); ASSERT(thread_local_.try_catch_handler_ != NULL);
Address external_handler_address = Address external_handler_address =
reinterpret_cast<Address>(thread_local_.try_catch_handler_); reinterpret_cast<Address>(thread_local_.try_catch_handler_);
......
...@@ -46,6 +46,7 @@ class ThreadLocalTop BASE_EMBEDDED { ...@@ -46,6 +46,7 @@ class ThreadLocalTop BASE_EMBEDDED {
// The context where the current execution method is created and for variable // The context where the current execution method is created and for variable
// lookups. // lookups.
Context* context_; Context* context_;
int thread_id_;
Object* pending_exception_; Object* pending_exception_;
bool has_pending_message_; bool has_pending_message_;
const char* pending_message_; const char* pending_message_;
...@@ -118,6 +119,10 @@ class Top { ...@@ -118,6 +119,10 @@ class Top {
thread_local_.save_context_ = 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. // Interface to pending exception.
static Object* pending_exception() { static Object* pending_exception() {
ASSERT(has_pending_exception()); ASSERT(has_pending_exception());
...@@ -152,7 +157,8 @@ class Top { ...@@ -152,7 +157,8 @@ class Top {
// exceptions. If an exception was thrown and not handled by an external // 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 // handler the exception is scheduled to be rethrown when we return to running
// JavaScript code. If an exception is scheduled true is returned. // 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() { static bool* external_caught_exception_address() {
return &thread_local_.external_caught_exception_; return &thread_local_.external_caught_exception_;
...@@ -246,7 +252,8 @@ class Top { ...@@ -246,7 +252,8 @@ class Top {
static void DoThrow(Object* exception, static void DoThrow(Object* exception,
MessageLocation* location, MessageLocation* location,
const char* message); 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, static void ReportUncaughtException(Handle<Object> exception,
MessageLocation* location, MessageLocation* location,
Handle<String> stack_trace); Handle<String> stack_trace);
...@@ -260,6 +267,7 @@ class Top { ...@@ -260,6 +267,7 @@ class Top {
// Out of resource exception helpers. // Out of resource exception helpers.
static Failure* StackOverflow(); static Failure* StackOverflow();
static Failure* TerminateExecution();
// Administration // Administration
static void Initialize(); static void Initialize();
......
...@@ -151,6 +151,10 @@ bool ThreadManager::RestoreThread() { ...@@ -151,6 +151,10 @@ bool ThreadManager::RestoreThread() {
from = RegExpStack::RestoreStack(from); from = RegExpStack::RestoreStack(from);
from = Bootstrapper::RestoreState(from); from = Bootstrapper::RestoreState(from);
Thread::SetThreadLocal(thread_state_key, NULL); Thread::SetThreadLocal(thread_state_key, NULL);
if (state->terminate_on_restore()) {
StackGuard::TerminateExecution();
state->set_terminate_on_restore(false);
}
state->set_id(kInvalidId); state->set_id(kInvalidId);
state->Unlink(); state->Unlink();
state->LinkInto(ThreadState::FREE_LIST); state->LinkInto(ThreadState::FREE_LIST);
...@@ -188,6 +192,7 @@ ThreadState* ThreadState::in_use_anchor_ = new ThreadState(); ...@@ -188,6 +192,7 @@ ThreadState* ThreadState::in_use_anchor_ = new ThreadState();
ThreadState::ThreadState() : id_(ThreadManager::kInvalidId), ThreadState::ThreadState() : id_(ThreadManager::kInvalidId),
terminate_on_restore_(false),
next_(this), previous_(this) { next_(this), previous_(this) {
} }
...@@ -317,7 +322,21 @@ int ThreadManager::CurrentId() { ...@@ -317,7 +322,21 @@ int ThreadManager::CurrentId() {
void ThreadManager::AssignId() { void ThreadManager::AssignId() {
if (!Thread::HasThreadLocal(thread_id_key)) { 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);
}
} }
} }
......
...@@ -50,6 +50,12 @@ class ThreadState { ...@@ -50,6 +50,12 @@ class ThreadState {
void set_id(int id) { id_ = id; } void set_id(int id) { id_ = id; }
int id() { return 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. // Get data area for archiving a thread.
char* data() { return data_; } char* data() { return data_; }
private: private:
...@@ -58,6 +64,7 @@ class ThreadState { ...@@ -58,6 +64,7 @@ class ThreadState {
void AllocateSpace(); void AllocateSpace();
int id_; int id_;
bool terminate_on_restore_;
char* data_; char* data_;
ThreadState* next_; ThreadState* next_;
ThreadState* previous_; ThreadState* previous_;
...@@ -88,6 +95,8 @@ class ThreadManager : public AllStatic { ...@@ -88,6 +95,8 @@ class ThreadManager : public AllStatic {
static int CurrentId(); static int CurrentId();
static void AssignId(); static void AssignId();
static void TerminateExecution(int thread_id);
static const int kInvalidId = -1; static const int kInvalidId = -1;
private: private:
static void EagerlyArchiveThread(); static void EagerlyArchiveThread();
......
...@@ -6747,6 +6747,7 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { ...@@ -6747,6 +6747,7 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
void CEntryStub::GenerateCore(MacroAssembler* masm, void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_normal_exception, Label* throw_normal_exception,
Label* throw_termination_exception,
Label* throw_out_of_memory_exception, Label* throw_out_of_memory_exception,
StackFrame::Type frame_type, StackFrame::Type frame_type,
bool do_gc, bool do_gc,
...@@ -6819,11 +6820,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, ...@@ -6819,11 +6820,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
__ testl(rax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize)); __ testl(rax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
__ j(zero, &retry); __ j(zero, &retry);
Label continue_exception; // Special handling of out of memory exceptions.
// If the returned failure is EXCEPTION then promote Top::pending_exception(). __ movq(kScratchRegister, Failure::OutOfMemoryException(), RelocInfo::NONE);
__ movq(kScratchRegister, Failure::Exception(), RelocInfo::NONE);
__ cmpq(rax, kScratchRegister); __ cmpq(rax, kScratchRegister);
__ j(not_equal, &continue_exception); __ j(equal, throw_out_of_memory_exception);
// Retrieve the pending exception and clear the variable. // Retrieve the pending exception and clear the variable.
ExternalReference pending_exception_address(Top::k_pending_exception_address); ExternalReference pending_exception_address(Top::k_pending_exception_address);
...@@ -6833,11 +6833,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, ...@@ -6833,11 +6833,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
__ movq(rdx, Operand(rdx, 0)); __ movq(rdx, Operand(rdx, 0));
__ movq(Operand(kScratchRegister, 0), rdx); __ movq(Operand(kScratchRegister, 0), rdx);
__ bind(&continue_exception); // Special handling of termination exceptions which are uncatchable
// Special handling of out of memory exception. // by javascript code.
__ movq(kScratchRegister, Failure::OutOfMemoryException(), RelocInfo::NONE); __ Cmp(rax, Factory::termination_exception());
__ cmpq(rax, kScratchRegister); __ j(equal, throw_termination_exception);
__ j(equal, throw_out_of_memory_exception);
// Handle normal exception. // Handle normal exception.
__ jmp(throw_normal_exception); __ jmp(throw_normal_exception);
...@@ -6847,7 +6846,8 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, ...@@ -6847,7 +6846,8 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
} }
void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
UncatchableExceptionType type) {
// Fetch top stack handler. // Fetch top stack handler.
ExternalReference handler_address(Top::k_handler_address); ExternalReference handler_address(Top::k_handler_address);
__ movq(kScratchRegister, handler_address); __ movq(kScratchRegister, handler_address);
...@@ -6857,30 +6857,32 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { ...@@ -6857,30 +6857,32 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) {
Label loop, done; Label loop, done;
__ bind(&loop); __ bind(&loop);
// Load the type of the current stack handler. // Load the type of the current stack handler.
__ cmpq(Operand(rsp, StackHandlerConstants::kStateOffset), const int kStateOffset = StackHandlerConstants::kStateOffset;
Immediate(StackHandler::ENTRY)); __ cmpq(Operand(rsp, kStateOffset), Immediate(StackHandler::ENTRY));
__ j(equal, &done); __ j(equal, &done);
// Fetch the next handler in the list. // Fetch the next handler in the list.
ASSERT(StackHandlerConstants::kNextOffset == 0); const int kNextOffset = StackHandlerConstants::kNextOffset;
__ pop(rsp); __ movq(rsp, Operand(rsp, kNextOffset));
__ jmp(&loop); __ jmp(&loop);
__ bind(&done); __ bind(&done);
// Set the top handler address to next handler past the current ENTRY handler. // Set the top handler address to next handler past the current ENTRY handler.
__ pop(rax); __ movq(kScratchRegister, handler_address);
__ store_rax(handler_address); __ pop(Operand(kScratchRegister, 0));
if (type == OUT_OF_MEMORY) {
// Set external caught exception to false. // Set external caught exception to false.
__ movq(rax, Immediate(false));
ExternalReference external_caught(Top::k_external_caught_exception_address); ExternalReference external_caught(Top::k_external_caught_exception_address);
__ movq(rax, Immediate(false));
__ store_rax(external_caught); __ store_rax(external_caught);
// Set pending exception and rax to out of memory exception. // Set pending exception and rax to out of memory exception.
__ movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE);
ExternalReference pending_exception(Top::k_pending_exception_address); ExternalReference pending_exception(Top::k_pending_exception_address);
__ movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE);
__ store_rax(pending_exception); __ store_rax(pending_exception);
}
// Clear the context pointer; // Clear the context pointer.
__ xor_(rsi, rsi); __ xor_(rsi, rsi);
// Restore registers from handler. // Restore registers from handler.
...@@ -6956,12 +6958,14 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { ...@@ -6956,12 +6958,14 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
// r14: number of arguments including receiver (C callee-saved). // r14: number of arguments including receiver (C callee-saved).
// r15: argv pointer (C callee-saved). // r15: argv pointer (C callee-saved).
Label throw_out_of_memory_exception;
Label throw_normal_exception; Label throw_normal_exception;
Label throw_termination_exception;
Label throw_out_of_memory_exception;
// Call into the runtime system. // Call into the runtime system.
GenerateCore(masm, GenerateCore(masm,
&throw_normal_exception, &throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception, &throw_out_of_memory_exception,
frame_type, frame_type,
false, false,
...@@ -6970,6 +6974,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { ...@@ -6970,6 +6974,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
// Do space-specific GC and retry runtime call. // Do space-specific GC and retry runtime call.
GenerateCore(masm, GenerateCore(masm,
&throw_normal_exception, &throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception, &throw_out_of_memory_exception,
frame_type, frame_type,
true, true,
...@@ -6980,14 +6985,17 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { ...@@ -6980,14 +6985,17 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
__ movq(rax, failure, RelocInfo::NONE); __ movq(rax, failure, RelocInfo::NONE);
GenerateCore(masm, GenerateCore(masm,
&throw_normal_exception, &throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception, &throw_out_of_memory_exception,
frame_type, frame_type,
true, true,
true); true);
__ bind(&throw_out_of_memory_exception); __ bind(&throw_out_of_memory_exception);
GenerateThrowOutOfMemory(masm); GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
// control flow for generated will not return.
__ bind(&throw_termination_exception);
GenerateThrowUncatchable(masm, TERMINATION);
__ bind(&throw_normal_exception); __ bind(&throw_normal_exception);
GenerateThrowTOS(masm); GenerateThrowTOS(masm);
......
...@@ -56,6 +56,7 @@ SOURCES = { ...@@ -56,6 +56,7 @@ SOURCES = {
'test-spaces.cc', 'test-spaces.cc',
'test-strings.cc', 'test-strings.cc',
'test-threads.cc', 'test-threads.cc',
'test-thread-termination.cc',
'test-utils.cc', 'test-utils.cc',
'test-version.cc' 'test-version.cc'
], ],
......
// 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;
}
...@@ -50,5 +50,3 @@ TEST(Preemption) { ...@@ -50,5 +50,3 @@ TEST(Preemption) {
script->Run(); script->Run();
} }
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