mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 787436: Implement a "really really quit" watchdog to monitor normal shutdown and exit Gecko in a hurry if it takes too long. r=bent
This commit is contained in:
parent
b4419c3dfa
commit
e2c1ec4c18
@ -436,6 +436,9 @@ pref("marionette.defaultPrefs.port", 2828);
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_UPDATER
|
||||
// When we're applying updates, we can't let anything hang us on
|
||||
// quit+restart. The user has no recourse.
|
||||
pref("shutdown.watchdog.timeoutSecs", 5);
|
||||
// Timeout before the update prompt automatically installs the update
|
||||
pref("b2g.update.apply-prompt-timeout", 60000); // milliseconds
|
||||
// Optional timeout the user can wait before getting another update prompt
|
||||
@ -466,6 +469,10 @@ pref("app.update.download.backgroundInterval", 0);
|
||||
// Enable update logging for now, to diagnose growing pains in the
|
||||
// field.
|
||||
pref("app.update.log", true);
|
||||
#else
|
||||
// Explicitly disable the shutdown watchdog. It's enabled by default.
|
||||
// When the updater is disabled, we want to know about shutdown hangs.
|
||||
pref("shutdown.watchdog.timeoutSecs", -1);
|
||||
#endif
|
||||
|
||||
// Extensions preferences
|
||||
|
@ -40,7 +40,6 @@ UpdatePrompt.prototype = {
|
||||
_update: null,
|
||||
_applyPromptTimer: null,
|
||||
_applyWaitTimer: null,
|
||||
_selfDestructTimer: null,
|
||||
|
||||
// nsIUpdatePrompt
|
||||
|
||||
@ -182,10 +181,6 @@ UpdatePrompt.prototype = {
|
||||
restartProcess: function UP_restartProcess() {
|
||||
log("Update downloaded, restarting to apply it");
|
||||
|
||||
// If not cleanly shut down within 5 seconds, this process will
|
||||
// explode.
|
||||
this._selfDestructTimer = this.createTimer(SELF_DESTRUCT_TIMEOUT);
|
||||
|
||||
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
|
||||
.getService(Ci.nsIAppStartup);
|
||||
// NB: on Gonk, we rely on the system process manager to restart
|
||||
@ -200,10 +195,7 @@ UpdatePrompt.prototype = {
|
||||
},
|
||||
|
||||
notify: function UP_notify(aTimer) {
|
||||
if (aTimer == this._selfDestructTimer) {
|
||||
this._selfDestructTimer = null;
|
||||
this.selfDestruct();
|
||||
} else if (aTimer == this._applyPromptTimer) {
|
||||
if (aTimer == this._applyPromptTimer) {
|
||||
log("Timed out waiting for result, restarting");
|
||||
this._applyPromptTimer = null;
|
||||
this.restartProcess();
|
||||
@ -213,19 +205,6 @@ UpdatePrompt.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
selfDestruct: function UP_selfDestruct() {
|
||||
#ifdef ANDROID
|
||||
Cu.import("resource://gre/modules/ctypes.jsm");
|
||||
let libc = ctypes.open("libc.so");
|
||||
let _exit = libc.declare("_exit", ctypes.default_abi,
|
||||
ctypes.void_t, // [return]
|
||||
ctypes.int); // status
|
||||
|
||||
log("Self-destruct timer fired; didn't cleanly shut down. BOOM");
|
||||
_exit(0);
|
||||
#endif
|
||||
},
|
||||
|
||||
createTimer: function UP_createTimer(aTimeoutMs) {
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback(this, aTimeoutMs, timer.TYPE_ONE_SHOT);
|
||||
|
@ -373,6 +373,50 @@ RecordShutdownEndTimeStamp() {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
static void
|
||||
QuitHard()
|
||||
{
|
||||
// Don't let signal handlers affect forced shutdown.
|
||||
kill(0, SIGKILL);
|
||||
// If we can't SIGKILL our process group, something is badly
|
||||
// wrong. Trying to deliver a catch-able signal to ourselves can
|
||||
// invoke signal handlers and might cause problems. So try
|
||||
// _exit() and hope we go away.
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
static void*
|
||||
ForceQuitWatchdog(void* aTimeoutSecs)
|
||||
{
|
||||
int32_t timeoutSecs = int32_t(intptr_t(aTimeoutSecs));
|
||||
if (timeoutSecs > 0 && timeoutSecs <= 30) {
|
||||
// If we shut down normally before the timeout, this thread will
|
||||
// be harmlessly reaped by the OS.
|
||||
sleep(timeoutSecs);
|
||||
}
|
||||
|
||||
QuitHard();
|
||||
MOZ_NOT_REACHED();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void
|
||||
StartForceQuitWatchdog(int32_t aTimeoutSecs)
|
||||
{
|
||||
// Use a raw pthread here to insulate ourselves from bugs in other
|
||||
// Gecko code that we're trying to protect!
|
||||
pthread_t watchdog;
|
||||
if (pthread_create(&watchdog, nullptr,
|
||||
ForceQuitWatchdog,
|
||||
reinterpret_cast<void*>(intptr_t(aTimeoutSecs)))) {
|
||||
// Better safe than sorry.
|
||||
QuitHard();
|
||||
}
|
||||
// The watchdog thread is off and running now.
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAppStartup::Quit(uint32_t aMode)
|
||||
{
|
||||
@ -418,6 +462,25 @@ nsAppStartup::Quit(uint32_t aMode)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Force-quits are intepreted a little more ferociously on Gonk,
|
||||
// because while Gecko is in the process of shutting down, the user
|
||||
// can't call 911, for example. And if we hang on shutdown, bad
|
||||
// things happen. So, make sure that doesn't happen.
|
||||
//
|
||||
// We rely on a service manager to restart us, so we can get away
|
||||
// with these kind of shenanigans.
|
||||
//
|
||||
// NB: default to *enabling* the watchdog even when the pref is
|
||||
// absent, in case the profile might be damaged and we need to
|
||||
// restart to repair it.
|
||||
int32_t timeoutSecs =
|
||||
Preferences::GetInt("shutdown.watchdog.timeoutSecs", 5);
|
||||
if (ferocity == eForceQuit && timeoutSecs > 0) {
|
||||
StartForceQuitWatchdog(timeoutSecs);
|
||||
}
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIObserverService> obsService;
|
||||
if (ferocity == eAttemptQuit || ferocity == eForceQuit) {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user