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) {
}
void HandleScopeImplementer::LeaveMicrotaskContext() {
DCHECK(microtask_context_);
microtask_context_ = nullptr;
entered_context_count_during_microtasks_ = 0;
}
......
......@@ -959,8 +959,10 @@ TF_BUILTIN(RunMicrotasks, InternalBuiltinsAssembler) {
// But from our current measurements it doesn't seem to be a
// serious performance problem, even if the microtask is full
// of CallHandlerTasks (which is not a realistic use case anyways).
CallRuntime(Runtime::kRunMicrotaskCallback, current_context,
microtask_callback, microtask_data);
Node* const result =
CallRuntime(Runtime::kRunMicrotaskCallback, current_context,
microtask_callback, microtask_data);
GotoIfException(result, &if_exception, &var_exception);
Goto(&loop_next);
}
......
......@@ -2764,6 +2764,12 @@ void Isolate::InitializeThreadLocal() {
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() {
Object* exception = pending_exception();
......@@ -2780,9 +2786,7 @@ bool Isolate::PropagatePendingExceptionToExternalTryCatch() {
thread_local_top_.external_caught_exception_ = true;
if (!is_catchable_by_javascript(exception)) {
try_catch_handler()->can_continue_ = false;
try_catch_handler()->has_terminated_ = true;
try_catch_handler()->exception_ = heap()->null_value();
SetTerminationOnExternalTryCatch();
} else {
v8::TryCatch* handler = try_catch_handler();
DCHECK(thread_local_top_.pending_message_obj_->IsJSMessageObject() ||
......@@ -3858,10 +3862,13 @@ void Isolate::RunMicrotasks() {
MaybeHandle<Object> maybe_exception;
MaybeHandle<Object> maybe_result = Execution::RunMicrotasks(
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()) {
heap()->set_microtask_queue(heap()->empty_fixed_array());
set_pending_microtask_count(0);
handle_scope_implementer()->LeaveMicrotaskContext();
SetTerminationOnExternalTryCatch();
}
CHECK_EQ(0, pending_microtask_count());
CHECK_EQ(0, heap()->microtask_queue()->length());
......
......@@ -1473,6 +1473,8 @@ class Isolate {
// then return true.
bool PropagatePendingExceptionToExternalTryCatch();
void SetTerminationOnExternalTryCatch();
const char* RAILModeName(RAILMode rail_mode) const {
switch (rail_mode) {
case PERFORMANCE_RESPONSE:
......
......@@ -74,6 +74,7 @@ RUNTIME_FUNCTION(Runtime_RunMicrotaskCallback) {
MicrotaskCallback callback = ToCData<MicrotaskCallback>(microtask_callback);
void* data = ToCData<void*>(microtask_data);
callback(data);
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
return isolate->heap()->undefined_value();
}
......
......@@ -21724,6 +21724,31 @@ TEST(RunMicrotasksIgnoresThrownExceptions) {
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;
......
......@@ -638,8 +638,6 @@ TEST(TerminateRegExp) {
#ifndef V8_INTERPRETED_REGEXP
i::FLAG_allow_natives_syntax = true;
v8::Isolate* isolate = CcTest::isolate();
ConsoleImpl console;
v8::debug::SetConsoleDelegate(isolate, &console);
v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
isolate, TerminateCurrentThread, DoLoopCancelTerminate);
......@@ -657,3 +655,63 @@ TEST(TerminateRegExp) {
CHECK(!isolate->IsExecutionTerminating());
#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