Revision: 18363
Author: [email protected]
Date: Wed Dec 18 18:38:35 2013 UTC
Log: Introduce API to temporarily interrupt long running JavaScript
code.
It is different from termination API as interrupted JavaScript will
continue to execute normally when registered InterruptCallback returns.
/**
* Request V8 to interrupt long running JavaScript code and invoke
* the given |callback| passing the given |data| to it. After |callback|
* returns control will be returned to the JavaScript code.
* At any given moment V8 can remember only a single callback for the very
* last interrupt request.
* Can be called from another thread without acquiring a |Locker|.
* Registered |callback| must not reenter interrupted Isolate.
*/
void RequestInterrupt(InterruptCallback callback, void* data);
/**
* Clear interrupt request created by |RequestInterrupt|.
* Can be called from another thread without acquiring a |Locker|.
*/
void ClearInterrupt();
Fix Hydrogen SCE pass to avoid eliminating stack guards too aggressively.
Only normal JavaScript functions are guaranteed to have stack guard in the
prologue. If function is a builtin or has a custom call IC it will lack one.
BUG=
[email protected], [email protected]
Review URL: https://codereview.chromium.org/102063004
http://code.google.com/p/v8/source/detail?r=18363
Modified:
/branches/bleeding_edge/include/v8.h
/branches/bleeding_edge/src/api.cc
/branches/bleeding_edge/src/execution.cc
/branches/bleeding_edge/src/execution.h
/branches/bleeding_edge/src/hydrogen-instructions.h
/branches/bleeding_edge/src/hydrogen-sce.cc
/branches/bleeding_edge/test/cctest/test-api.cc
=======================================
--- /branches/bleeding_edge/include/v8.h Wed Dec 18 08:09:37 2013 UTC
+++ /branches/bleeding_edge/include/v8.h Wed Dec 18 18:38:35 2013 UTC
@@ -3918,6 +3918,8 @@
typedef void (*GCPrologueCallback)(GCType type, GCCallbackFlags flags);
typedef void (*GCEpilogueCallback)(GCType type, GCCallbackFlags flags);
+typedef void (*InterruptCallback)(Isolate* isolate, void* data);
+
/**
* Collection of V8 heap information.
@@ -4174,6 +4176,23 @@
*/
void RemoveGCEpilogueCallback(GCEpilogueCallback callback);
+ /**
+ * Request V8 to interrupt long running JavaScript code and invoke
+ * the given |callback| passing the given |data| to it. After |callback|
+ * returns control will be returned to the JavaScript code.
+ * At any given moment V8 can remember only a single callback for the
very
+ * last interrupt request.
+ * Can be called from another thread without acquiring a |Locker|.
+ * Registered |callback| must not reenter interrupted Isolate.
+ */
+ void RequestInterrupt(InterruptCallback callback, void* data);
+
+ /**
+ * Clear interrupt request created by |RequestInterrupt|.
+ * Can be called from another thread without acquiring a |Locker|.
+ */
+ void ClearInterrupt();
+
private:
Isolate();
Isolate(const Isolate&);
=======================================
--- /branches/bleeding_edge/src/api.cc Wed Dec 18 10:31:42 2013 UTC
+++ /branches/bleeding_edge/src/api.cc Wed Dec 18 18:38:35 2013 UTC
@@ -6498,6 +6498,17 @@
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i_isolate->stack_guard()->CancelTerminateExecution();
}
+
+
+void Isolate::RequestInterrupt(InterruptCallback callback, void* data) {
+ reinterpret_cast<i::Isolate*>(this)->stack_guard()->RequestInterrupt(
+ callback, data);
+}
+
+
+void Isolate::ClearInterrupt() {
+ reinterpret_cast<i::Isolate*>(this)->stack_guard()->ClearInterrupt();
+}
Isolate* Isolate::GetCurrent() {
=======================================
--- /branches/bleeding_edge/src/execution.cc Wed Nov 27 17:21:40 2013 UTC
+++ /branches/bleeding_edge/src/execution.cc Wed Dec 18 18:38:35 2013 UTC
@@ -538,6 +538,48 @@
reset_limits(access);
}
}
+
+
+void StackGuard::RequestInterrupt(InterruptCallback callback, void* data) {
+ ExecutionAccess access(isolate_);
+ thread_local_.interrupt_flags_ |= API_INTERRUPT;
+ thread_local_.interrupt_callback_ = callback;
+ thread_local_.interrupt_callback_data_ = data;
+ set_interrupt_limits(access);
+}
+
+
+void StackGuard::ClearInterrupt() {
+ thread_local_.interrupt_callback_ = 0;
+ thread_local_.interrupt_callback_data_ = 0;
+ Continue(API_INTERRUPT);
+}
+
+
+bool StackGuard::IsAPIInterrupt() {
+ ExecutionAccess access(isolate_);
+ return thread_local_.interrupt_flags_ & API_INTERRUPT;
+}
+
+
+void StackGuard::InvokeInterruptCallback() {
+ InterruptCallback callback = 0;
+ void* data = 0;
+
+ {
+ ExecutionAccess access(isolate_);
+ callback = thread_local_.interrupt_callback_;
+ data = thread_local_.interrupt_callback_data_;
+ thread_local_.interrupt_callback_ = NULL;
+ thread_local_.interrupt_callback_data_ = NULL;
+ }
+
+ if (callback != NULL) {
+ VMState<EXTERNAL> state(isolate_);
+ HandleScope handle_scope(isolate_);
+ callback(reinterpret_cast<v8::Isolate*>(isolate_), data);
+ }
+}
char* StackGuard::ArchiveStackGuard(char* to) {
@@ -581,6 +623,7 @@
nesting_ = 0;
postpone_interrupts_nesting_ = 0;
interrupt_flags_ = 0;
+ interrupt_callback_ = 0;
}
@@ -601,6 +644,7 @@
nesting_ = 0;
postpone_interrupts_nesting_ = 0;
interrupt_flags_ = 0;
+ interrupt_callback_ = 0;
return should_set_stack_limits;
}
@@ -935,6 +979,11 @@
if (stack_guard->ShouldPostponeInterrupts()) {
return isolate->heap()->undefined_value();
}
+
+ if (stack_guard->IsAPIInterrupt()) {
+ stack_guard->InvokeInterruptCallback();
+ stack_guard->Continue(API_INTERRUPT);
+ }
if (stack_guard->IsGCRequest()) {
isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags,
=======================================
--- /branches/bleeding_edge/src/execution.h Wed Nov 27 17:21:40 2013 UTC
+++ /branches/bleeding_edge/src/execution.h Wed Dec 18 18:38:35 2013 UTC
@@ -43,7 +43,8 @@
TERMINATE = 1 << 4,
GC_REQUEST = 1 << 5,
FULL_DEOPT = 1 << 6,
- INSTALL_CODE = 1 << 7
+ INSTALL_CODE = 1 << 7,
+ API_INTERRUPT = 1 << 8
};
@@ -222,6 +223,11 @@
void FullDeopt();
void Continue(InterruptFlag after_what);
+ void RequestInterrupt(InterruptCallback callback, void* data);
+ void ClearInterrupt();
+ bool IsAPIInterrupt();
+ void InvokeInterruptCallback();
+
// 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.
@@ -307,6 +313,9 @@
int nesting_;
int postpone_interrupts_nesting_;
int interrupt_flags_;
+
+ InterruptCallback interrupt_callback_;
+ void* interrupt_callback_data_;
};
// TODO(isolates): Technically this could be calculated directly from a
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.h Wed Dec 18 18:05:10
2013 UTC
+++ /branches/bleeding_edge/src/hydrogen-instructions.h Wed Dec 18 18:38:35
2013 UTC
@@ -1245,7 +1245,7 @@
virtual void Verify() V8_OVERRIDE;
#endif
- virtual bool IsCall() { return false; }
+ virtual bool HasStackCheck() { return false; }
DECLARE_ABSTRACT_INSTRUCTION(Instruction)
@@ -2242,8 +2242,6 @@
virtual int argument_delta() const V8_OVERRIDE {
return -argument_count();
}
-
- virtual bool IsCall() V8_FINAL V8_OVERRIDE { return true; }
private:
int argument_count_;
@@ -2315,6 +2313,12 @@
HValue* function() { return second(); }
Handle<JSFunction> known_function() { return known_function_; }
int formal_parameter_count() const { return formal_parameter_count_; }
+
+ virtual bool HasStackCheck() V8_FINAL V8_OVERRIDE {
+ return !known_function().is_null() &&
+ (known_function()->code()->kind() == Code::FUNCTION ||
+ known_function()->code()->kind() == Code::OPTIMIZED_FUNCTION);
+ }
DECLARE_CONCRETE_INSTRUCTION(InvokeFunction)
@@ -2347,6 +2351,11 @@
virtual Representation RequiredInputRepresentation(int index)
V8_OVERRIDE {
return Representation::None();
}
+
+ virtual bool HasStackCheck() V8_FINAL V8_OVERRIDE {
+ return (function()->code()->kind() == Code::FUNCTION ||
+ function()->code()->kind() == Code::OPTIMIZED_FUNCTION);
+ }
DECLARE_CONCRETE_INSTRUCTION(CallConstantFunction)
@@ -2464,6 +2473,11 @@
virtual Representation RequiredInputRepresentation(int index)
V8_OVERRIDE {
return Representation::None();
}
+
+ virtual bool HasStackCheck() V8_FINAL V8_OVERRIDE {
+ return (target()->code()->kind() == Code::FUNCTION ||
+ target()->code()->kind() == Code::OPTIMIZED_FUNCTION);
+ }
DECLARE_CONCRETE_INSTRUCTION(CallKnownGlobal)
=======================================
--- /branches/bleeding_edge/src/hydrogen-sce.cc Mon Jul 8 07:03:57 2013 UTC
+++ /branches/bleeding_edge/src/hydrogen-sce.cc Wed Dec 18 18:38:35 2013 UTC
@@ -43,7 +43,7 @@
HBasicBlock* dominator = back_edge;
while (true) {
for (HInstructionIterator it(dominator); !it.Done(); it.Advance())
{
- if (it.Current()->IsCall()) {
+ if (it.Current()->HasStackCheck()) {
block->loop_information()->stack_check()->Eliminate();
break;
}
=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc Wed Dec 18 10:31:42
2013 UTC
+++ /branches/bleeding_edge/test/cctest/test-api.cc Wed Dec 18 18:38:35
2013 UTC
@@ -20734,6 +20734,9 @@
THREADED_TEST(SemaphoreInterruption) {
ThreadInterruptTest().RunTest();
}
+
+
+#endif // V8_OS_POSIX
static bool NamedAccessAlwaysBlocked(Local<v8::Object> global,
@@ -21022,7 +21025,258 @@
}
-#endif // V8_OS_POSIX
+class RequestInterruptTestBase {
+ public:
+ RequestInterruptTestBase()
+ : env_(),
+ isolate_(env_->GetIsolate()),
+ sem_(0),
+ warmup_(20000),
+ should_continue_(true) {
+ }
+
+ virtual ~RequestInterruptTestBase() { }
+
+ virtual void TestBody() = 0;
+
+ void RunTest() {
+ i::FLAG_print_opt_code = true;
+ i::FLAG_code_comments = true;
+ i::FLAG_print_code_stubs = true;
+ InterruptThread i_thread(this);
+ i_thread.Start();
+
+ v8::HandleScope handle_scope(isolate_);
+
+ TestBody();
+
+ isolate_->ClearInterrupt();
+ }
+
+ void WakeUpInterruptor() {
+ sem_.Signal();
+ }
+
+ bool should_continue() const { return should_continue_; }
+
+ bool ShouldContinue() {
+ if (warmup_ > 0) {
+ if (--warmup_ == 0) {
+ WakeUpInterruptor();
+ }
+ }
+
+ return should_continue_;
+ }
+
+ protected:
+ static void ShouldContinueCallback(
+ const v8::FunctionCallbackInfo<Value>& info) {
+ RequestInterruptTestBase* test =
+ reinterpret_cast<RequestInterruptTestBase*>(
+ info.Data().As<v8::External>()->Value());
+ info.GetReturnValue().Set(test->ShouldContinue());
+ }
+
+ class InterruptThread : public i::Thread {
+ public:
+ explicit InterruptThread(RequestInterruptTestBase* test)
+ : Thread("RequestInterruptTest"), test_(test) {}
+
+ virtual void Run() {
+ test_->sem_.Wait();
+ test_->isolate_->RequestInterrupt(&OnInterrupt, test_);
+ }
+
+ static void OnInterrupt(v8::Isolate* isolate, void* data) {
+ reinterpret_cast<RequestInterruptTestBase*>(data)->
+ should_continue_ = false;
+ }
+
+ private:
+ RequestInterruptTestBase* test_;
+ };
+
+ LocalContext env_;
+ v8::Isolate* isolate_;
+ i::Semaphore sem_;
+ int warmup_;
+ bool should_continue_;
+};
+
+
+class RequestInterruptTestWithFunctionCall : public
RequestInterruptTestBase {
+ public:
+ virtual void TestBody() {
+ Local<Function> func = Function::New(
+ isolate_, ShouldContinueCallback, v8::External::New(isolate_,
this));
+ env_->Global()->Set(v8_str("ShouldContinue"), func);
+
+ CompileRun("while (ShouldContinue()) { }");
+ }
+};
+
+
+class RequestInterruptTestWithMethodCall : public RequestInterruptTestBase
{
+ public:
+ virtual void TestBody() {
+ v8::Local<v8::FunctionTemplate> t =
v8::FunctionTemplate::New(isolate_);
+ v8::Local<v8::Template> proto = t->PrototypeTemplate();
+ proto->Set(v8_str("shouldContinue"), Function::New(
+ isolate_, ShouldContinueCallback, v8::External::New(isolate_,
this)));
+ env_->Global()->Set(v8_str("Klass"), t->GetFunction());
+
+ CompileRun("var obj = new Klass; while (obj.shouldContinue()) { }");
+ }
+};
+
+
+class RequestInterruptTestWithAccessor : public RequestInterruptTestBase {
+ public:
+ virtual void TestBody() {
+ v8::Local<v8::FunctionTemplate> t =
v8::FunctionTemplate::New(isolate_);
+ v8::Local<v8::Template> proto = t->PrototypeTemplate();
+ proto->SetAccessorProperty(v8_str("shouldContinue"),
FunctionTemplate::New(
+ isolate_, ShouldContinueCallback, v8::External::New(isolate_,
this)));
+ env_->Global()->Set(v8_str("Klass"), t->GetFunction());
+
+ CompileRun("var obj = new Klass; while (obj.shouldContinue) { }");
+ }
+};
+
+
+class RequestInterruptTestWithNativeAccessor : public
RequestInterruptTestBase {
+ public:
+ virtual void TestBody() {
+ v8::Local<v8::FunctionTemplate> t =
v8::FunctionTemplate::New(isolate_);
+ v8::Local<v8::Template> proto = t->PrototypeTemplate();
+ proto->SetNativeDataProperty(v8_str("shouldContinue"),
+ &ShouldContinueNativeGetter,
+ NULL,
+ v8::External::New(isolate_, this));
+ env_->Global()->Set(v8_str("Klass"), t->GetFunction());
+
+ CompileRun("var obj = new Klass; while (obj.shouldContinue) { }");
+ }
+
+ private:
+ static void ShouldContinueNativeGetter(
+ Local<String> property,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ RequestInterruptTestBase* test =
+ reinterpret_cast<RequestInterruptTestBase*>(
+ info.Data().As<v8::External>()->Value());
+ info.GetReturnValue().Set(test->ShouldContinue());
+ }
+};
+
+
+class RequestInterruptTestWithMethodCallAndInterceptor
+ : public RequestInterruptTestBase {
+ public:
+ virtual void TestBody() {
+ v8::Local<v8::FunctionTemplate> t =
v8::FunctionTemplate::New(isolate_);
+ v8::Local<v8::Template> proto = t->PrototypeTemplate();
+ proto->Set(v8_str("shouldContinue"), Function::New(
+ isolate_, ShouldContinueCallback, v8::External::New(isolate_,
this)));
+ v8::Local<v8::ObjectTemplate> instance_template =
t->InstanceTemplate();
+ instance_template->SetNamedPropertyHandler(EmptyInterceptor);
+
+ env_->Global()->Set(v8_str("Klass"), t->GetFunction());
+
+ CompileRun("var obj = new Klass; while (obj.shouldContinue()) { }");
+ }
+
+ private:
+ static void EmptyInterceptor(
+ Local<String> property,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ }
+};
+
+
+class RequestInterruptTestWithMathAbs : public RequestInterruptTestBase {
+ public:
+ virtual void TestBody() {
+ env_->Global()->Set(v8_str("WakeUpInterruptor"), Function::New(
+ isolate_,
+ WakeUpInterruptorCallback,
+ v8::External::New(isolate_, this)));
+
+ env_->Global()->Set(v8_str("ShouldContinue"), Function::New(
+ isolate_,
+ ShouldContinueCallback,
+ v8::External::New(isolate_, this)));
+
+ i::FLAG_allow_natives_syntax = true;
+ CompileRun("function loopish(o) {"
+ " var pre = 10;"
+ " while (o.abs(1) > 0) {"
+ " if (o.abs(1) >= 0 && !ShouldContinue()) break;"
+ " if (pre > 0) {"
+ " if (--pre === 0) WakeUpInterruptor(o === Math);"
+ " }"
+ " }"
+ "}"
+ "var i = 50;"
+ "var obj = {abs: function () { return i-- }, x: null};"
+ "delete obj.x;"
+ "loopish(obj);"
+ "%OptimizeFunctionOnNextCall(loopish);"
+ "loopish(Math);");
+
+ i::FLAG_allow_natives_syntax = false;
+ }
+
+ private:
+ static void WakeUpInterruptorCallback(
+ const v8::FunctionCallbackInfo<Value>& info) {
+ if (!info[0]->BooleanValue()) return;
+
+ RequestInterruptTestBase* test =
+ reinterpret_cast<RequestInterruptTestBase*>(
+ info.Data().As<v8::External>()->Value());
+ test->WakeUpInterruptor();
+ }
+
+ static void ShouldContinueCallback(
+ const v8::FunctionCallbackInfo<Value>& info) {
+ RequestInterruptTestBase* test =
+ reinterpret_cast<RequestInterruptTestBase*>(
+ info.Data().As<v8::External>()->Value());
+ info.GetReturnValue().Set(test->should_continue());
+ }
+};
+
+
+THREADED_TEST(RequestInterruptTestWithFunctionCall) {
+ RequestInterruptTestWithFunctionCall().RunTest();
+}
+
+
+THREADED_TEST(RequestInterruptTestWithMethodCall) {
+ RequestInterruptTestWithMethodCall().RunTest();
+}
+
+
+THREADED_TEST(RequestInterruptTestWithAccessor) {
+ RequestInterruptTestWithAccessor().RunTest();
+}
+
+
+THREADED_TEST(RequestInterruptTestWithNativeAccessor) {
+ RequestInterruptTestWithNativeAccessor().RunTest();
+}
+
+
+THREADED_TEST(RequestInterruptTestWithMethodCallAndInterceptor) {
+ RequestInterruptTestWithMethodCallAndInterceptor().RunTest();
+}
+
+
+THREADED_TEST(RequestInterruptTestWithMathAbs) {
+ RequestInterruptTestWithMathAbs().RunTest();
+}
static Local<Value> function_new_expected_env;
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.