Revision: 21574
Author: [email protected]
Date: Wed May 28 18:40:04 2014 UTC
Log: Allow microtasks to throw exceptions and handle them gracefully
If the embedder calls V8::TerminateExecution while we're running
microtasks, bail out
and clear any pending microtasks.
All other exceptions are simply swallowed. No current Blink or V8
microtasks throw, this
just ensures something sane happens if another embedder decides to pass a
throwing
microtask (or if ours unexpectedly throw due to, e.g., stack exhaustion).
BUG=371566
LOG=Y
[email protected]
Review URL: https://codereview.chromium.org/294943009
http://code.google.com/p/v8/source/detail?r=21574
Modified:
/branches/bleeding_edge/include/v8.h
/branches/bleeding_edge/src/isolate.cc
/branches/bleeding_edge/test/cctest/test-api.cc
/branches/bleeding_edge/test/cctest/test-thread-termination.cc
=======================================
--- /branches/bleeding_edge/include/v8.h Mon May 26 09:36:13 2014 UTC
+++ /branches/bleeding_edge/include/v8.h Wed May 28 18:40:04 2014 UTC
@@ -4380,6 +4380,7 @@
/**
* Experimental: Runs the Microtask Work Queue until empty
+ * Any exceptions thrown by microtask callbacks are swallowed.
*/
void RunMicrotasks();
=======================================
--- /branches/bleeding_edge/src/isolate.cc Tue May 27 13:20:58 2014 UTC
+++ /branches/bleeding_edge/src/isolate.cc Wed May 28 18:40:04 2014 UTC
@@ -2254,11 +2254,11 @@
if (!handle_scope_implementer()->CallDepthIsZero()) return;
if (run_microtasks) RunMicrotasks();
// Fire callbacks. Increase call depth to prevent recursive callbacks.
- handle_scope_implementer()->IncrementCallDepth();
+ v8::Isolate::SuppressMicrotaskExecutionScope suppress(
+ reinterpret_cast<v8::Isolate*>(this));
for (int i = 0; i < call_completed_callbacks_.length(); i++) {
call_completed_callbacks_.at(i)();
}
- handle_scope_implementer()->DecrementCallDepth();
}
@@ -2288,7 +2288,8 @@
// ASSERT(handle_scope_implementer()->CallDepthIsZero());
// Increase call depth to prevent recursive callbacks.
- handle_scope_implementer()->IncrementCallDepth();
+ v8::Isolate::SuppressMicrotaskExecutionScope suppress(
+ reinterpret_cast<v8::Isolate*>(this));
while (pending_microtask_count() > 0) {
HandleScope scope(this);
@@ -2301,13 +2302,20 @@
for (int i = 0; i < num_tasks; i++) {
HandleScope scope(this);
Handle<JSFunction> microtask(JSFunction::cast(queue->get(i)), this);
- // TODO(adamk): This should ignore/clear exceptions instead of
Checking.
- Execution::Call(this, microtask, factory()->undefined_value(),
- 0, NULL).Check();
+ Handle<Object> exception;
+ MaybeHandle<Object> result = Execution::TryCall(
+ microtask, factory()->undefined_value(), 0, NULL, &exception);
+ // If execution is terminating, just bail out.
+ if (result.is_null() &&
+ !exception.is_null() &&
+ *exception == heap()->termination_exception()) {
+ // Clear out any remaining callbacks in the queue.
+ heap()->set_microtask_queue(heap()->empty_fixed_array());
+ set_pending_microtask_count(0);
+ return;
+ }
}
}
-
- handle_scope_implementer()->DecrementCallDepth();
}
=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc Wed May 28 09:58:27
2014 UTC
+++ /branches/bleeding_edge/test/cctest/test-api.cc Wed May 28 18:40:04
2014 UTC
@@ -20775,6 +20775,43 @@
CHECK_EQ(2, CompileRun("ext1Calls")->Int32Value());
CHECK_EQ(2, CompileRun("ext2Calls")->Int32Value());
}
+
+
+static void MicrotaskExceptionOne(
+ const v8::FunctionCallbackInfo<Value>& info) {
+ v8::HandleScope scope(info.GetIsolate());
+ CompileRun("exception1Calls++;");
+ info.GetIsolate()->ThrowException(
+ v8::Exception::Error(v8_str("first")));
+}
+
+
+static void MicrotaskExceptionTwo(
+ const v8::FunctionCallbackInfo<Value>& info) {
+ v8::HandleScope scope(info.GetIsolate());
+ CompileRun("exception2Calls++;");
+ info.GetIsolate()->ThrowException(
+ v8::Exception::Error(v8_str("second")));
+}
+
+
+TEST(RunMicrotasksIgnoresThrownExceptions) {
+ LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+ CompileRun(
+ "var exception1Calls = 0;"
+ "var exception2Calls = 0;");
+ isolate->EnqueueMicrotask(
+ Function::New(isolate, MicrotaskExceptionOne));
+ isolate->EnqueueMicrotask(
+ Function::New(isolate, MicrotaskExceptionTwo));
+ TryCatch try_catch;
+ CompileRun("1+1;");
+ CHECK(!try_catch.HasCaught());
+ CHECK_EQ(1, CompileRun("exception1Calls")->Int32Value());
+ CHECK_EQ(1, CompileRun("exception2Calls")->Int32Value());
+}
TEST(SetAutorunMicrotasks) {
=======================================
--- /branches/bleeding_edge/test/cctest/test-thread-termination.cc Wed Jan
8 06:53:31 2014 UTC
+++ /branches/bleeding_edge/test/cctest/test-thread-termination.cc Wed May
28 18:40:04 2014 UTC
@@ -358,3 +358,47 @@
// Check that execution completed with correct return value.
CHECK(v8::Script::Compile(source)->Run()->Equals(v8_str("completed")));
}
+
+
+void MicrotaskShouldNotRun(const v8::FunctionCallbackInfo<v8::Value>&
info) {
+ CHECK(false);
+}
+
+
+void MicrotaskLoopForever(const v8::FunctionCallbackInfo<v8::Value>& info)
{
+ v8::Isolate* isolate = info.GetIsolate();
+ v8::HandleScope scope(isolate);
+ // Enqueue another should-not-run task to ensure we clean out the queue
+ // when we terminate.
+ isolate->EnqueueMicrotask(v8::Function::New(isolate,
MicrotaskShouldNotRun));
+ CompileRun("terminate(); while (true) { }");
+ CHECK(v8::V8::IsExecutionTerminating());
+}
+
+
+TEST(TerminateFromOtherThreadWhileMicrotaskRunning) {
+ semaphore = new v8::internal::Semaphore(0);
+ TerminatorThread thread(CcTest::i_isolate());
+ thread.Start();
+
+ v8::Isolate* isolate = CcTest::isolate();
+ isolate->SetAutorunMicrotasks(false);
+ v8::HandleScope scope(isolate);
+ v8::Handle<v8::ObjectTemplate> global =
+ CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
+ v8::Handle<v8::Context> context =
+ v8::Context::New(CcTest::isolate(), NULL, global);
+ v8::Context::Scope context_scope(context);
+ isolate->EnqueueMicrotask(v8::Function::New(isolate,
MicrotaskLoopForever));
+ // The second task should never be run because we bail out if we're
+ // terminating.
+ isolate->EnqueueMicrotask(v8::Function::New(isolate,
MicrotaskShouldNotRun));
+ isolate->RunMicrotasks();
+
+ v8::V8::CancelTerminateExecution(isolate);
+ isolate->RunMicrotasks(); // should not run MicrotaskShouldNotRun
+
+ thread.Join();
+ delete semaphore;
+ semaphore = NULL;
+}
--
--
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/d/optout.