Bug 1438945 - Part 13: keeping ContentProcess alive. r=asuth,mrbkap draft baku-remote-apr-23
authorAndrea Marchesini <amarchesini@mozilla.com>
Mon, 23 Apr 2018 14:57:00 +0200
changeset 469201 9e020e4ee7c0694d99e6a6cc6eeb29454c0a61af
parent 469200 7a38b675d0b242b619e0b43ad2ac2705966f501e
push id8
push userbugmail@asutherland.org
push dateSat, 11 Aug 2018 16:11:21 +0000
reviewersasuth, mrbkap
bugs1438945
milestone63.0a1
Bug 1438945 - Part 13: keeping ContentProcess alive. r=asuth,mrbkap
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/workers/remoteworkers/RemoteWorkerManager.cpp
dom/workers/remoteworkers/RemoteWorkerParent.cpp
dom/workers/remoteworkers/RemoteWorkerParent.h
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1922,16 +1922,21 @@ ContentParent::TryToRecycle()
 
 bool
 ContentParent::ShouldKeepProcessAlive() const
 {
   if (IsForJSPlugin()) {
     return true;
   }
 
+  // If we have active workers, we need to stay alive.
+  if (mRemoteWorkerActors) {
+    return true;
+  }
+
   if (!sBrowserContentParents) {
     return false;
   }
 
   // If we have already been marked as troubled/dead, don't prevent shutdown.
   if (!IsAvailable()) {
     return false;
   }
@@ -2304,16 +2309,17 @@ ContentParent::ContentParent(ContentPare
   , mSubprocess(nullptr)
   , mLaunchTS(TimeStamp::Now())
   , mActivateTS(TimeStamp::Now())
   , mOpener(aOpener)
   , mRemoteType(aRemoteType)
   , mChildID(gContentChildID++)
   , mGeolocationWatchID(-1)
   , mJSPluginID(aJSPluginID)
+  , mRemoteWorkerActors(0)
   , mNumDestroyingTabs(0)
   , mIsAvailable(true)
   , mIsAlive(true)
   , mIsForBrowser(!mRemoteType.IsEmpty())
   , mRecordReplayState(aRecordReplayState)
   , mRecordingFile(aRecordingFile)
   , mCalledClose(false)
   , mCalledKillHard(false)
@@ -6092,8 +6098,37 @@ ContentParent::RecvDetachBrowsingContext
   if (aMoveToBFCache) {
     context->CacheChildren();
   } else {
     context->Detach();
   }
 
   return IPC_OK();
 }
+
+void
+ContentParent::RegisterRemoteWorkerActor()
+{
+  ++mRemoteWorkerActors;
+}
+
+void
+ContentParent::UnregisterRemoveWorkerActor()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (--mRemoteWorkerActors) {
+    return;
+  }
+
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  if (!cpm->GetTabParentCountByProcessId(ChildID()) &&
+      !ShouldKeepProcessAlive() &&
+      !TryToRecycle()) {
+    // In the case of normal shutdown, send a shutdown message to child to
+    // allow it to perform shutdown tasks.
+    MessageLoop::current()->PostTask(
+      NewRunnableMethod<ShutDownMethod>("dom::ContentParent::ShutDownProcess",
+                                        this,
+                                        &ContentParent::ShutDownProcess,
+                                        SEND_SHUTDOWN_MESSAGE));
+  }
+}
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -363,16 +363,25 @@ public:
 
   jsipc::CPOWManager* GetCPOWManager() override;
 
   static void
   UnregisterRemoteFrame(const TabId& aTabId,
                         const ContentParentId& aCpId,
                         bool aMarkedDestroying);
 
