Fix shutdown race when running Dart on the message loop
- Shutdown the DartController when the content handler exits not when the content handler's message loop hits an error (which occurs when the final application closes its shell handle.)
Other fixes:
- DartMessageHandler considers the isolate as exited if it hit an unhandled exception error.
- DartMessageHandler can be configured to not quit the message loop when the isolate exits.
R=zra@google.com
Review URL: https://codereview.chromium.org/1688793002 .
diff --git a/services/dart/content_handler_main.cc b/services/dart/content_handler_main.cc
index ad599cc..219f2be 100644
--- a/services/dart/content_handler_main.cc
+++ b/services/dart/content_handler_main.cc
@@ -140,7 +140,11 @@
default_strict_(false),
run_on_message_loop_(false) {}
- ~DartContentHandlerApp() override {}
+ ~DartContentHandlerApp() override {
+ // Shutdown the controller.
+ mojo::dart::DartController::Shutdown();
+ delete service_connector_;
+ }
void ExtractApplication(base::FilePath* application_dir,
mojo::URLResponsePtr response,
@@ -242,13 +246,6 @@
return true;
}
- // Overridden from ApplicationDelegate:
- void Quit() override {
- // Shutdown the controller.
- mojo::dart::DartController::Shutdown();
- delete service_connector_;
- }
-
mojo::TracingImpl tracing_;
DartContentHandler content_handler_;
DartContentHandler strict_content_handler_;
diff --git a/tonic/dart_message_handler.cc b/tonic/dart_message_handler.cc
index 60cd502..85565e6 100644
--- a/tonic/dart_message_handler.cc
+++ b/tonic/dart_message_handler.cc
@@ -15,6 +15,9 @@
DartMessageHandler::DartMessageHandler()
: handled_first_message_(false),
+ quit_message_loop_when_isolate_exits_(true),
+ isolate_exited_(false),
+ isolate_had_uncaught_exception_error_(false),
task_runner_(nullptr) {
}
@@ -43,6 +46,8 @@
DartIsolateScope scope(dart_state->isolate());
DartApiScope dart_api_scope;
+ bool error = false;
+
// On the first message, check if we should pause on isolate start.
if (!handled_first_message()) {
set_handled_first_message(true);
@@ -62,7 +67,7 @@
}
Dart_SetPausedOnStart(false);
// We've resumed, handle *all* normal messages that are in the queue.
- LogIfError(Dart_HandleMessages());
+ error = LogIfError(Dart_HandleMessages());
}
} else if (Dart_IsPausedOnExit()) {
// We are paused on isolate exit. Only handle service messages until we are
@@ -76,17 +81,25 @@
}
} else {
// We are processing messages normally.
- LogIfError(Dart_HandleMessage());
+ error = LogIfError(Dart_HandleMessage());
}
- if (!Dart_HasLivePorts()) {
+ if (error) {
+ // Remember that we had an uncaught exception error.
+ isolate_had_uncaught_exception_error_ = true;
+ }
+
+ if (error || !Dart_HasLivePorts()) {
// The isolate has no live ports and would like to exit.
if (Dart_ShouldPauseOnExit()) {
// Mark that we are paused on exit.
Dart_SetPausedOnExit(true);
} else {
- // Quit.
- base::MessageLoop::current()->QuitWhenIdle();
+ isolate_exited_ = true;
+ if (quit_message_loop_when_isolate_exits()) {
+ // Quit.
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
}
}
}
diff --git a/tonic/dart_message_handler.h b/tonic/dart_message_handler.h
index 5cb2f9a..a6ab3d3 100644
--- a/tonic/dart_message_handler.h
+++ b/tonic/dart_message_handler.h
@@ -20,6 +20,27 @@
// Messages for the current isolate will be scheduled on |runner|.
void Initialize(const scoped_refptr<base::SingleThreadTaskRunner>& runner);
+ // Request the message loop to quit when isolate exits? Default is true.
+ void set_quit_message_loop_when_isolate_exits(
+ bool quit_message_loop_when_isolate_exits) {
+ quit_message_loop_when_isolate_exits_ =
+ quit_message_loop_when_isolate_exits;
+ }
+
+ bool quit_message_loop_when_isolate_exits() const {
+ return quit_message_loop_when_isolate_exits_;
+ }
+
+ // Did the isolate exit?
+ bool isolate_exited() const {
+ return isolate_exited_;
+ }
+
+ // Did the isolate have an uncaught exception error?
+ bool isolate_had_uncaught_exception_error() const {
+ return isolate_had_uncaught_exception_error_;
+ }
+
protected:
// Called from an unknown thread for each message.
void OnMessage(DartState* dart_state);
@@ -39,6 +60,9 @@
}
bool handled_first_message_;
+ bool quit_message_loop_when_isolate_exits_;
+ bool isolate_exited_;
+ bool isolate_had_uncaught_exception_error_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
private: