Bug 793970 - Reuse nsAppStartup's watchdog to compulsively power-off/reboot/quit Gecko if profile synchronizing hangs. r=cjones

This commit is contained in:
Gene Lian 2012-10-02 15:26:32 +08:00
Родитель 33e222f68b
Коммит 56e7808397
11 изменённых файлов: 214 добавлений и 105 удалений

Просмотреть файл

@ -248,17 +248,16 @@ UpdatePrompt.prototype = {
restartProcess: function UP_restartProcess() {
log("Update downloaded, restarting to apply it");
#ifndef MOZ_WIDGET_GONK
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
.getService(Ci.nsIAppStartup);
// NB: on Gonk, we rely on the system process manager to restart
// us. Trying to restart here would conflict with the process
// manager. We should be using a runtime check to detect Gonk
// instead of this gross ifdef, but the ifdef works for now.
appStartup.quit(appStartup.eForceQuit
#ifndef ANDROID
| appStartup.eRestart
.getService(Ci.nsIAppStartup);
appStartup.quit(appStartup.eForceQuit | appStartup.eRestart);
#else
// NB: on Gonk, we rely on the system process manager to restart us.
let pmService = Cc["@mozilla.org/power/powermanagerservice;1"]
.getService(Ci.nsIPowerManagerService);
pmService.restart();
#endif
);
},
finishOSUpdate: function UP_finishOSUpdate(aOsUpdatePath) {

Просмотреть файл

@ -6,6 +6,7 @@
#include "mozilla/Hal.h"
#include "mozilla/HalWakeLock.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "nsIDOMWakeLockListener.h"
#include "nsIDOMWindow.h"
@ -13,15 +14,24 @@
#include "PowerManagerService.h"
#include "WakeLock.h"
// For _exit().
#ifdef XP_WIN
#include <process.h>
#else
#include <unistd.h>
#endif
namespace mozilla {
namespace dom {
namespace power {
using namespace hal;
NS_IMPL_ISUPPORTS1(PowerManagerService, nsIPowerManagerService)
/* static */ StaticRefPtr<PowerManagerService> PowerManagerService::sSingleton;
/* static */ already_AddRefed<nsIPowerManagerService>
/* static */ already_AddRefed<PowerManagerService>
PowerManagerService::GetInstance()
{
if (!sSingleton) {
@ -30,42 +40,48 @@ PowerManagerService::GetInstance()
ClearOnShutdown(&sSingleton);
}
nsCOMPtr<nsIPowerManagerService> service(do_QueryInterface(sSingleton));
nsRefPtr<PowerManagerService> service = sSingleton.get();
return service.forget();
}
void
PowerManagerService::Init()
{
hal::RegisterWakeLockObserver(this);
RegisterWakeLockObserver(this);
// 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.
mWatchdogTimeoutSecs =
Preferences::GetInt("shutdown.watchdog.timeoutSecs", 5);
}
PowerManagerService::~PowerManagerService()
{
hal::UnregisterWakeLockObserver(this);
UnregisterWakeLockObserver(this);
}
void
PowerManagerService::ComputeWakeLockState(const hal::WakeLockInformation& aWakeLockInfo,
PowerManagerService::ComputeWakeLockState(const WakeLockInformation& aWakeLockInfo,
nsAString &aState)
{
hal::WakeLockState state = hal::ComputeWakeLockState(aWakeLockInfo.numLocks(),
aWakeLockInfo.numHidden());
WakeLockState state = hal::ComputeWakeLockState(aWakeLockInfo.numLocks(),
aWakeLockInfo.numHidden());
switch (state) {
case hal::WAKE_LOCK_STATE_UNLOCKED:
case WAKE_LOCK_STATE_UNLOCKED:
aState.AssignLiteral("unlocked");
break;
case hal::WAKE_LOCK_STATE_HIDDEN:
case WAKE_LOCK_STATE_HIDDEN:
aState.AssignLiteral("locked-background");
break;
case hal::WAKE_LOCK_STATE_VISIBLE:
case WAKE_LOCK_STATE_VISIBLE:
aState.AssignLiteral("locked-foreground");
break;
}
}
void
PowerManagerService::Notify(const hal::WakeLockInformation& aWakeLockInfo)
PowerManagerService::Notify(const WakeLockInformation& aWakeLockInfo)
{
nsAutoString state;
ComputeWakeLockState(aWakeLockInfo, state);
@ -85,8 +101,6 @@ PowerManagerService::Notify(const hal::WakeLockInformation& aWakeLockInfo)
void
PowerManagerService::SyncProfile()
{
// FIXME/bug 793970: We need to start a watchdog thread to force gecko
// to really power off or reboot if the profile synchronizing hangs.
nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService();
if (obsServ) {
NS_NAMED_LITERAL_STRING(context, "shutdown-persist");
@ -99,6 +113,7 @@ PowerManagerService::SyncProfile()
NS_IMETHODIMP
PowerManagerService::Reboot()
{
StartForceQuitWatchdog(eHalShutdownMode_Reboot, mWatchdogTimeoutSecs);
// To synchronize any unsaved user data before rebooting.
SyncProfile();
hal::Reboot();
@ -109,6 +124,7 @@ PowerManagerService::Reboot()
NS_IMETHODIMP
PowerManagerService::PowerOff()
{
StartForceQuitWatchdog(eHalShutdownMode_PowerOff, mWatchdogTimeoutSecs);
// To synchronize any unsaved user data before powering off.
SyncProfile();
hal::PowerOff();
@ -116,6 +132,20 @@ PowerManagerService::PowerOff()
return NS_OK;
}
NS_IMETHODIMP
PowerManagerService::Restart()
{
// FIXME/bug 796826 this implementation is currently gonk-specific,
// because it relies on the Gonk to initialize the Gecko processes to
// restart B2G. It's better to do it here to have a real "restart".
StartForceQuitWatchdog(eHalShutdownMode_Restart, mWatchdogTimeoutSecs);
// To synchronize any unsaved user data before restarting.
SyncProfile();
_exit(0);
MOZ_NOT_REACHED();
return NS_OK;
}
NS_IMETHODIMP
PowerManagerService::AddWakeLockListener(nsIDOMMozWakeLockListener *aListener)
{
@ -136,8 +166,8 @@ PowerManagerService::RemoveWakeLockListener(nsIDOMMozWakeLockListener *aListener
NS_IMETHODIMP
PowerManagerService::GetWakeLockState(const nsAString &aTopic, nsAString &aState)
{
hal::WakeLockInformation info;
hal::GetWakeLockInfo(aTopic, &info);
WakeLockInformation info;
GetWakeLockInfo(aTopic, &info);
ComputeWakeLockState(info, aState);

Просмотреть файл

@ -26,7 +26,7 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPOWERMANAGERSERVICE
static already_AddRefed<nsIPowerManagerService> GetInstance();
static already_AddRefed<PowerManagerService> GetInstance();
void Init();
@ -45,6 +45,8 @@ private:
static StaticRefPtr<PowerManagerService> sSingleton;
nsTArray<nsCOMPtr<nsIDOMMozWakeLockListener> > mWakeLockListeners;
int32_t mWatchdogTimeoutSecs;
};
} // namespace power

Просмотреть файл

@ -17,11 +17,24 @@ interface nsIDOMWindow;
/**
* For use with non-content code.
*/
[scriptable, builtinclass, uuid(235ca1a1-d0c8-41f3-9b4a-dbaa4437d69c)]
[scriptable, builtinclass, uuid(a232e826-07bd-11e2-8a8f-236186ff1a14)]
interface nsIPowerManagerService : nsISupports
{
/**
* This API will power off the machine.
*/
void powerOff();
/**
* This API will completely shut down and boot the machine.
*/
void reboot();
/**
* This API will restart the Gecko processes without powering off the machine.
*/
void restart();
void addWakeLockListener(in nsIDOMMozWakeLockListener aListener);
void removeWakeLockListener(in nsIDOMMozWakeLockListener aListener);
DOMString getWakeLockState(in DOMString aTopic);

Просмотреть файл

@ -579,6 +579,13 @@ void PowerOff()
PROXY_IF_SANDBOXED(PowerOff());
}
void StartForceQuitWatchdog(ShutdownMode aMode, int32_t aTimeoutSecs)
{
AssertMainProcess();
AssertMainThread();
PROXY_IF_SANDBOXED(StartForceQuitWatchdog(aMode, aTimeoutSecs));
}
void
RegisterWakeLockObserver(WakeLockObserver* aObserver)
{

Просмотреть файл

@ -508,6 +508,15 @@ void CancelFMRadioSeek();
*/
hal::FMRadioSettings GetFMBandSettings(hal::FMRadioCountry aCountry);
/**
* Start a watchdog to compulsively shutdown the system if it hangs.
* @param aMode Specify how to shutdown the system.
* @param aTimeoutSecs Specify the delayed seconds to shutdown the system.
*
* This API is currently only allowed to be used from the main process.
*/
void StartForceQuitWatchdog(hal::ShutdownMode aMode, int32_t aTimeoutSecs);
} // namespace MOZ_HAL_NAMESPACE
} // namespace mozilla

Просмотреть файл

@ -17,26 +17,34 @@ namespace hal {
* If these change and are exposed to JS, make sure nsIHal.idl is updated as well.
*/
enum LightType {
eHalLightID_Backlight = 0,
eHalLightID_Keyboard = 1,
eHalLightID_Buttons = 2,
eHalLightID_Battery = 3,
eHalLightID_Notifications = 4,
eHalLightID_Attention = 5,
eHalLightID_Bluetooth = 6,
eHalLightID_Wifi = 7,
eHalLightID_Count = 8 // This should stay at the end
eHalLightID_Backlight = 0,
eHalLightID_Keyboard = 1,
eHalLightID_Buttons = 2,
eHalLightID_Battery = 3,
eHalLightID_Notifications = 4,
eHalLightID_Attention = 5,
eHalLightID_Bluetooth = 6,
eHalLightID_Wifi = 7,
eHalLightID_Count = 8 // This should stay at the end
};
enum LightMode {
eHalLightMode_User = 0, // brightness is managed by user setting
eHalLightMode_Sensor = 1, // brightness is managed by a light sensor
eHalLightMode_Count
eHalLightMode_User = 0, // brightness is managed by user setting
eHalLightMode_Sensor = 1, // brightness is managed by a light sensor
eHalLightMode_Count
};
enum FlashMode {
eHalLightFlash_None = 0,
eHalLightFlash_Timed = 1, // timed flashing. Use flashOnMS and flashOffMS for timing
eHalLightFlash_Hardware = 2, // hardware assisted flashing
eHalLightFlash_Count
eHalLightFlash_None = 0,
eHalLightFlash_Timed = 1, // timed flashing. Use flashOnMS and flashOffMS for timing
eHalLightFlash_Hardware = 2, // hardware assisted flashing
eHalLightFlash_Count
};
enum ShutdownMode {
eHalShutdownMode_Unknown = -1,
eHalShutdownMode_PowerOff = 0,
eHalShutdownMode_Reboot = 1,
eHalShutdownMode_Restart = 2,
eHalShutdownMode_Count = 3
};
class SwitchEvent;
@ -195,6 +203,16 @@ struct ParamTraits<mozilla::hal::FlashMode>
mozilla::hal::eHalLightFlash_Count>
{};
/**
* Serializer for ShutdownMode.
*/
template <>
struct ParamTraits<mozilla::hal::ShutdownMode>
: public EnumSerializer<mozilla::hal::ShutdownMode,
mozilla::hal::eHalShutdownMode_Unknown,
mozilla::hal::eHalShutdownMode_Count>
{};
/**
* WakeLockControl serializer.
*/

Просмотреть файл

@ -4,6 +4,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Hal.h"
namespace mozilla {
namespace hal_impl {
@ -15,5 +17,9 @@ void
PowerOff()
{}
void
StartForceQuitWatchdog(hal::ShutdownMode aMode, int32_t aTimeoutSecs)
{}
} // hal_impl
} // namespace mozilla

Просмотреть файл

@ -23,5 +23,87 @@ PowerOff()
reboot(RB_POWER_OFF);
}
// Structure to specify how watchdog pthread is going to work.
typedef struct watchdogParam
{
hal::ShutdownMode mode; // Specify how to shutdown the system.
int32_t timeoutSecs; // Specify the delayed seconds to shutdown the system.
watchdogParam(hal::ShutdownMode aMode, int32_t aTimeoutSecs)
: mode(aMode), timeoutSecs(aTimeoutSecs) {}
} watchdogParam_t;
// Function to complusively shut down the system with a given mode.
static void
QuitHard(hal::ShutdownMode aMode)
{
switch (aMode)
{
case hal::eHalShutdownMode_PowerOff:
PowerOff();
break;
case hal::eHalShutdownMode_Reboot:
Reboot();
break;
case hal::eHalShutdownMode_Restart:
// 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);
break;
default:
MOZ_NOT_REACHED();
break;
}
MOZ_NOT_REACHED();
}
// Function to complusively shut down the system with a given mode when timeout.
static void*
ForceQuitWatchdog(void* aParamPtr)
{
watchdogParam_t* paramPtr = reinterpret_cast<watchdogParam_t*>(aParamPtr);
if (paramPtr->timeoutSecs > 0 && paramPtr->timeoutSecs <= 30) {
// If we shut down normally before the timeout, this thread will
// be harmlessly reaped by the OS.
sleep(paramPtr->timeoutSecs);
}
hal::ShutdownMode mode = paramPtr->mode;
delete paramPtr;
QuitHard(mode);
return nullptr;
}
void
StartForceQuitWatchdog(hal::ShutdownMode aMode, int32_t aTimeoutSecs)
{
// 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.
if (aTimeoutSecs <= 0) {
return;
}
// Use a raw pthread here to insulate ourselves from bugs in other
// Gecko code that we're trying to protect!
//
// Note that we let the watchdog in charge of releasing |paramPtr|
// if the pthread is successfully created.
watchdogParam_t* paramPtr = new watchdogParam_t(aMode, aTimeoutSecs);
pthread_t watchdog;
if (pthread_create(&watchdog, nullptr,
ForceQuitWatchdog,
reinterpret_cast<void*>(paramPtr))) {
// Better safe than sorry.
delete paramPtr;
QuitHard(aMode);
}
// The watchdog thread is off and running now.
}
} // hal_impl
} // mozilla

Просмотреть файл

@ -227,6 +227,12 @@ PowerOff()
NS_RUNTIMEABORT("PowerOff() can't be called from sandboxed contexts.");
}
void
StartForceQuitWatchdog(ShutdownMode aMode, int32_t aTimeoutSecs)
{
NS_RUNTIMEABORT("StartForceQuitWatchdog() can't be called from sandboxed contexts.");
}
void
EnableSensorNotifications(SensorType aSensor) {
Hal()->SendEnableSensorNotifications(aSensor);

Просмотреть файл

@ -373,50 +373,6 @@ 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)
{
@ -462,25 +418,6 @@ 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) {