зеркало из https://github.com/mozilla/gecko-dev.git
Bug 793970 - Reuse nsAppStartup's watchdog to compulsively power-off/reboot/quit Gecko if profile synchronizing hangs. r=cjones
This commit is contained in:
Родитель
33e222f68b
Коммит
56e7808397
|
@ -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) {
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче