void StackGuard::set_interrupt_limits(const ExecutionAccess& lock) {
ASSERT(isolate_ != NULL);
- // Ignore attempts to interrupt when interrupts are postponed.
- if (should_postpone_interrupts(lock)) return;
thread_local_.jslimit_ = kInterruptLimit;
thread_local_.climit_ = kInterruptLimit;
isolate_->heap()->SetStackLimits();
}
-bool StackGuard::CheckInterrupt(int flagbit) {
+void StackGuard::PushPostponeInterruptsScope(PostponeInterruptsScope* scope) {
ExecutionAccess access(isolate_);
- return thread_local_.interrupt_flags_ & flagbit;
+ // Intercept already requested interrupts.
+ int intercepted = thread_local_.interrupt_flags_ & scope->intercept_mask_;
+ scope->intercepted_flags_ = intercepted;
+ thread_local_.interrupt_flags_ &= ~intercepted;
+ if (!has_pending_interrupts(access)) reset_limits(access);
+ // Add scope to the chain.
+ scope->prev_ = thread_local_.postpone_interrupts_;
+ thread_local_.postpone_interrupts_ = scope;
}
-void StackGuard::RequestInterrupt(int flagbit) {
+void StackGuard::PopPostponeInterruptsScope() {
ExecutionAccess access(isolate_);
- thread_local_.interrupt_flags_ |= flagbit;
+ PostponeInterruptsScope* top = thread_local_.postpone_interrupts_;
+ // Make intercepted interrupts active.
+ ASSERT((thread_local_.interrupt_flags_ & top->intercept_mask_) == 0);
+ thread_local_.interrupt_flags_ |= top->intercepted_flags_;
+ if (has_pending_interrupts(access)) set_interrupt_limits(access);
+ // Remove scope from chain.
+ thread_local_.postpone_interrupts_ = top->prev_;
+}
+
+
+bool StackGuard::CheckInterrupt(InterruptFlag flag) {
+ ExecutionAccess access(isolate_);
+ return thread_local_.interrupt_flags_ & flag;
+}
+
+
+void StackGuard::RequestInterrupt(InterruptFlag flag) {
+ ExecutionAccess access(isolate_);
+ // Check the chain of PostponeInterruptsScopes for interception.
+ if (thread_local_.postpone_interrupts_ &&
+ thread_local_.postpone_interrupts_->Intercept(flag)) {
+ return;
+ }
+
+ // Not intercepted. Set as active interrupt flag.
+ thread_local_.interrupt_flags_ |= flag;
set_interrupt_limits(access);
}
-void StackGuard::ClearInterrupt(int flagbit) {
+void StackGuard::ClearInterrupt(InterruptFlag flag) {
ExecutionAccess access(isolate_);
- thread_local_.interrupt_flags_ &= ~flagbit;
- if (!should_postpone_interrupts(access) && !has_pending_interrupts(access)) {
- reset_limits(access);
+ // Clear the interrupt flag from the chain of PostponeInterruptsScopes.
+ for (PostponeInterruptsScope* current = thread_local_.postpone_interrupts_;
+ current != NULL;
+ current = current->prev_) {
+ current->intercepted_flags_ &= ~flag;
}
+
+ // Clear the interrupt flag from the active interrupt flags.
+ thread_local_.interrupt_flags_ &= ~flag;
+ if (!has_pending_interrupts(access)) reset_limits(access);
}
bool StackGuard::CheckAndClearInterrupt(InterruptFlag flag) {
ExecutionAccess access(isolate_);
- int flagbit = 1 << flag;
- bool result = (thread_local_.interrupt_flags_ & flagbit);
- thread_local_.interrupt_flags_ &= ~flagbit;
- if (!should_postpone_interrupts(access) && !has_pending_interrupts(access)) {
- reset_limits(access);
- }
+ bool result = (thread_local_.interrupt_flags_ & flag);
+ thread_local_.interrupt_flags_ &= ~flag;
+ if (!has_pending_interrupts(access)) reset_limits(access);
return result;
}
jslimit_ = kIllegalLimit;
real_climit_ = kIllegalLimit;
climit_ = kIllegalLimit;
- nesting_ = 0;
- postpone_interrupts_nesting_ = 0;
+ postpone_interrupts_ = NULL;
interrupt_flags_ = 0;
}
climit_ = limit;
should_set_stack_limits = true;
}
- nesting_ = 0;
- postpone_interrupts_nesting_ = 0;
+ postpone_interrupts_ = NULL;
interrupt_flags_ = 0;
return should_set_stack_limits;
}
Object* StackGuard::HandleInterrupts() {
- {
- ExecutionAccess access(isolate_);
- if (should_postpone_interrupts(access)) {
- return isolate_->heap()->undefined_value();
- }
- }
-
if (CheckAndClearInterrupt(GC_REQUEST)) {
isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags, "GC interrupt");
}
class ExecutionAccess;
+class PostponeInterruptsScope;
// StackGuard contains the handling of the limits that are used to limit the
// it has been set up.
void ClearThread(const ExecutionAccess& lock);
-#define INTERRUPT_LIST(V) \
- V(DEBUGBREAK, DebugBreak) \
- V(DEBUGCOMMAND, DebugCommand) \
- V(TERMINATE_EXECUTION, TerminateExecution) \
- V(GC_REQUEST, GC) \
- V(INSTALL_CODE, InstallCode) \
- V(API_INTERRUPT, ApiInterrupt) \
- V(DEOPT_MARKED_ALLOCATION_SITES, DeoptMarkedAllocationSites)
-
-#define V(NAME, Name) \
- inline bool Check##Name() { return CheckInterrupt(1 << NAME); } \
- inline void Request##Name() { RequestInterrupt(1 << NAME); } \
- inline void Clear##Name() { ClearInterrupt(1 << NAME); }
+#define INTERRUPT_LIST(V) \
+ V(DEBUGBREAK, DebugBreak, 0) \
+ V(DEBUGCOMMAND, DebugCommand, 1) \
+ V(TERMINATE_EXECUTION, TerminateExecution, 2) \
+ V(GC_REQUEST, GC, 3) \
+ V(INSTALL_CODE, InstallCode, 4) \
+ V(API_INTERRUPT, ApiInterrupt, 5) \
+ V(DEOPT_MARKED_ALLOCATION_SITES, DeoptMarkedAllocationSites, 6)
+
+#define V(NAME, Name, id) \
+ inline bool Check##Name() { return CheckInterrupt(NAME); } \
+ inline void Request##Name() { RequestInterrupt(NAME); } \
+ inline void Clear##Name() { ClearInterrupt(NAME); }
INTERRUPT_LIST(V)
#undef V
+ // Flag used to set the interrupt causes.
+ enum InterruptFlag {
+ #define V(NAME, Name, id) NAME = (1 << id),
+ INTERRUPT_LIST(V)
+ #undef V
+ #define V(NAME, Name, id) NAME |
+ ALL_INTERRUPTS = INTERRUPT_LIST(V) 0
+ #undef V
+ };
+
// This provides an asynchronous read of the stack limits for the current
// thread. There are no locks protecting this, but it is assumed that you
// have the global V8 lock if you are using multiple V8 threads.
private:
StackGuard();
-// Flag used to set the interrupt causes.
-enum InterruptFlag {
-#define V(NAME, Name) NAME,
- INTERRUPT_LIST(V)
-#undef V
- NUMBER_OF_INTERRUPTS
-};
-
- bool CheckInterrupt(int flagbit);
- void RequestInterrupt(int flagbit);
- void ClearInterrupt(int flagbit);
+ bool CheckInterrupt(InterruptFlag flag);
+ void RequestInterrupt(InterruptFlag flag);
+ void ClearInterrupt(InterruptFlag flag);
bool CheckAndClearInterrupt(InterruptFlag flag);
// You should hold the ExecutionAccess lock when calling this method.
bool has_pending_interrupts(const ExecutionAccess& lock) {
- // Sanity check: We shouldn't be asking about pending interrupts
- // unless we're not postponing them anymore.
- ASSERT(!should_postpone_interrupts(lock));
return thread_local_.interrupt_flags_ != 0;
}
// You should hold the ExecutionAccess lock when calling this method.
- bool should_postpone_interrupts(const ExecutionAccess& lock) {
- return thread_local_.postpone_interrupts_nesting_ > 0;
- }
-
- // You should hold the ExecutionAccess lock when calling this method.
inline void set_interrupt_limits(const ExecutionAccess& lock);
// Reset limits to actual values. For example after handling interrupt.
static const uintptr_t kIllegalLimit = 0xfffffff8;
#endif
+ void PushPostponeInterruptsScope(PostponeInterruptsScope* scope);
+ void PopPostponeInterruptsScope();
+
class ThreadLocal V8_FINAL {
public:
ThreadLocal() { Clear(); }
uintptr_t real_climit_; // Actual C++ stack limit set for the VM.
uintptr_t climit_;
- int nesting_;
- int postpone_interrupts_nesting_;
+ PostponeInterruptsScope* postpone_interrupts_;
int interrupt_flags_;
};
}
+bool PostponeInterruptsScope::Intercept(StackGuard::InterruptFlag flag) {
+ // First check whether the previous scope intercepts.
+ if (prev_ && prev_->Intercept(flag)) return true;
+ // Then check whether this scope intercepts.
+ if ((flag & intercept_mask_)) {
+ intercepted_flags_ |= flag;
+ return true;
+ }
+ return false;
+}
+
} } // namespace v8::internal
// account.
class PostponeInterruptsScope BASE_EMBEDDED {
public:
- explicit PostponeInterruptsScope(Isolate* isolate)
- : stack_guard_(isolate->stack_guard()), isolate_(isolate) {
- ExecutionAccess access(isolate_);
- stack_guard_->thread_local_.postpone_interrupts_nesting_++;
- stack_guard_->DisableInterrupts();
+ PostponeInterruptsScope(Isolate* isolate,
+ int intercept_mask = StackGuard::ALL_INTERRUPTS)
+ : stack_guard_(isolate->stack_guard()),
+ intercept_mask_(intercept_mask),
+ intercepted_flags_(0) {
+ stack_guard_->PushPostponeInterruptsScope(this);
}
~PostponeInterruptsScope() {
- ExecutionAccess access(isolate_);
- if (--stack_guard_->thread_local_.postpone_interrupts_nesting_ == 0) {
- stack_guard_->EnableInterrupts();
- }
+ stack_guard_->PopPostponeInterruptsScope();
}
+
+ // Find the bottom-most scope that intercepts this interrupt.
+ // Return whether the interrupt has been intercepted.
+ bool Intercept(StackGuard::InterruptFlag flag);
+
private:
StackGuard* stack_guard_;
- Isolate* isolate_;
+ int intercept_mask_;
+ int intercepted_flags_;
+ PostponeInterruptsScope* prev_;
+
+ friend class StackGuard;
};
} else if (message.IsEvent() && message.GetEvent() == v8::AfterCompile) {
i::HandleScope scope(isolate);
- bool is_debug_break = isolate->stack_guard()->CheckDebugBreak();
- // Force DebugBreak flag while serializer is working.
- isolate->stack_guard()->RequestDebugBreak();
+ int current_count = break_point_hit_count;
// Force serialization to trigger some internal JS execution.
message.GetJSON();
- // Restore previous state.
- if (is_debug_break) {
- isolate->stack_guard()->RequestDebugBreak();
- } else {
- isolate->stack_guard()->ClearDebugBreak();
- }
+ CHECK_EQ(current_count, break_point_hit_count);
}
}
delete semaphore;
semaphore = NULL;
}
+
+
+static int callback_counter = 0;
+
+
+static void CounterCallback(v8::Isolate* isolate, void* data) {
+ callback_counter++;
+}
+
+
+TEST(PostponeTerminateException) {
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope scope(isolate);
+ v8::Handle<v8::ObjectTemplate> global =
+ CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop);
+ v8::Handle<v8::Context> context =
+ v8::Context::New(CcTest::isolate(), NULL, global);
+ v8::Context::Scope context_scope(context);
+
+ v8::TryCatch try_catch;
+ static const char* terminate_and_loop =
+ "terminate(); for (var i = 0; i < 10000; i++);";
+
+ { // Postpone terminate execution interrupts.
+ i::PostponeInterruptsScope p1(CcTest::i_isolate(),
+ i::StackGuard::TERMINATE_EXECUTION) ;
+
+ // API interrupts should still be triggered.
+ CcTest::isolate()->RequestInterrupt(&CounterCallback, NULL);
+ CHECK_EQ(0, callback_counter);
+ CompileRun(terminate_and_loop);
+ CHECK(!try_catch.HasTerminated());
+ CHECK_EQ(1, callback_counter);
+
+ { // Postpone API interrupts as well.
+ i::PostponeInterruptsScope p2(CcTest::i_isolate(),
+ i::StackGuard::API_INTERRUPT);
+
+ // None of the two interrupts should trigger.
+ CcTest::isolate()->RequestInterrupt(&CounterCallback, NULL);
+ CompileRun(terminate_and_loop);
+ CHECK(!try_catch.HasTerminated());
+ CHECK_EQ(1, callback_counter);
+ }
+
+ // Now the previously requested API interrupt should trigger.
+ CompileRun(terminate_and_loop);
+ CHECK(!try_catch.HasTerminated());
+ CHECK_EQ(2, callback_counter);
+ }
+
+ // Now the previously requested terminate execution interrupt should trigger.
+ CompileRun("for (var i = 0; i < 10000; i++);");
+ CHECK(try_catch.HasTerminated());
+ CHECK_EQ(2, callback_counter);
+}