+  // This method can be called on any thread.
+  void
+  RegisterRemoteWorkerActor();
+
+  // This method _must_ be called on main-thread because it can start the
+  // shutting down of the content process.
+  void
+  UnregisterRemoveWorkerActor();
+
   void ReportChildAlreadyBlocked();
 
   bool RequestRunToCompletion();
 
   void UpdateCookieStatus(nsIChannel *aChannel);
 
   bool IsAvailable() const
   {
@@ -1307,16 +1316,23 @@ private:
 
   nsCString mKillHardAnnotation;
 
   // After we initiate shutdown, we also start a timer to ensure
   // that even content processes that are 100% blocked (say from
   // SIGSTOP), are still killed eventually.  This task enforces that
   // timer.
   nsCOMPtr<nsITimer> mForceKillTimer;
+
+  // Number of active remote workers. This value is increased when a
+  // RemoteWorkerParent actor is created for this ContentProcess and it is
+  // decreased when the actor is destroyed.
+  // It's touched on PBackground thread and on main-thread.
+  Atomic<uint32_t> mRemoteWorkerActors;
+
   // How many tabs we're waiting to finish their destruction
   // sequence.  Precisely, how many TabParents have called
   // NotifyTabDestroying() but not called NotifyTabDestroyed().
   int32_t mNumDestroyingTabs;
   // True only while this process is in "good health" and may be used for
   // new remote tabs.
   bool mIsAvailable;
   // True only while remote content is being actively used from this process.
--- a/dom/workers/remoteworkers/RemoteWorkerManager.cpp
+++ b/dom/workers/remoteworkers/RemoteWorkerManager.cpp
@@ -134,16 +134,18 @@ RemoteWorkerManager::LaunchInternal(Remo
   RemoteWorkerParent* workerActor =
     static_cast<RemoteWorkerParent*>(
       aTargetActor->Manager()->SendPRemoteWorkerConstructor(aData));
   if (NS_WARN_IF(!workerActor)) {
     AsyncCreationFailed(aController);
     return;
   }
 
+  workerActor->Initialize();
+
   // This makes the link better the 2 actors.
   aController->SetWorkerActor(workerActor);
   workerActor->SetController(aController);
 }
 
 void
 RemoteWorkerManager::AsyncCreationFailed(RemoteWorkerController* aController)
 {
--- a/dom/workers/remoteworkers/RemoteWorkerParent.cpp
+++ b/dom/workers/remoteworkers/RemoteWorkerParent.cpp
@@ -1,43 +1,103 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "RemoteWorkerParent.h"
 #include "RemoteWorkerController.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/Unused.h"
+#include "nsProxyRelease.h"
 
 namespace mozilla {
 
 using namespace ipc;
 
 namespace dom {
 
+namespace {
+
+class UnregisterActorRunnable final : public Runnable
+{
+public:
+  explicit UnregisterActorRunnable(already_AddRefed<ContentParent> aParent)
+    : Runnable("UnregisterActorRunnable")
+    , mContentParent(aParent)
+  {
+    AssertIsOnBackgroundThread();
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    mContentParent->UnregisterRemoveWorkerActor();
+    mContentParent = nullptr;
+
+    return NS_OK;
+  }
+
+private:
+  RefPtr<ContentParent> mContentParent;
+};
+
+} // anonymous
+
 RemoteWorkerParent::RemoteWorkerParent()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(XRE_IsParentProcess());
 }
 
 RemoteWorkerParent::~RemoteWorkerParent()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(XRE_IsParentProcess());
 }
 
 void
+RemoteWorkerParent::Initialize()
+{
+  RefPtr<ContentParent> parent = BackgroundParent::GetContentParent(Manager());
+
+  // Parent is null if the child actor runs on the parent process.
+  if (parent) {
+    parent->RegisterRemoteWorkerActor();
+
+    nsCOMPtr<nsIEventTarget> target =
+      SystemGroup::EventTargetFor(TaskCategory::Other);
+
+    NS_ProxyRelease("RemoteWorkerParent::Initialize ContentParent",
+                    target, parent.forget());
+  }
+}
+
+void
 RemoteWorkerParent::ActorDestroy(IProtocol::ActorDestroyReason)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(XRE_IsParentProcess());
 
+  RefPtr<ContentParent> parent = BackgroundParent::GetContentParent(Manager());
+
+  // Parent is null if the child actor runs on the parent process.
+  if (parent) {
+    RefPtr<UnregisterActorRunnable> r =
+      new UnregisterActorRunnable(parent.forget());
+
+    nsCOMPtr<nsIEventTarget> target =
+      SystemGroup::EventTargetFor(TaskCategory::Other);
+    target->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
+  }
+
   mController = nullptr;
 }
 
 IPCResult
 RemoteWorkerParent::RecvCreated(const bool& aStatus)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(XRE_IsParentProcess());
--- a/dom/workers/remoteworkers/RemoteWorkerParent.h
+++ b/dom/workers/remoteworkers/RemoteWorkerParent.h
@@ -17,16 +17,19 @@ class RemoteWorkerController;
 class RemoteWorkerParent final : public PRemoteWorkerParent
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(RemoteWorkerParent)
 
   RemoteWorkerParent();
 
   void
+  Initialize();
+
+  void
   SetController(RemoteWorkerController* aController);
 
 private:
   ~RemoteWorkerParent();
 
   void
   ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override;