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:
Chris Jones 2012-09-19 16:13:31 -07:00
parent b4419c3dfa
commit e2c1ec4c18
3 changed files with 71 additions and 22 deletions

View File

@ -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

View File

@ -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);

View File

@ -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) {