Bug 806363 - Remove wake locks on content-shutdown. r=jlebar
authorKan-Ru Chen (陳侃如) <kanru@kanru.info>
Sat, 17 Nov 2012 23:05:18 +0800
changeset 121558 fc4e7e3a74074768888a7b98bf4b5811919e17ed
parent 121557 278bf5deb265a8d50fef9f5df53a239ab6e2ded8
child 121559 a8832e8df0c84f10f609ac6c47989ded1fac97c9
push id1997
push userakeybl@mozilla.com
push dateMon, 07 Jan 2013 21:25:26 +0000
treeherdermozilla-beta@4baf45cdcf21 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjlebar
bugs806363
milestone19.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 806363 - Remove wake locks on content-shutdown. r=jlebar
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
hal/Hal.cpp
hal/Hal.h
hal/HalWakeLock.cpp
hal/sandbox/PHal.ipdl
hal/sandbox/SandboxHal.cpp
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -626,32 +626,33 @@ ContentParent::ActorDestroy(ActorDestroy
         mRunToCompletionDepth = 0;
 
     MarkAsDead();
 
     if (obs) {
         nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
         props->Init();
 
+        props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID);
+
         if (AbnormalShutdown == why) {
             props->SetPropertyAsBool(NS_LITERAL_STRING("abnormal"), true);
 
 #ifdef MOZ_CRASHREPORTER
             MOZ_ASSERT(ManagedPCrashReporterParent().Length() > 0);
             CrashReporterParent* crashReporter =
                     static_cast<CrashReporterParent*>(ManagedPCrashReporterParent()[0]);
 
             crashReporter->GenerateCrashReport(this, NULL);
  
             nsAutoString dumpID(crashReporter->ChildDumpID());
             props->SetPropertyAsAString(NS_LITERAL_STRING("dumpID"), dumpID);
 #endif
-
-            obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", nullptr);
         }
+        obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", nullptr);
     }
 
     MessageLoop::current()->
         PostTask(FROM_HERE,
                  NewRunnableFunction(DelayedDeleteSubprocess, mSubprocess));
     mSubprocess = NULL;
 
     // IPDL rules require actors to live on past ActorDestroy, but it
@@ -697,16 +698,17 @@ ContentParent::GetTestShellSingleton()
     return static_cast<TestShellParent*>(ManagedPTestShellParent()[0]);
 }
 
 ContentParent::ContentParent(const nsAString& aAppManifestURL,
                              bool aIsForBrowser,
                              ChildOSPrivileges aOSPrivileges)
     : mSubprocess(nullptr)
     , mOSPrivileges(aOSPrivileges)
