Bug 1476250 - Simplify HAL initialization and shutdown to reduce the chance of leaks and UAFs r=froydnj
authorGabriele Svelto <gsvelto@mozilla.com>
Fri, 31 Aug 2018 20:29:30 +0000
changeset 434930 1616abd4c12f3d5ca793c0d332b1738e93803ddd
parent 434929 3430ddb28e8408ec10e625494dcc21264d70a8ac
child 434931 270e36de2a74631387f512b506b1aa095fd44cc7
push id107506
push userdluca@mozilla.com
push dateWed, 05 Sep 2018 16:08:26 +0000
treeherdermozilla-inbound@98a35e16a9df [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1476250
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1476250 - Simplify HAL initialization and shutdown to reduce the chance of leaks and UAFs r=froydnj This patch initializes some HAL components greedily so that we can get rid of lazy initializers within the code. Observers are still lazily initialized because they can be instanced within content processes but that doesn't always happen and we don't want to pay the memory price for structures we don't use. Shutdown is now happening at a fixed time for all HAL components save WakeLocks. This ensures that we don't destroy an object while still iterating over it, something that could happen before. Finally a workaround for a compiler limitation has been removed. Differential Revision: https://phabricator.services.mozilla.com/D3100
hal/Hal.cpp
hal/Hal.h
hal/HalWakeLock.cpp
hal/HalWakeLockInternal.h
widget/android/nsAppShell.cpp
widget/android/nsAppShell.h
widget/cocoa/nsAppShell.mm
widget/gtk/nsAppShell.cpp
widget/windows/nsAppShell.cpp
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -4,16 +4,17 @@
  * 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"
 
 #include "HalImpl.h"
 #include "HalLog.h"
 #include "HalSandbox.h"
+#include "HalWakeLockInternal.h"
 #include "nsIDOMWindow.h"
 #include "nsIDocument.h"
 #include "nsIDocShell.h"
 #include "nsITabChild.h"
 #include "nsIWebNavigation.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "nsPIDOMWindow.h"
@@ -52,16 +53,18 @@ using namespace mozilla::dom;
     } else {                                      \
       return hal_impl::_call;                     \
     }                                             \
   } while (0)
 
 namespace mozilla {
 namespace hal {
 
+static bool sInitialized = false;
+
 mozilla::LogModule *
 GetHalLog()
 {
   static mozilla::LazyLogModule sHalLog("hal");
   return sHalLog;
 }
 
 namespace {
@@ -90,20 +93,45 @@ WindowIsActive(nsPIDOMWindowInner* aWind
   nsIDocument* document = aWindow->GetDoc();
   NS_ENSURE_TRUE(document, false);
 
   return !document->Hidden();
 }
 
 StaticAutoPtr<WindowIdentifier::IDArrayType> gLastIDToVibrate;
 
-void InitLastIDToVibrate()
+static void
+RecordLastIDToVibrate(const WindowIdentifier& aId)
+{
+  if (!InSandbox()) {
+    *gLastIDToVibrate = aId.AsArray();
+  }
+}
+
+static bool
+MayCancelVibration(const WindowIdentifier& aId)
 {
-  gLastIDToVibrate = new WindowIdentifier::IDArrayType();
-  ClearOnShutdown(&gLastIDToVibrate);
+  // Although only active windows may start vibrations, a window may
+  // cancel its own vibration even if it's no longer active.
+  //
+  // After a window is marked as inactive, it sends a CancelVibrate
+  // request.  We want this request to cancel a playing vibration
+  // started by that window, so we certainly don't want to reject the
+  // cancellation request because the window is now inactive.
+  //
+  // But it could be the case that, after this window became inactive,
+  // some other window came along and started a vibration.  We don't
+  // want this window's cancellation request to cancel that window's
+  // actively-playing vibration!
+  //
+  // To solve this problem, we keep track of the id of the last window
+  // to start a vibration, and only accepts cancellation requests from
+  // the same window.  All other cancellation requests are ignored.
+
+  return InSandbox() || (*gLastIDToVibrate == aId.AsArray());
 }
 
 } // namespace
 
 void
 Vibrate(const nsTArray<uint32_t>& pattern, nsPIDOMWindowInner* window)
 {
   Vibrate(pattern, WindowIdentifier(window));
@@ -120,22 +148,17 @@ Vibrate(const nsTArray<uint32_t>& patter
   // |id| has gone through IPC, don't check the window's visibility;
   // only the window corresponding to the bottommost process has its
   // visibility state set correctly.
   if (!id.HasTraveledThroughIPC() && !WindowIsActive(id.GetWindow())) {
     HAL_LOG("Vibrate: Window is inactive, dropping vibrate.");
     return;
   }
 
-  if (!InSandbox()) {
-    if (!gLastIDToVibrate) {
-      InitLastIDToVibrate();
-    }
-    *gLastIDToVibrate = id.AsArray();
-  }
+  RecordLastIDToVibrate(id);
 
   // Don't forward our ID if we are not in the sandbox, because hal_impl
   // doesn't need it, and we don't want it to be tempted to read it.  The
   // empty identifier will assert if it's used.
   PROXY_IF_SANDBOXED(Vibrate(pattern, InSandbox() ? id : WindowIdentifier()));
 }
 
 void
@@ -144,92 +167,63 @@ CancelVibrate(nsPIDOMWindowInner* window
   CancelVibrate(WindowIdentifier(window));
 }
 
 void
 CancelVibrate(const WindowIdentifier &id)
 {
   AssertMainThread();
 
-  // Although only active windows may start vibrations, a window may
-  // cancel its own vibration even if it's no longer active.
-  //
-  // After a window is marked as inactive, it sends a CancelVibrate
-  // request.  We want this request to cancel a playing vibration
-  // started by that window, so we certainly don't want to reject the
-  // cancellation request because the window is now inactive.
-  //
-  // But it could be the case that, after this window became inactive,
-  // some other window came along and started a vibration.  We don't
-  // want this window's cancellation request to cancel that window's
-  // actively-playing vibration!
-  //
-  // To solve this problem, we keep track of the id of the last window
-  // to start a vibration, and only accepts cancellation requests from
-  // the same window.  All other cancellation requests are ignored.
-
-  if (InSandbox() || (gLastIDToVibrate && *gLastIDToVibrate == id.AsArray())) {
+  if (MayCancelVibration(id)) {
     // Don't forward our ID if we are not in the sandbox, because hal_impl
     // doesn't need it, and we don't want it to be tempted to read it.  The
     // empty identifier will assert if it's used.
     PROXY_IF_SANDBOXED(CancelVibrate(InSandbox() ? id : WindowIdentifier()));
   }
 }
 
 template <class InfoType>
 class ObserversManager
 {
 public:
   void AddObserver(Observer<InfoType>* aObserver) {
-    if (!mObservers) {
-      mObservers = new mozilla::ObserverList<InfoType>();
-    }
+    mObservers.AddObserver(aObserver);
 
-    mObservers->AddObserver(aObserver);
-
-    if (mObservers->Length() == 1) {
+    if (mObservers.Length() == 1) {
       EnableNotifications();
     }
   }
 
   void RemoveObserver(Observer<InfoType>* aObserver) {
-    bool removed = mObservers && mObservers->RemoveObserver(aObserver);
+    bool removed = mObservers.RemoveObserver(aObserver);
     if (!removed) {
       return;
     }
 
-    if (mObservers->Length() == 0) {
+    if (mObservers.Length() == 0) {
       DisableNotifications();
-
       OnNotificationsDisabled();
-
-      delete mObservers;
-      mObservers = nullptr;
     }
   }
 
   void BroadcastInformation(const InfoType& aInfo) {
-    // It is possible for mObservers to be nullptr here on some platforms,
-    // because a call to BroadcastInformation gets queued up asynchronously
-    // while RemoveObserver is running (and before the notifications are
-    // disabled). The queued call can then get run after mObservers has
-    // been nulled out. See bug 757025.
-    if (!mObservers) {
-      return;
-    }
-    mObservers->Broadcast(aInfo);
+    mObservers.Broadcast(aInfo);
   }
 
 protected:
+  ~ObserversManager() {
+    MOZ_ASSERT(mObservers.Length() == 0);
+  }
+
   virtual void EnableNotifications() = 0;
   virtual void DisableNotifications() = 0;
   virtual void OnNotificationsDisabled() {}
 
 private:
-  mozilla::ObserverList<InfoType>* mObservers;
+  mozilla::ObserverList<InfoType> mObservers;
 };
 
 template <class InfoType>
 class CachingObserversManager : public ObserversManager<InfoType>
 {
 public:
   InfoType GetCurrentInformation() {
     if (mHasValidCache) {
@@ -257,240 +251,191 @@ protected:
     mHasValidCache = false;
   }
 
 private:
   InfoType                mInfo;
   bool                    mHasValidCache;
 };
 
-class BatteryObserversManager : public CachingObserversManager<BatteryInformation>
+class BatteryObserversManager final
+  : public CachingObserversManager<BatteryInformation>
 {
 protected:
   void EnableNotifications() override {
     PROXY_IF_SANDBOXED(EnableBatteryNotifications());
   }
 
   void DisableNotifications() override {
     PROXY_IF_SANDBOXED(DisableBatteryNotifications());
   }
 
   void GetCurrentInformationInternal(BatteryInformation* aInfo) override {
     PROXY_IF_SANDBOXED(GetCurrentBatteryInformation(aInfo));
   }
 };
 
-static BatteryObserversManager&
-BatteryObservers()
-{
-  static BatteryObserversManager sBatteryObservers;
-  AssertMainThread();
-  return sBatteryObservers;
-}
-
-class NetworkObserversManager : public CachingObserversManager<NetworkInformation>
+class NetworkObserversManager final
+  : public CachingObserversManager<NetworkInformation>
 {
 protected:
   void EnableNotifications() override {
     PROXY_IF_SANDBOXED(EnableNetworkNotifications());
   }
 
   void DisableNotifications() override {
     PROXY_IF_SANDBOXED(DisableNetworkNotifications());
   }
 
   void GetCurrentInformationInternal(NetworkInformation* aInfo) override {
     PROXY_IF_SANDBOXED(GetCurrentNetworkInformation(aInfo));
   }
 };
 
-static NetworkObserversManager&
-NetworkObservers()
-{
-  static NetworkObserversManager sNetworkObservers;
-  AssertMainThread();
-  return sNetworkObservers;
-}
-
-class WakeLockObserversManager : public ObserversManager<WakeLockInformation>
+class WakeLockObserversManager final
+  : public ObserversManager<WakeLockInformation>
 {
 protected:
   void EnableNotifications() override {
     PROXY_IF_SANDBOXED(EnableWakeLockNotifications());
   }
 
   void DisableNotifications() override {
     PROXY_IF_SANDBOXED(DisableWakeLockNotifications());
   }
 };
 
-static WakeLockObserversManager&
-WakeLockObservers()
-{
-  static WakeLockObserversManager sWakeLockObservers;
-  AssertMainThread();
-  return sWakeLockObservers;
-}
-
-class ScreenConfigurationObserversManager : public CachingObserversManager<ScreenConfiguration>
+class ScreenConfigurationObserversManager final
+  : public CachingObserversManager<ScreenConfiguration>
 {
 protected:
   void EnableNotifications() override {
     PROXY_IF_SANDBOXED(EnableScreenConfigurationNotifications());
   }
 
   void DisableNotifications() override {
     PROXY_IF_SANDBOXED(DisableScreenConfigurationNotifications());
   }
 
   void GetCurrentInformationInternal(ScreenConfiguration* aInfo) override {
     PROXY_IF_SANDBOXED(GetCurrentScreenConfiguration(aInfo));
   }
 };
 
-static ScreenConfigurationObserversManager&
-ScreenConfigurationObservers()
-{
+typedef mozilla::ObserverList<SensorData> SensorObserverList;
+StaticAutoPtr<SensorObserverList> sSensorObservers[NUM_SENSOR_TYPE];
+
+static SensorObserverList*
+GetSensorObservers(SensorType sensor_type) {
   AssertMainThread();
-  static ScreenConfigurationObserversManager sScreenConfigurationObservers;
-  return sScreenConfigurationObservers;
+  MOZ_ASSERT(sensor_type < NUM_SENSOR_TYPE);
+
+  if (!sSensorObservers[sensor_type]) {
+    sSensorObservers[sensor_type] = new SensorObserverList();
+  }
+
+  return sSensorObservers[sensor_type];
 }
 
-#define MOZ_IMPL_HAL_OBSERVER(name_)                    \
-void                                                    \
-Register##name_##Observer(name_##Observer* aObserver)   \
-{                                                       \
-  AssertMainThread();                                   \
-  name_##Observers().AddObserver(aObserver);            \
-}                                                       \
-                                                        \
-void                                                    \
-Unregister##name_##Observer(name_##Observer* aObserver) \
-{                                                       \
-  AssertMainThread();                                   \
-  name_##Observers().RemoveObserver(aObserver);         \
+#define MOZ_IMPL_HAL_OBSERVER(name_)                          \
+StaticAutoPtr<name_##ObserversManager> s##name_##Observers;   \
+                                                              \
+static name_##ObserversManager*                               \
+name_##Observers()                                            \
+{                                                             \
+  AssertMainThread();                                         \
+                                                              \
+  if (!s##name_##Observers) {                                 \
+    MOZ_ASSERT(sInitialized);                                 \
+    s##name_##Observers = new name_##ObserversManager();      \
+  }                                                           \
+                                                              \
+  return s##name_##Observers;                                 \
+}                                                             \
+                                                              \
+void                                                          \
+Register##name_##Observer(name_##Observer* aObserver)         \
+{                                                             \
+  AssertMainThread();                                         \
+  name_##Observers()->AddObserver(aObserver);                 \
+}                                                             \
+                                                              \
+void                                                          \
+Unregister##name_##Observer(name_##Observer* aObserver)       \
+{                                                             \
+  AssertMainThread();                                         \
+  name_##Observers()->RemoveObserver(aObserver);              \
 }
 
 MOZ_IMPL_HAL_OBSERVER(Battery)
 
 void
 GetCurrentBatteryInformation(BatteryInformation* aInfo)
 {
-  AssertMainThread();
-  *aInfo = BatteryObservers().GetCurrentInformation();
+  *aInfo = BatteryObservers()->GetCurrentInformation();
 }
 
 void
 NotifyBatteryChange(const BatteryInformation& aInfo)
 {
-  AssertMainThread();
-  BatteryObservers().CacheInformation(aInfo);
-  BatteryObservers().BroadcastCachedInformation();
+  BatteryObservers()->CacheInformation(aInfo);
+  BatteryObservers()->BroadcastCachedInformation();
 }
 
 void
 EnableSensorNotifications(SensorType aSensor) {
   AssertMainThread();
   PROXY_IF_SANDBOXED(EnableSensorNotifications(aSensor));
 }
 
 void
 DisableSensorNotifications(SensorType aSensor) {
   AssertMainThread();
   PROXY_IF_SANDBOXED(DisableSensorNotifications(aSensor));
 }
 
-typedef mozilla::ObserverList<SensorData> SensorObserverList;
-static SensorObserverList* gSensorObservers = nullptr;
-
-static SensorObserverList &
-GetSensorObservers(SensorType sensor_type) {
-  MOZ_ASSERT(sensor_type < NUM_SENSOR_TYPE);
-
-  if(!gSensorObservers) {
-    gSensorObservers = new SensorObserverList[NUM_SENSOR_TYPE];
-  }
-  return gSensorObservers[sensor_type];
-}
-
 void
 RegisterSensorObserver(SensorType aSensor, ISensorObserver *aObserver) {
-  SensorObserverList &observers = GetSensorObservers(aSensor);
+  SensorObserverList* observers = GetSensorObservers(aSensor);
 
-  AssertMainThread();
-
-  observers.AddObserver(aObserver);
-  if(observers.Length() == 1) {
+  observers->AddObserver(aObserver);
+  if (observers->Length() == 1) {
     EnableSensorNotifications(aSensor);
   }
 }
 
 void
 UnregisterSensorObserver(SensorType aSensor, ISensorObserver *aObserver) {
-  AssertMainThread();
-
-  if (!gSensorObservers) {
-    HAL_ERR("Un-registering a sensor when none have been registered");
-    return;
-  }
-
-  SensorObserverList &observers = GetSensorObservers(aSensor);
-  if (!observers.RemoveObserver(aObserver) || observers.Length() > 0) {
+  SensorObserverList* observers = GetSensorObservers(aSensor);
+  if (!observers->RemoveObserver(aObserver) || observers->Length() > 0) {
     return;
   }
   DisableSensorNotifications(aSensor);
-
-  for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
-    if (gSensorObservers[i].Length() > 0) {
-      return;
-    }
-  }
-
-  // We want to destroy gSensorObservers if all observer lists are
-  // empty, but we have to defer the deallocation via a runnable to
-  // mainthread (since we may be inside NotifySensorChange()/Broadcast()
-  // when it calls UnregisterSensorObserver()).
-  SensorObserverList* sensorlists = gSensorObservers;
-  gSensorObservers = nullptr;
-
-  // Unlike DispatchToMainThread, DispatchToCurrentThread doesn't leak a runnable if
-  // it fails (and we assert we're on MainThread).
-  if (NS_FAILED(NS_DispatchToCurrentThread(NS_NewRunnableFunction("UnregisterSensorObserver",
-                                                                  [sensorlists]() -> void {
-      delete [] sensorlists;
-      }))))
-  {
-    // Still need to delete sensorlists if the dispatch fails
-    delete [] sensorlists;
-  }
 }
 
 void
 NotifySensorChange(const SensorData &aSensorData) {
-  SensorObserverList &observers = GetSensorObservers(aSensorData.sensor());
+  SensorObserverList* observers = GetSensorObservers(aSensorData.sensor());
 
-  AssertMainThread();
-
-  observers.Broadcast(aSensorData);
+  observers->Broadcast(aSensorData);
 }
 
 MOZ_IMPL_HAL_OBSERVER(Network)
 
 void
 GetCurrentNetworkInformation(NetworkInformation* aInfo)
 {
-  AssertMainThread();
-  *aInfo = NetworkObservers().GetCurrentInformation();
+  *aInfo = NetworkObservers()->GetCurrentInformation();
 }
 
 void
 NotifyNetworkChange(const NetworkInformation& aInfo)
 {
-  NetworkObservers().CacheInformation(aInfo);
-  NetworkObservers().BroadcastCachedInformation();
+  NetworkObservers()->CacheInformation(aInfo);
+  NetworkObservers()->BroadcastCachedInformation();
 }
 
 MOZ_IMPL_HAL_OBSERVER(WakeLock)
 
 void
 ModifyWakeLock(const nsAString& aTopic,
                WakeLockControl aLockAdjust,
                WakeLockControl aHiddenAdjust,
@@ -513,33 +458,33 @@ GetWakeLockInfo(const nsAString& aTopic,
   AssertMainThread();
   PROXY_IF_SANDBOXED(GetWakeLockInfo(aTopic, aWakeLockInfo));
 }
 
 void
 NotifyWakeLockChange(const WakeLockInformation& aInfo)
 {
   AssertMainThread();
-  WakeLockObservers().BroadcastInformation(aInfo);
+  WakeLockObservers()->BroadcastInformation(aInfo);
 }
 
 MOZ_IMPL_HAL_OBSERVER(ScreenConfiguration)
 
 void
 GetCurrentScreenConfiguration(ScreenConfiguration* aScreenConfiguration)
 {
-  AssertMainThread();
-  *aScreenConfiguration = ScreenConfigurationObservers().GetCurrentInformation();
+  *aScreenConfiguration =
+    ScreenConfigurationObservers()->GetCurrentInformation();
 }
 
 void
 NotifyScreenConfigurationChange(const ScreenConfiguration& aScreenConfiguration)
 {
-  ScreenConfigurationObservers().CacheInformation(aScreenConfiguration);
-  ScreenConfigurationObservers().BroadcastCachedInformation();
+  ScreenConfigurationObservers()->CacheInformation(aScreenConfiguration);
+  ScreenConfigurationObservers()->BroadcastCachedInformation();
 }
 
 bool
 LockScreenOrientation(const ScreenOrientation& aOrientation)
 {
   AssertMainThread();
   RETURN_PROXY_IF_SANDBOXED(LockScreenOrientation(aOrientation), false);
 }
@@ -603,10 +548,43 @@ StartDiskSpaceWatcher()
 void
 StopDiskSpaceWatcher()
 {
   AssertMainProcess();
   AssertMainThread();
   PROXY_IF_SANDBOXED(StopDiskSpaceWatcher());
 }
 
+void
+Init()
+{
+  MOZ_ASSERT(!sInitialized);
+
+  if (!InSandbox()) {
+    gLastIDToVibrate = new WindowIdentifier::IDArrayType();
+  }
+
+  WakeLockInit();
+
+  sInitialized = true;
+}
+
+void
+Shutdown()
+{
+  MOZ_ASSERT(sInitialized);
+
+  gLastIDToVibrate = nullptr;
+
+  sBatteryObservers = nullptr;
+  sNetworkObservers = nullptr;
+  sWakeLockObservers = nullptr;
+  sScreenConfigurationObservers = nullptr;
+
+  for (auto& sensorObserver : sSensorObservers) {
+    sensorObserver = nullptr;
+  }
+
+  sInitialized = false;
+}
+
 } // namespace hal
 } // namespace mozilla
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -41,16 +41,27 @@ namespace hal {
 
 class WindowIdentifier;
 
 } // namespace hal
 
 namespace MOZ_HAL_NAMESPACE {
 
 /**
+ * Initializes the HAL. This must be called before any other HAL function.
+ */
+void Init();
+
+/**
+ * Shuts down the HAL. Besides freeing all the used resources this will check
+ * that all observers have been properly deregistered and assert if not.
+ */
+void Shutdown();
+
+/**
  * Turn the default vibrator device on/off per the pattern specified
  * by |pattern|.  Each element in the pattern is the number of
  * milliseconds to turn the vibrator on or off.  The first element in
  * |pattern| is an "on" element, the next is "off", and so on.
  *
  * If |pattern| is empty, any in-progress vibration is canceled.
  *
  * Only an active window within an active tab may call Vibrate; calls
--- a/hal/HalWakeLock.cpp
+++ b/hal/HalWakeLock.cpp
@@ -31,33 +31,25 @@ struct LockCount {
   nsTArray<uint64_t> processes;
 };
 
 typedef nsDataHashtable<nsUint64HashKey, LockCount> ProcessLockTable;
 typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
 
 int sActiveListeners = 0;
 StaticAutoPtr<LockTable> sLockTable;
-bool sInitialized = false;
 bool sIsShuttingDown = false;
 
 WakeLockInformation
 WakeLockInfoFromLockCount(const nsAString& aTopic, const LockCount& aLockCount)
 {
-  // TODO: Once we abandon b2g18, we can switch this to use the
-  // WakeLockInformation constructor, which is better because it doesn't let us
-  // forget to assign a param.  For now we have to do it this way, because
-  // b2g18 doesn't have the nsTArray <--> InfallibleTArray conversion (bug
-  // 819791).
+  nsString topic(aTopic);
+  WakeLockInformation info(topic, aLockCount.numLocks, aLockCount.numHidden,
+                           aLockCount.processes);
 
-  WakeLockInformation info;
-  info.topic() = aTopic;
-  info.numLocks() = aLockCount.numLocks;
-  info.numHidden() = aLockCount.numHidden;
-  info.lockingProcesses().AppendElements(aLockCount.processes);
   return info;
 }
 
 static void
 CountWakeLocks(ProcessLockTable* aTable, LockCount* aTotalCount)
 {
   for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
     const uint64_t& key = iter.Key();
@@ -141,34 +133,34 @@ CleanupOnContentShutdown::Observe(nsISup
       }
     }
   } else {
     NS_WARNING("ipc:content-shutdown message without childID property");
   }
   return NS_OK;
 }
 
+} // namespace
+
+namespace mozilla {
+
+namespace hal {
+
 void
-Init()
+WakeLockInit()
 {
   sLockTable = new LockTable();
-  sInitialized = true;
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
     obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown", false);
   }
 }
 
-} // namespace
-
-namespace mozilla {
-
-namespace hal {
 
 WakeLockState
 ComputeWakeLockState(int aNumLocks, int aNumHidden)
 {
   if (aNumLocks == 0) {
     return WAKE_LOCK_STATE_UNLOCKED;
   } else if (aNumLocks == aNumHidden) {
     return WAKE_LOCK_STATE_HIDDEN;
@@ -200,19 +192,16 @@ ModifyWakeLock(const nsAString& aTopic,
                uint64_t aProcessID)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN);
 
   if (sIsShuttingDown) {
     return;
   }
-  if (!sInitialized) {
-    Init();
-  }
 
   ProcessLockTable* table = sLockTable->Get(aTopic);
   LockCount processCount;
   LockCount totalCount;
   if (!table) {
     table = new ProcessLockTable();
     sLockTable->Put(aTopic, table);
   } else {
@@ -259,19 +248,16 @@ ModifyWakeLock(const nsAString& aTopic,
 void
 GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
 {
   if (sIsShuttingDown) {
     NS_WARNING("You don't want to get wake lock information during xpcom-shutdown!");
     *aWakeLockInfo = WakeLockInformation();
     return;
   }
-  if (!sInitialized) {
-    Init();
-  }
 
   ProcessLockTable* table = sLockTable->Get(aTopic);
   if (!table) {
     *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
     return;
   }
   LockCount totalCount;
   CountWakeLocks(table, &totalCount);
new file mode 100644
--- /dev/null
+++ b/hal/HalWakeLockInternal.h
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef __HAL_WAKELOCK_INTERNAL_H_
+#define __HAL_WAKELOCK_INTERNAL_H_
+
+namespace mozilla {
+namespace hal {
+
+void WakeLockInit();
+
+} // namespace hal
+} // namespace mozilla
+
+#endif /* __HAL_WAKELOCK_INTERNAL_H_ */
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -400,16 +400,18 @@ nsAppShell::nsAppShell()
                        "nsAppShell.SyncRun")
     , mSyncRunQuit(false)
 {
     {
         MutexAutoLock lock(*sAppShellLock);
         sAppShell = this;
     }
 
+    hal::Init();
+
     if (!XRE_IsParentProcess()) {
         if (jni::IsAvailable()) {
             GeckoThreadSupport::Init();
             GeckoAppShellSupport::Init();
 
             // Set the corresponding state in GeckoThread.
             java::GeckoThread::SetState(java::GeckoThread::State::RUNNING());
         }
@@ -468,16 +470,18 @@ nsAppShell::~nsAppShell()
 
     if (sPowerManagerService) {
         sPowerManagerService->RemoveWakeLockListener(sWakeLockListener);
 
         sPowerManagerService = nullptr;
         sWakeLockListener = nullptr;
     }
 
+    hal::Shutdown();
+
     if (jni::IsAvailable() && XRE_IsParentProcess()) {
         DestroyAndroidUiThread();
         AndroidBridge::DeconstructBridge();
     }
 }
 
 void
 nsAppShell::NotifyNativeEvent()
--- a/widget/android/nsAppShell.h
+++ b/widget/android/nsAppShell.h
@@ -252,16 +252,18 @@ protected:
             sLatencyCount[latencyType]++;
             sLatencyTime[latencyType] += latency;
 #endif
             return event;
         }
 
     } mEventQueue;
 
+private:
+
     mozilla::CondVar mSyncRunFinished;
     bool mSyncRunQuit;
 
     bool mAllowCoalescingTouches;
 
     nsCOMPtr<nsIAndroidBrowserApp> mBrowserApp;
     nsInterfaceHashtable<nsStringHashKey, nsIObserver> mObserversHash;
 };
--- a/widget/cocoa/nsAppShell.mm
+++ b/widget/cocoa/nsAppShell.mm
@@ -28,16 +28,17 @@
 #include "nsCocoaFeatures.h"
 #include "nsCocoaUtils.h"
 #include "nsChildView.h"
 #include "nsToolkit.h"
 #include "TextInputHandler.h"
 #include "mozilla/BackgroundHangMonitor.h"
 #include "GeckoProfiler.h"
 #include "ScreenHelperCocoa.h"
+#include "mozilla/Hal.h"
 #include "mozilla/widget/ScreenManager.h"
 #include "HeadlessScreenHelper.h"
 #include "pratom.h"
 #if !defined(RELEASE_OR_BETA) || defined(DEBUG)
 #include "nsSandboxViolationSink.h"
 #endif
 
 #include <IOKit/pwr_mgt/IOPMLib.h>
@@ -232,16 +233,18 @@ nsAppShell::nsAppShell()
   // by a Cocoa app.
   mRunningCocoaEmbedded = [NSApp isRunning] ? true : false;
 }
 
 nsAppShell::~nsAppShell()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
+  hal::Shutdown();
+
   if (mCFRunLoop) {
     if (mCFRunLoopSource) {
       ::CFRunLoopRemoveSource(mCFRunLoop, mCFRunLoopSource,
                               kCFRunLoopCommonModes);
       ::CFRelease(mCFRunLoopSource);
     }
     ::CFRelease(mCFRunLoop);
   }
@@ -351,16 +354,18 @@ nsAppShell::Init()
   context.info = this;
   context.perform = ProcessGeckoEvents;
 
   mCFRunLoopSource = ::CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
   NS_ENSURE_STATE(mCFRunLoopSource);
 
   ::CFRunLoopAddSource(mCFRunLoop, mCFRunLoopSource, kCFRunLoopCommonModes);
 
+  hal::Init();
+
   if (XRE_IsParentProcess()) {
     ScreenManager& screenManager = ScreenManager::GetSingleton();
 
     if (gfxPlatform::IsHeadless()) {
       screenManager.SetHelper(mozilla::MakeUnique<HeadlessScreenHelper>());
     } else {
       screenManager.SetHelper(mozilla::MakeUnique<ScreenHelperCocoa>());
     }
--- a/widget/gtk/nsAppShell.cpp
+++ b/widget/gtk/nsAppShell.cpp
@@ -10,16 +10,17 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <gdk/gdk.h>
 #include "nsAppShell.h"
 #include "nsWindow.h"
 #include "mozilla/Logging.h"
 #include "prenv.h"
 #include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/Hal.h"
 #include "mozilla/Unused.h"
 #include "mozilla/WidgetUtils.h"
 #include "GeckoProfiler.h"
 #include "nsIPowerManagerService.h"
 #ifdef MOZ_ENABLE_DBUS
 #include "WakeLockListener.h"
 #endif
 #include "gfxPlatform.h"
@@ -129,16 +130,18 @@ nsAppShell::EventProcessorCallback(GIOCh
     NS_ASSERTION(c == (unsigned char) NOTIFY_TOKEN, "wrong token");
 
     self->NativeEventCallback();
     return TRUE;
 }
 
 nsAppShell::~nsAppShell()
 {
+    mozilla::hal::Shutdown();
+
     if (mTag)
         g_source_remove(mTag);
     if (mPipeFDs[0])
         close(mPipeFDs[0]);
     if (mPipeFDs[1])
         close(mPipeFDs[1]);
 }
 
@@ -146,16 +149,18 @@ nsresult
 nsAppShell::Init()
 {
     // For any versions of Glib before 2.36, g_type_init must be explicitly called
     // to safely use the library. Failure to do so may cause various failures/crashes
     // in any code that uses Glib, Gdk, or Gtk. In later versions of Glib, this call
     // is a no-op.
     g_type_init();
 
+    mozilla::hal::Init();
+
 #ifdef MOZ_ENABLE_DBUS
     if (XRE_IsParentProcess()) {
         nsCOMPtr<nsIPowerManagerService> powerManagerService =
             do_GetService(POWERMANAGERSERVICE_CONTRACTID);
 
         if (powerManagerService) {
             powerManagerService->AddWakeLockListener(WakeLockListener::GetSingleton());
         } else {
--- a/widget/windows/nsAppShell.cpp
+++ b/widget/windows/nsAppShell.cpp
@@ -11,16 +11,17 @@
 #include "WinUtils.h"
 #include "WinTaskbar.h"
 #include "WinMouseScrollHandler.h"
 #include "nsWindowDefs.h"
 #include "nsString.h"
 #include "WinIMEHandler.h"
 #include "mozilla/widget/AudioSession.h"
 #include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/Hal.h"
 #include "nsIDOMWakeLockListener.h"
 #include "nsIPowerManagerService.h"
 #include "mozilla/StaticPtr.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "GeckoProfiler.h"
 #include "nsComponentManagerUtils.h"
 #include "ScreenHelperWin.h"
@@ -205,16 +206,18 @@ nsAppShell::EventWindowProc(HWND hwnd, U
     --sOutstandingNativeEventCallbacks;
     return TRUE;
   }
   return DefWindowProc(hwnd, uMsg, wParam, lParam);
 }
 
 nsAppShell::~nsAppShell()
 {
+  hal::Shutdown();
+
   if (mEventWnd) {
     // DestroyWindow doesn't do anything when called from a non UI thread.
     // Since mEventWnd was created on the UI thread, it must be destroyed on
     // the UI thread.
     SendMessage(mEventWnd, WM_CLOSE, 0, 0);
   }
 
   // Cancel any outstanding native event callbacks.
@@ -322,16 +325,18 @@ nsAppShell::Observe(nsISupports* aSubjec
 
 #endif // defined(ACCESSIBILITY)
 
 nsresult
 nsAppShell::Init()
 {
   LSPAnnotate();
 
+  hal::Init();
+
   mozilla::ipc::windows::InitUIThread();
 
   sTaskbarButtonCreatedMsg = ::RegisterWindowMessageW(kTaskbarButtonEventId);
   NS_ASSERTION(sTaskbarButtonCreatedMsg, "Could not register taskbar button creation message");
 
   // The hidden message window is used for interrupting the processing of native
   // events, so that we can process gecko events. Therefore, we only need it if
   // we are processing native events.