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 113623 fc4e7e3a74074768888a7b98bf4b5811919e17ed
parent 113622 278bf5deb265a8d50fef9f5df53a239ab6e2ded8
child 113624 a8832e8df0c84f10f609ac6c47989ded1fac97c9
push idunknown
push userunknown
push dateunknown
reviewersjlebar
bugs806363
milestone19.0a1
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);