+    , mChildID(-1)
     , mGeolocationWatchID(-1)
     , mRunToCompletionDepth(0)
     , mShouldCallUnblockChild(false)
     , mAppManifestURL(aAppManifestURL)
     , mIsAlive(true)
     , mSendPermissionUpdates(false)
     , mIsForBrowser(aIsForBrowser)
 {
@@ -1120,17 +1122,17 @@ ContentParent::AllocPImageBridge(mozilla
 {
     return ImageBridgeParent::Create(aTransport, aOtherProcess);
 }
 
 bool
 ContentParent::RecvGetProcessAttributes(uint64_t* aId, bool* aStartBackground,
                                         bool* aIsForApp, bool* aIsForBrowser)
 {
-    *aId = gContentChildID++;
+    *aId = mChildID = gContentChildID++;
     *aStartBackground =
         (mAppManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL);
     *aIsForApp = IsForApp();
     *aIsForBrowser = mIsForBrowser;
 
     return true;
 }
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -306,16 +306,17 @@ private:
 
     virtual bool RecvFirstIdle();
 
     virtual void ProcessingError(Result what) MOZ_OVERRIDE;
 
     GeckoChildProcessHost* mSubprocess;
     ChildOSPrivileges mOSPrivileges;
 
+    uint64_t mChildID;
     int32_t mGeolocationWatchID;
     int mRunToCompletionDepth;
     bool mShouldCallUnblockChild;
 
     // This is a cache of all of the memory reporters
     // registered in the child process.  To update this, one
     // can broadcast the topic "child-memory-reporter-request" using
     // the nsIObserverService.
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/Services.h"
 #include "nsIWebNavigation.h"
 #include "nsITabChild.h"
 #include "nsIDocShell.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "WindowIdentifier.h"
 #include "mozilla/dom/ScreenOrientation.h"
+#include "mozilla/dom/ContentChild.h"
 
 #ifdef XP_WIN
 #include <process.h>
 #define getpid _getpid
 #endif
 
 using namespace mozilla::services;
 
@@ -636,26 +637,39 @@ RegisterWakeLockObserver(WakeLockObserve
 void
 UnregisterWakeLockObserver(WakeLockObserver* aObserver)
 {
   AssertMainThread();
   sWakeLockObservers.RemoveObserver(aObserver);
 }
 
 void
-ModifyWakeLock(const nsAString &aTopic,
+ModifyWakeLock(const nsAString& aTopic,
                WakeLockControl aLockAdjust,
                WakeLockControl aHiddenAdjust)
 {
   AssertMainThread();
-  PROXY_IF_SANDBOXED(ModifyWakeLock(aTopic, aLockAdjust, aHiddenAdjust));
+  uint64_t processID = InSandbox() ? dom::ContentChild::GetSingleton()->GetID() : 0;
+  PROXY_IF_SANDBOXED(ModifyWakeLockInternal(aTopic, aLockAdjust, aHiddenAdjust, processID));
 }
 
 void
-GetWakeLockInfo(const nsAString &aTopic, WakeLockInformation *aWakeLockInfo)
+ModifyWakeLockInternal(const nsAString& aTopic,
+                       WakeLockControl aLockAdjust,
+                       WakeLockControl aHiddenAdjust,
+                       uint64_t aProcessID)
+{
+  AssertMainThread();
+  // TODO: Bug 812403 - support wake locks in nested content processes.
+  AssertMainProcess();
+  PROXY_IF_SANDBOXED(ModifyWakeLockInternal(aTopic, aLockAdjust, aHiddenAdjust, aProcessID));
+}
+
+void
+GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
 {
   AssertMainThread();
   PROXY_IF_SANDBOXED(GetWakeLockInfo(aTopic, aWakeLockInfo));
 }
 
 void
 NotifyWakeLockChange(const WakeLockInformation& aInfo)
 {
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -335,26 +335,38 @@ void RegisterWakeLockObserver(WakeLockOb
 
 /**
  * Inform the wake lock backend a wake lock observer unregistered.
  * @param aWakeLockObserver The observer that should be removed.
  */
 void UnregisterWakeLockObserver(WakeLockObserver* aObserver);
 
 /**
- * Adjust the internal wake lock counts.
+ * Adjust the wake lock counts.
  * @param aTopic        lock topic
  * @param aLockAdjust   to increase or decrease active locks
  * @param aHiddenAdjust to increase or decrease hidden locks
  */
 void ModifyWakeLock(const nsAString &aTopic,
                     hal::WakeLockControl aLockAdjust,
                     hal::WakeLockControl aHiddenAdjust);
 
 /**
+ * Adjust the wake lock counts. Do not call this function directly.
+ * @param aTopic        lock topic
+ * @param aLockAdjust   to increase or decrease active locks
+ * @param aHiddenAdjust to increase or decrease hidden locks
+ * @param aProcessID    unique id per-ContentChild or 0 for chrome
+ */
+void ModifyWakeLockInternal(const nsAString &aTopic,
+                            hal::WakeLockControl aLockAdjust,
+                            hal::WakeLockControl aHiddenAdjust,
+                            uint64_t aProcessID);
+
+/**
  * Query the wake lock numbers of aTopic.
  * @param aTopic        lock topic
  * @param aWakeLockInfo wake lock numbers
  */
 void GetWakeLockInfo(const nsAString &aTopic, hal::WakeLockInformation *aWakeLockInfo);
 
 /**
  * Notify of a change in the wake lock state.
--- a/hal/HalWakeLock.cpp
+++ b/hal/HalWakeLock.cpp
@@ -1,19 +1,22 @@
 /* -*- 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/. */
 
 #include "mozilla/Hal.h"
 #include "mozilla/HalWakeLock.h"
 #include "mozilla/Services.h"
-#include "nsObserverService.h"
+#include "mozilla/StaticPtr.h"
+#include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
+#include "nsIPropertyBag2.h"
+#include "nsObserverService.h"
 
 using namespace mozilla::hal;
 
 namespace mozilla {
 namespace hal {
 
 WakeLockState
 ComputeWakeLockState(int aNumLocks, int aNumHidden)
@@ -30,27 +33,54 @@ ComputeWakeLockState(int aNumLocks, int 
 } // hal
 } // mozilla
 
 namespace mozilla {
 namespace hal_impl {
 
 namespace {
 struct LockCount {
+  LockCount()
+    : numLocks(0)
+    , numHidden(0)
+  {}
   uint32_t numLocks;
   uint32_t numHidden;
 };
-}
+typedef nsDataHashtable<nsUint64HashKey, LockCount> ProcessLockTable;
+typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
 
-static int sActiveChildren = 0;
-static nsAutoPtr<nsDataHashtable<nsStringHashKey, LockCount> > sLockTable;
+static int sActiveListeners = 0;
+static StaticAutoPtr<LockTable> sLockTable;
 static bool sInitialized = false;
 static bool sIsShuttingDown = false;
 
-namespace {
+static PLDHashOperator
+RemoveChildFromList(const nsAString& aKey, ProcessLockTable* aTable, void* aUserArg)
+{
+  MOZ_ASSERT(aUserArg);
+
+  uint64_t childID = *static_cast<uint64_t*>(aUserArg);
+  aTable->Remove(childID);
+
+  return PL_DHASH_NEXT;
+}
+
+static PLDHashOperator
+CountWakeLocks(const uint64_t& aKey, LockCount aCount, void* aUserArg)
+{
+  MOZ_ASSERT(aUserArg);
+
+  LockCount* totalCount = static_cast<LockCount*>(aUserArg);
+  totalCount->numLocks += aCount.numLocks;
+  totalCount->numHidden += aCount.numHidden;
+
+  return PL_DHASH_NEXT;
+}
+
 class ClearHashtableOnShutdown MOZ_FINAL : public nsIObserver {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 };
 
 NS_IMPL_ISUPPORTS1(ClearHashtableOnShutdown, nsIObserver)
 
@@ -59,101 +89,157 @@ ClearHashtableOnShutdown::Observe(nsISup
 {
   MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
 
   sIsShuttingDown = true;
   sLockTable = nullptr;
 
   return NS_OK;
 }
-} // anonymous namespace
+
+class CleanupOnContentShutdown MOZ_FINAL : public nsIObserver {
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+};
+
+NS_IMPL_ISUPPORTS1(CleanupOnContentShutdown, nsIObserver)
+
+NS_IMETHODIMP
+CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* data)
+{
+  MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
+
+  if (sIsShuttingDown) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
+  if (!props) {
+    NS_WARNING("ipc:content-shutdown message without property bag as subject");
+    return NS_OK;
+  }
+
+  uint64_t childID = 0;
+  nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
+                                           &childID);
+  if (NS_SUCCEEDED(rv)) {
+    sLockTable->EnumerateRead(RemoveChildFromList, &childID);
+  } else {
+    NS_WARNING("ipc:content-shutdown message without childID property");
+  }
+  return NS_OK;
+}
 
 static void
 Init()
 {
-  sLockTable = new nsDataHashtable<nsStringHashKey, LockCount>();
+  sLockTable = new LockTable();
   sLockTable->Init();
   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);
   }
 }
+} // anonymous namespace
 
 void
 EnableWakeLockNotifications()
 {
-  sActiveChildren++;
+  sActiveListeners++;
 }
 
 void
 DisableWakeLockNotifications()
 {
-  sActiveChildren--;
+  sActiveListeners--;
 }
 
 void
-ModifyWakeLock(const nsAString &aTopic,
-               hal::WakeLockControl aLockAdjust,
-               hal::WakeLockControl aHiddenAdjust)
+ModifyWakeLockInternal(const nsAString& aTopic,
+                       hal::WakeLockControl aLockAdjust,
+                       hal::WakeLockControl aHiddenAdjust,
+                       uint64_t aProcessID)
 {
   if (sIsShuttingDown) {
     return;
   }
   if (!sInitialized) {
     Init();
   }
 
-  LockCount count;
-  count.numLocks = 0;
-  count.numHidden = 0;
-  sLockTable->Get(aTopic, &count);
-  MOZ_ASSERT(count.numLocks >= count.numHidden);
-  MOZ_ASSERT(aLockAdjust >= 0 || count.numLocks > 0);
-  MOZ_ASSERT(aHiddenAdjust >= 0 || count.numHidden > 0);
+  ProcessLockTable* table = sLockTable->Get(aTopic);
+  LockCount processCount;
+  LockCount totalCount;
+  if (!table) {
+    table = new ProcessLockTable();
+    table->Init();
+    sLockTable->Put(aTopic, table);
+  } else {
+    table->Get(aProcessID, &processCount);
+    table->EnumerateRead(CountWakeLocks, &totalCount);
+  }
 
-  WakeLockState oldState = ComputeWakeLockState(count.numLocks, count.numHidden);
+  MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
+  MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0);
+  MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0);
+  MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden);
+  MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0);
+  MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
+
+  WakeLockState oldState = ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
 
-  count.numLocks += aLockAdjust;
-  count.numHidden += aHiddenAdjust;
-  MOZ_ASSERT(count.numLocks >= count.numHidden);
+  processCount.numLocks += aLockAdjust;
+  processCount.numHidden += aHiddenAdjust;
+
+  totalCount.numLocks += aLockAdjust;
+  totalCount.numHidden += aHiddenAdjust;
 
-  if (count.numLocks) {
-    sLockTable->Put(aTopic, count);
+  if (processCount.numLocks) {
+    table->Put(aProcessID, processCount);
   } else {
+    table->Remove(aProcessID);
+  }
+  if (!totalCount.numLocks) {
     sLockTable->Remove(aTopic);
   }
 
-  WakeLockState newState = ComputeWakeLockState(count.numLocks, count.numHidden);
+  WakeLockState newState = ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
 
-  if (sActiveChildren && oldState != newState) {
+  if (sActiveListeners && oldState != newState) {
     WakeLockInformation info;
-    info.numLocks() = count.numLocks;
-    info.numHidden() = count.numHidden;
+    info.numLocks() = totalCount.numLocks;
+    info.numHidden() = totalCount.numHidden;
     info.topic() = aTopic;
     NotifyWakeLockChange(info);
   }
 }
 
 void
-GetWakeLockInfo(const nsAString &aTopic, WakeLockInformation *aWakeLockInfo)
+GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
 {
   if (sIsShuttingDown) {
     NS_WARNING("You don't want to get wake lock information during xpcom-shutdown!");
     return;
   }
   if (!sInitialized) {
     Init();
   }
 
-  LockCount count;
-  count.numLocks = 0;
-  count.numHidden = 0;
-  sLockTable->Get(aTopic, &count);
-
-  aWakeLockInfo->numLocks() = count.numLocks;
-  aWakeLockInfo->numHidden() = count.numHidden;
+  ProcessLockTable* table = sLockTable->Get(aTopic);
+  if (!table) {
+    aWakeLockInfo->numLocks() = 0;
+    aWakeLockInfo->numHidden() = 0;
+    aWakeLockInfo->topic() = aTopic;
+    return;
+  }
+  LockCount totalCount;
+  table->EnumerateRead(CountWakeLocks, &totalCount);
+  aWakeLockInfo->numLocks() = totalCount.numLocks;
+  aWakeLockInfo->numHidden() = totalCount.numHidden;
   aWakeLockInfo->topic() = aTopic;
 }
 
 } // hal_impl
 } // mozilla
--- a/hal/sandbox/PHal.ipdl
+++ b/hal/sandbox/PHal.ipdl
@@ -147,17 +147,20 @@ parent:
     EnableSystemTimezoneChangeNotifications();
     DisableSystemTimezoneChangeNotifications();
 
     sync SetLight(LightType light, LightConfiguration aConfig)
       returns (bool status);
     sync GetLight(LightType light)
       returns (LightConfiguration aConfig, bool status);
 
-    ModifyWakeLock(nsString aTopic, WakeLockControl aLockAdjust, WakeLockControl aHiddenAdjust);
+    ModifyWakeLock(nsString aTopic,
+                   WakeLockControl aLockAdjust,
+                   WakeLockControl aHiddenAdjust,
+                   uint64_t aProcessID);
     EnableWakeLockNotifications();
     DisableWakeLockNotifications();
     sync GetWakeLockInfo(nsString aTopic)
       returns (WakeLockInformation aWakeLockInfo);
 
     EnableScreenConfigurationNotifications();
     DisableScreenConfigurationNotifications();
     sync GetCurrentScreenConfiguration()
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -271,19 +271,22 @@ EnableWakeLockNotifications()
 
 void
 DisableWakeLockNotifications()
 {
   Hal()->SendDisableWakeLockNotifications();
 }
 
 void
-ModifyWakeLock(const nsAString &aTopic, WakeLockControl aLockAdjust, WakeLockControl aHiddenAdjust)
+ModifyWakeLockInternal(const nsAString &aTopic,
+                       WakeLockControl aLockAdjust,
+                       WakeLockControl aHiddenAdjust,
+                       uint64_t aProcessID)
 {
-  Hal()->SendModifyWakeLock(nsString(aTopic), aLockAdjust, aHiddenAdjust);
+  Hal()->SendModifyWakeLock(nsString(aTopic), aLockAdjust, aHiddenAdjust, aProcessID);
 }
 
 void
 GetWakeLockInfo(const nsAString &aTopic, WakeLockInformation *aWakeLockInfo)
 {
   Hal()->SendGetWakeLockInfo(nsString(aTopic), aWakeLockInfo);
 }
 
@@ -698,22 +701,23 @@ public:
     return true;
   }
   
   void Notify(const SensorData& aSensorData) {
     unused << SendNotifySensorChange(aSensorData);
   }
 
   virtual bool
-  RecvModifyWakeLock(const nsString &aTopic,
-                     const WakeLockControl &aLockAdjust,
-                     const WakeLockControl &aHiddenAdjust) MOZ_OVERRIDE
+  RecvModifyWakeLock(const nsString& aTopic,
+                     const WakeLockControl& aLockAdjust,
+                     const WakeLockControl& aHiddenAdjust,
+                     const uint64_t& aProcessID) MOZ_OVERRIDE
   {
     // We allow arbitrary content to use wake locks.
-    hal::ModifyWakeLock(aTopic, aLockAdjust, aHiddenAdjust);
+    hal::ModifyWakeLockInternal(aTopic, aLockAdjust, aHiddenAdjust, aProcessID);
     return true;
   }
 
   virtual bool
   RecvEnableWakeLockNotifications() MOZ_OVERRIDE
   {
     // We allow arbitrary content to use wake locks.
     hal::RegisterWakeLockObserver(this);