Commit a21d0552 authored by Yang Guo's avatar Yang Guo Committed by Commit Bot

Fix termination within microtasks.

Bug: v8:7552
Change-Id: I6bee9de640bae67e005fc174ea53875d79afc1ba
Reviewed-on: https://chromium-review.googlesource.com/964281
Commit-Queue: Yang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52037}
parent 7d5e6b15
...@@ -633,7 +633,6 @@ void HandleScopeImplementer::EnterMicrotaskContext(Handle<Context> context) { ...@@ -633,7 +633,6 @@ void HandleScopeImplementer::EnterMicrotaskContext(Handle<Context> context) {
} }
void HandleScopeImplementer::LeaveMicrotaskContext() { void HandleScopeImplementer::LeaveMicrotaskContext() {
DCHECK(microtask_context_);
microtask_context_ = nullptr; microtask_context_ = nullptr;
entered_context_count_during_microtasks_ = 0; entered_context_count_during_microtasks_ = 0;
} }
......
...@@ -959,8 +959,10 @@ TF_BUILTIN(RunMicrotasks, InternalBuiltinsAssembler) { ...@@ -959,8 +959,10 @@ TF_BUILTIN(RunMicrotasks, InternalBuiltinsAssembler) {
// But from our current measurements it doesn't seem to be a // But from our current measurements it doesn't seem to be a
// serious performance problem, even if the microtask is full // serious performance problem, even if the microtask is full
// of CallHandlerTasks (which is not a realistic use case anyways). // of CallHandlerTasks (which is not a realistic use case anyways).
CallRuntime(Runtime::kRunMicrotaskCallback, current_context, Node* const result =
microtask_callback, microtask_data); CallRuntime(Runtime::kRunMicrotaskCallback, current_context,
microtask_callback, microtask_data);
GotoIfException(result, &if_exception, &var_exception);
Goto(&loop_next); Goto(&loop_next);
} }
......
...@@ -2764,6 +2764,12 @@ void Isolate::InitializeThreadLocal() { ...@@ -2764,6 +2764,12 @@ void Isolate::InitializeThreadLocal() {
thread_local_top_.Initialize(); thread_local_top_.Initialize();
} }
void Isolate::SetTerminationOnExternalTryCatch() {
if (try_catch_handler() == nullptr) return;
try_catch_handler()->can_continue_ = false;
try_catch_handler()->has_terminated_ = true;
try_catch_handler()->exception_ = heap()->null_value();
}
bool Isolate::PropagatePendingExceptionToExternalTryCatch() { bool Isolate::PropagatePendingExceptionToExternalTryCatch() {
Object* exception = pending_exception(); Object* exception = pending_exception();
...@@ -2780,9 +2786,7 @@ bool Isolate::PropagatePendingExceptionToExternalTryCatch() { ...@@ -2780,9 +2786,7 @@ bool Isolate::PropagatePendingExceptionToExternalTryCatch() {
thread_local_top_.external_caught_exception_ = true; thread_local_top_.external_caught_exception_ = true;
if (!is_catchable_by_javascript(exception)) { if (!is_catchable_by_javascript(exception)) {
try_catch_handler()->can_continue_ = false; SetTerminationOnExternalTryCatch();
try_catch_handler()->has_terminated_ = true;
try_catch_handler()->exception_ = heap()->null_value();
} else { } else {
v8::TryCatch* handler = try_catch_handler(); v8::TryCatch* handler = try_catch_handler();
DCHECK(thread_local_top_.pending_message_obj_->IsJSMessageObject() || DCHECK(thread_local_top_.pending_message_obj_->IsJSMessageObject() ||
...@@ -3858,10 +3862,13 @@ void Isolate::RunMicrotasks() { ...@@ -3858,10 +3862,13 @@ void Isolate::RunMicrotasks() {
MaybeHandle<Object> maybe_exception; MaybeHandle<Object> maybe_exception;
MaybeHandle<Object> maybe_result = Execution::RunMicrotasks( MaybeHandle<Object> maybe_result = Execution::RunMicrotasks(
this, Execution::MessageHandling::kReport, &maybe_exception); this, Execution::MessageHandling::kReport, &maybe_exception);
// If execution is terminating, just bail out. // If execution is terminating, bail out, clean up, and propagate to
// TryCatch scope.
if (maybe_result.is_null() && maybe_exception.is_null()) { if (maybe_result.is_null() && maybe_exception.is_null()) {
heap()->set_microtask_queue(heap()->empty_fixed_array()); heap()->set_microtask_queue(heap()->empty_fixed_array());
set_pending_microtask_count(0); set_pending_microtask_count(0);
handle_scope_implementer()->LeaveMicrotaskContext();
SetTerminationOnExternalTryCatch();
} }
CHECK_EQ(0, pending_microtask_count()); CHECK_EQ(0, pending_microtask_count());
CHECK_EQ(0, heap()->microtask_queue()->length()); CHECK_EQ(0, heap()->microtask_queue()->length());
......
...@@ -1473,6 +1473,8 @@ class Isolate { ...@@ -1473,6 +1473,8 @@ class Isolate {
// then return true. // then return true.
bool PropagatePendingExceptionToExternalTryCatch(); bool PropagatePendingExceptionToExternalTryCatch();
void SetTerminationOnExternalTryCatch();
const char* RAILModeName(RAILMode rail_mode) const { const char* RAILModeName(RAILMode rail_mode) const {
switch (rail_mode) { switch (rail_mode) {
case PERFORMANCE_RESPONSE: case PERFORMANCE_RESPONSE:
......
...@@ -74,6 +74,7 @@ RUNTIME_FUNCTION(Runtime_RunMicrotaskCallback) { ...@@ -74,6 +74,7 @@ RUNTIME_FUNCTION(Runtime_RunMicrotaskCallback) {
MicrotaskCallback callback = ToCData<MicrotaskCallback>(microtask_callback); MicrotaskCallback callback = ToCData<MicrotaskCallback>(microtask_callback);
void* data = ToCData<void*>(microtask_data); void* data = ToCData<void*>(microtask_data);
callback(data); callback(data);
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
......
...@@ -21724,6 +21724,31 @@ TEST(RunMicrotasksIgnoresThrownExceptions) { ...@@ -21724,6 +21724,31 @@ TEST(RunMicrotasksIgnoresThrownExceptions) {
CompileRun("exception2Calls")->Int32Value(env.local()).FromJust()); CompileRun("exception2Calls")->Int32Value(env.local()).FromJust());
} }
static void ThrowExceptionMicrotask(void* data) {
CcTest::isolate()->ThrowException(v8_str("exception"));
}
int microtask_callback_count = 0;
static void IncrementCounterMicrotask(void* data) {
microtask_callback_count++;
}
TEST(RunMicrotasksIgnoresThrownExceptionsFromApi) {
LocalContext env;
v8::Isolate* isolate = CcTest::isolate();
isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit);
v8::HandleScope scope(isolate);
v8::TryCatch try_catch(isolate);
{
CHECK(!isolate->IsExecutionTerminating());
isolate->EnqueueMicrotask(ThrowExceptionMicrotask);
isolate->EnqueueMicrotask(IncrementCounterMicrotask);
isolate->RunMicrotasks();
CHECK_EQ(1, microtask_callback_count);
CHECK(!try_catch.HasCaught());
}
}
uint8_t microtasks_completed_callback_count = 0; uint8_t microtasks_completed_callback_count = 0;
......
...@@ -638,8 +638,6 @@ TEST(TerminateRegExp) { ...@@ -638,8 +638,6 @@ TEST(TerminateRegExp) {
#ifndef V8_INTERPRETED_REGEXP #ifndef V8_INTERPRETED_REGEXP
i::FLAG_allow_natives_syntax = true; i::FLAG_allow_natives_syntax = true;
v8::Isolate* isolate = CcTest::isolate(); v8::Isolate* isolate = CcTest::isolate();
ConsoleImpl console;
v8::debug::SetConsoleDelegate(isolate, &console);
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate( v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
isolate, TerminateCurrentThread, DoLoopCancelTerminate); isolate, TerminateCurrentThread, DoLoopCancelTerminate);
...@@ -657,3 +655,63 @@ TEST(TerminateRegExp) { ...@@ -657,3 +655,63 @@ TEST(TerminateRegExp) {
CHECK(!isolate->IsExecutionTerminating()); CHECK(!isolate->IsExecutionTerminating());
#endif // V8_INTERPRETED_REGEXP #endif // V8_INTERPRETED_REGEXP
} }
TEST(TerminateInMicrotask) {
v8::Isolate* isolate = CcTest::isolate();
v8::Locker locker(isolate);
isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit);
v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
isolate, TerminateCurrentThread, DoLoopCancelTerminate);
v8::Local<v8::Context> context1 = v8::Context::New(isolate, nullptr, global);
v8::Local<v8::Context> context2 = v8::Context::New(isolate, nullptr, global);
v8::TryCatch try_catch(isolate);
{
v8::Context::Scope context_scope(context1);
CHECK(!isolate->IsExecutionTerminating());
CHECK(!CompileRun("Promise.resolve().then(function() {"
"terminate(); loop(); fail();})")
.IsEmpty());
CHECK(!try_catch.HasCaught());
}
{
v8::Context::Scope context_scope(context2);
CHECK(context2 == isolate->GetCurrentContext());
CHECK(context2 == isolate->GetEnteredContext());
CHECK(!isolate->IsExecutionTerminating());
isolate->RunMicrotasks();
CHECK(context2 == isolate->GetCurrentContext());
CHECK(context2 == isolate->GetEnteredContext());
CHECK(try_catch.HasCaught());
CHECK(try_catch.HasTerminated());
}
CHECK(!isolate->IsExecutionTerminating());
}
void TerminationMicrotask(void* data) {
CcTest::isolate()->TerminateExecution();
CompileRun("");
}
void UnreachableMicrotask(void* data) { UNREACHABLE(); }
TEST(TerminateInApiMicrotask) {
v8::Isolate* isolate = CcTest::isolate();
v8::Locker locker(isolate);
isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit);
v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
isolate, TerminateCurrentThread, DoLoopCancelTerminate);
v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
v8::TryCatch try_catch(isolate);
{
v8::Context::Scope context_scope(context);
CHECK(!isolate->IsExecutionTerminating());
isolate->EnqueueMicrotask(TerminationMicrotask);
isolate->EnqueueMicrotask(UnreachableMicrotask);
isolate->RunMicrotasks();
CHECK(try_catch.HasCaught());
CHECK(try_catch.HasTerminated());
}
CHECK(!isolate->IsExecutionTerminating());
}
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