Make process.exit() quit gracefully

Instead of abrupting the whole program immediately, we should close all
windows and release all native resources gracefully on exit. This avoids
possible crashes.

Fix #3350.
This commit is contained in:
Cheng Zhao 2015-11-06 18:27:13 +08:00
parent c10c74b23a
commit 863199348f
8 changed files with 59 additions and 1 deletions

View file

@ -339,6 +339,7 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder(
auto browser = base::Unretained(Browser::Get()); auto browser = base::Unretained(Browser::Get());
return mate::ObjectTemplateBuilder(isolate) return mate::ObjectTemplateBuilder(isolate)
.SetMethod("quit", base::Bind(&Browser::Quit, browser)) .SetMethod("quit", base::Bind(&Browser::Quit, browser))
.SetMethod("exit", base::Bind(&Browser::Exit, browser))
.SetMethod("focus", base::Bind(&Browser::Focus, browser)) .SetMethod("focus", base::Bind(&Browser::Focus, browser))
.SetMethod("getVersion", base::Bind(&Browser::GetVersion, browser)) .SetMethod("getVersion", base::Bind(&Browser::GetVersion, browser))
.SetMethod("setVersion", base::Bind(&Browser::SetVersion, browser)) .SetMethod("setVersion", base::Bind(&Browser::SetVersion, browser))

View file

@ -50,7 +50,6 @@ app.getAppPath = ->
# Be compatible with old API. # Be compatible with old API.
app.once 'ready', -> @emit 'finish-launching' app.once 'ready', -> @emit 'finish-launching'
app.terminate = app.quit app.terminate = app.quit
app.exit = process.exit
app.getHomeDir = -> @getPath 'home' app.getHomeDir = -> @getPath 'home'
app.getDataPath = -> @getPath 'userData' app.getDataPath = -> @getPath 'userData'
app.setDataPath = (path) -> @setPath 'userData', path app.setDataPath = (path) -> @setPath 'userData', path

View file

@ -30,6 +30,7 @@ AtomBrowserMainParts* AtomBrowserMainParts::self_ = NULL;
AtomBrowserMainParts::AtomBrowserMainParts() AtomBrowserMainParts::AtomBrowserMainParts()
: fake_browser_process_(new BrowserProcess), : fake_browser_process_(new BrowserProcess),
exit_code_(nullptr),
browser_(new Browser), browser_(new Browser),
node_bindings_(NodeBindings::Create(true)), node_bindings_(NodeBindings::Create(true)),
atom_bindings_(new AtomBindings), atom_bindings_(new AtomBindings),
@ -47,6 +48,14 @@ AtomBrowserMainParts* AtomBrowserMainParts::Get() {
return self_; return self_;
} }
bool AtomBrowserMainParts::SetExitCode(int code) {
if (!exit_code_)
return false;
*exit_code_ = code;
return true;
}
void AtomBrowserMainParts::RegisterDestructionCallback( void AtomBrowserMainParts::RegisterDestructionCallback(
const base::Closure& callback) { const base::Closure& callback) {
destruction_callbacks_.push_back(callback); destruction_callbacks_.push_back(callback);
@ -118,6 +127,11 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() {
#endif #endif
} }
bool AtomBrowserMainParts::MainMessageLoopRun(int* result_code) {
exit_code_ = result_code;
return brightray::BrowserMainParts::MainMessageLoopRun(result_code);
}
void AtomBrowserMainParts::PostMainMessageLoopStart() { void AtomBrowserMainParts::PostMainMessageLoopStart() {
brightray::BrowserMainParts::PostMainMessageLoopStart(); brightray::BrowserMainParts::PostMainMessageLoopStart();
#if defined(OS_POSIX) #if defined(OS_POSIX)

View file

@ -31,6 +31,9 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
static AtomBrowserMainParts* Get(); static AtomBrowserMainParts* Get();
// Sets the exit code, will fail if the the message loop is not ready.
bool SetExitCode(int code);
// Register a callback that should be destroyed before JavaScript environment // Register a callback that should be destroyed before JavaScript environment
// gets destroyed. // gets destroyed.
void RegisterDestructionCallback(const base::Closure& callback); void RegisterDestructionCallback(const base::Closure& callback);
@ -42,6 +45,7 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
void PreEarlyInitialization() override; void PreEarlyInitialization() override;
void PostEarlyInitialization() override; void PostEarlyInitialization() override;
void PreMainMessageLoopRun() override; void PreMainMessageLoopRun() override;
bool MainMessageLoopRun(int* result_code) override;
void PostMainMessageLoopStart() override; void PostMainMessageLoopStart() override;
void PostMainMessageLoopRun() override; void PostMainMessageLoopRun() override;
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
@ -66,6 +70,9 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
// with a task runner that will post all work to main loop. // with a task runner that will post all work to main loop.
scoped_refptr<BridgeTaskRunner> bridge_task_runner_; scoped_refptr<BridgeTaskRunner> bridge_task_runner_;
// Pointer to exit code.
int* exit_code_;
scoped_ptr<Browser> browser_; scoped_ptr<Browser> browser_;
scoped_ptr<JavascriptEnvironment> js_env_; scoped_ptr<JavascriptEnvironment> js_env_;
scoped_ptr<NodeBindings> node_bindings_; scoped_ptr<NodeBindings> node_bindings_;

View file

@ -7,6 +7,7 @@
#include <string> #include <string>
#include "atom/browser/atom_browser_main_parts.h" #include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/native_window.h"
#include "atom/browser/window_list.h" #include "atom/browser/window_list.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "content/public/browser/client_certificate_delegate.h" #include "content/public/browser/client_certificate_delegate.h"
@ -45,6 +46,27 @@ void Browser::Quit() {
window_list->CloseAllWindows(); window_list->CloseAllWindows();
} }
void Browser::Exit(int code) {
if (!AtomBrowserMainParts::Get()->SetExitCode(code)) {
// Message loop is not ready, quit directly.
exit(code);
} else {
// Prepare to quit when all windows have been closed..
is_quiting_ = true;
// Must destroy windows before quitting, otherwise bad things can happen.
atom::WindowList* window_list = atom::WindowList::GetInstance();
if (window_list->size() == 0) {
NotifyAndShutdown();
} else {
// Unlike Quit(), we do not ask to close window, but destroy the window
// without asking.
for (NativeWindow* window : *window_list)
window->CloseContents(nullptr); // e.g. Destroy()
}
}
}
void Browser::Shutdown() { void Browser::Shutdown() {
if (is_shutdown_) if (is_shutdown_)
return; return;

View file

@ -42,6 +42,9 @@ class Browser : public WindowListObserver {
// Try to close all windows and quit the application. // Try to close all windows and quit the application.
void Quit(); void Quit();
// Exit the application immediately and set exit code.
void Exit(int code);
// Cleanup everything and shutdown the application gracefully. // Cleanup everything and shutdown the application gracefully.
void Shutdown(); void Shutdown();

View file

@ -53,6 +53,9 @@ app = require 'app'
app.on 'quit', -> app.on 'quit', ->
process.emit 'exit' process.emit 'exit'
# Map process.exit to app.exit, which quits gracefully.
process.exit = app.exit
# Load the RPC server. # Load the RPC server.
require './rpc-server' require './rpc-server'

View file

@ -208,6 +208,15 @@ This method guarantees that all `beforeunload` and `unload` event handlers are
correctly executed. It is possible that a window cancels the quitting by correctly executed. It is possible that a window cancels the quitting by
returning `false` in the `beforeunload` event handler. returning `false` in the `beforeunload` event handler.
### `app.exit(exitCode)`
* `exitCode` Integer
Exits immediately with `exitCode`.
All windows will be closed immediately without asking user and the `before-quit`
and `will-quit` events will not be emitted.
### `app.getAppPath()` ### `app.getAppPath()`
Returns the current application directory. Returns the current application directory.