Bug 1510934 - Change ContentParent's alive/dead tracking to launching/alive/dead. r=mrbkap
authorJed Davis <jld@mozilla.com>
Sat, 15 Dec 2018 01:38:27 +0000
changeset 450820 c2e55bc9206b1dd51be3723b610c62bcb5b82efd
parent 450819 b232989d707c19b4ae49982a243e128fcdd63592
child 450821 74bfa63e922c5782da7f5a58979bbddec0e4fa45
push id35209
push usernerli@mozilla.com
push dateSat, 15 Dec 2018 09:38:42 +0000
treeherdermozilla-central@10aa1d024484 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs1510934, 1459212
milestone66.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 1510934 - Change ContentParent's alive/dead tracking to launching/alive/dead. r=mrbkap We need content processes that are created but not finished launching (not "alive" yet) to be treated differently from ones that have exited (no longer "alive"), so the boolean mIsAlive is expanded to a 3-state enumeration, which could be expanded more in the future if needed. (This is similar to GeckoChildProcessHost::mProcessState, but it's synchronized with the rest of the ContentParent's state, which can lag the GeckoChildProcessHost state due to runnable dispatch.) This patch also removes mIsAvailable/IsAvailable/MarkAsTroubled, which are unused as of bug 1459212. Differential Revision: https://phabricator.services.mozilla.com/D14089
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1504,24 +1504,19 @@ void ContentParent::RemoveFromList() {
     sPrivateContent->RemoveElement(this);
     if (!sPrivateContent->Length()) {
       delete sPrivateContent;
       sPrivateContent = nullptr;
     }
   }
 }
 
-void ContentParent::MarkAsTroubled() {
+void ContentParent::MarkAsDead() {
   RemoveFromList();
-  mIsAvailable = false;
-}
-
-void ContentParent::MarkAsDead() {
-  MarkAsTroubled();
-  mIsAlive = false;
+  mLifecycleState = LifecycleState::DEAD;
 }
 
 void ContentParent::OnChannelError() {
   RefPtr<ContentParent> kungFuDeathGrip(this);
   PContentParent::OnChannelError();
 }
 
 void ContentParent::OnChannelConnected(int32_t pid) {
@@ -1728,17 +1723,17 @@ void ContentParent::ActorDestroy(ActorDe
 
   ChromeBrowsingContext::CleanupContexts(ChildID());
 }
 
 bool ContentParent::TryToRecycle() {
   // This life time check should be replaced by a memory health check (memory
   // usage + fragmentation).
   const double kMaxLifeSpan = 5;
-  if (mShutdownPending || mCalledKillHard || !IsAvailable() ||
+  if (mShutdownPending || mCalledKillHard || !IsAlive() ||
       !mRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE) ||
       (TimeStamp::Now() - mActivateTS).ToSeconds() > kMaxLifeSpan ||
       !PreallocatedProcessManager::Provide(this)) {
     return false;
   }
 
   // The PreallocatedProcessManager took over the ownership let's not keep a
   // reference to it, until we don't take it back.
@@ -1755,18 +1750,18 @@ bool ContentParent::ShouldKeepProcessAli
   if (mRemoteWorkerActors) {
     return true;
   }
 
   if (!sBrowserContentParents) {
     return false;
   }
 
-  // If we have already been marked as troubled/dead, don't prevent shutdown.
-  if (!IsAvailable()) {
+  // If we have already been marked as dead, don't prevent shutdown.
+  if (!IsAlive()) {
     return false;
   }
 
   // Recording/replaying content parents cannot be reused and should not be
   // kept alive.
   if (this->IsRecordingOrReplaying()) {
     return false;
   }
@@ -2239,17 +2234,17 @@ void ContentParent::LaunchSubprocessInte
 
     base::ProcessId procId = base::GetProcId(handle);
     Open(mSubprocess->GetChannel(), procId);
 #ifdef MOZ_CODE_COVERAGE
     Unused << SendShareCodeCoverageMutex(
         CodeCoverageHandler::Get()->GetMutexHandle(procId));
 #endif
 
-    mIsAlive = true;
+    mLifecycleState = LifecycleState::ALIVE;
     InitInternal(aInitialPriority);
 
     ContentProcessManager::GetSingleton()->AddContentProcess(this);
 
     mHangMonitorActor = ProcessHangMonitor::AddProcess(this);
 
     // Set a reply timeout for CPOWs.
     SetReplyTimeoutMs(Preferences::GetInt("dom.ipc.cpow.timeout", 0));
@@ -2330,18 +2325,17 @@ ContentParent::ContentParent(ContentPare
       mActivateTS(mLaunchTS),
       mOpener(aOpener),
       mRemoteType(aRemoteType),
       mChildID(gContentChildID++),
       mGeolocationWatchID(-1),
       mJSPluginID(aJSPluginID),
       mRemoteWorkerActors(0),
       mNumDestroyingTabs(0),
-      mIsAvailable(true),
-      mIsAlive(false),
+      mLifecycleState(LifecycleState::LAUNCHING),
       mIsForBrowser(!mRemoteType.IsEmpty()),
       mRecordReplayState(aRecordReplayState),
       mRecordingFile(aRecordingFile),
       mCalledClose(false),
       mCalledKillHard(false),
       mCreatedPairedMinidumps(false),
       mShutdownPending(false),
       mIPCOpen(true),
@@ -2731,17 +2725,19 @@ void ContentParent::InitInternal(Process
 
   // Start up nsPluginHost and run FindPlugins to cache the plugin list.
   // If this isn't our first content process, just send over cached list.
   RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
   pluginHost->SendPluginsToContent();
   MaybeEnableRemoteInputEventQueue();
 }
 
-bool ContentParent::IsAlive() const { return mIsAlive; }
+bool ContentParent::IsAlive() const {
+  return mLifecycleState == LifecycleState::ALIVE;
+}
 
 int32_t ContentParent::Pid() const {
   if (!mSubprocess || !mSubprocess->GetChildProcessHandle()) {
     return -1;
   }
   return base::GetProcId(mSubprocess->GetChildProcessHandle());
 }
 
@@ -3040,17 +3036,17 @@ ContentParent::Observe(nsISupports* aSub
 
     // Wait for shutdown to complete, so that we receive any shutdown
     // data (e.g. telemetry) from the child before we quit.
     // This loop terminate prematurely based on mForceKillTimer.
     SpinEventLoopUntil([&]() { return !mIPCOpen || mCalledKillHard; });
     NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess");
   }
 
-  if (!mIsAlive || !mSubprocess) return NS_OK;
+  if (!IsAlive() || !mSubprocess) return NS_OK;
 
   // listening for memory pressure event
   if (!strcmp(aTopic, "memory-pressure")) {
     Unused << SendFlushMemory(nsDependentString(aData));
   } else if (!strcmp(aTopic, "nsPref:changed")) {
     // A pref changed. If it's not on the blacklist, inform child processes.
 #define BLACKLIST_ENTRY(s) \
   { s, (sizeof(s) / sizeof(char16_t)) - 1 }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -218,31 +218,31 @@ class ContentParent final : public PCont
     CPIteratorPolicy mPolicy;
 
    public:
     ContentParentIterator(CPIteratorPolicy aPolicy, ContentParent* aCurrent)
         : mCurrent(aCurrent), mPolicy(aPolicy) {}
 
     ContentParentIterator begin() {
       // Move the cursor to the first element that matches the policy.
-      while (mPolicy != eAll && mCurrent && !mCurrent->mIsAlive) {
+      while (mPolicy != eAll && mCurrent && !mCurrent->IsAlive()) {
         mCurrent = mCurrent->LinkedListElement<ContentParent>::getNext();
       }
 
       return *this;
     }
     ContentParentIterator end() {
       return ContentParentIterator(mPolicy, nullptr);
     }
 
     const ContentParentIterator& operator++() {
       MOZ_ASSERT(mCurrent);
       do {
         mCurrent = mCurrent->LinkedListElement<ContentParent>::getNext();
-      } while (mPolicy != eAll && mCurrent && !mCurrent->mIsAlive);
+      } while (mPolicy != eAll && mCurrent && !mCurrent->IsAlive());
 
       return *this;
     }
 
     bool operator!=(const ContentParentIterator& aOther) {
       MOZ_ASSERT(mPolicy == aOther.mPolicy);
       return mCurrent != aOther.mCurrent;
     }
@@ -355,17 +355,19 @@ class ContentParent final : public PCont
   void UnregisterRemoveWorkerActor();
 
   void ReportChildAlreadyBlocked();
 
   bool RequestRunToCompletion();
 
   void UpdateCookieStatus(nsIChannel* aChannel);
 
-  bool IsAvailable() const { return mIsAvailable; }
+  bool IsLaunching() const {
+    return mLifecycleState == LifecycleState::LAUNCHING;
+  }
   bool IsAlive() const override;
 
   virtual bool IsForBrowser() const override { return mIsForBrowser; }
   virtual bool IsForJSPlugin() const override {
     return mJSPluginID != nsFakePluginTag::NOT_JSPLUGIN;
   }
 
   GeckoChildProcessHost* Process() const { return mSubprocess; }
@@ -1237,23 +1239,29 @@ class ContentParent final : public PCont
   // 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.
-  // After mIsAlive goes to false, some previously scheduled IPC traffic may
-  // still pass through.
-  bool mIsAlive;
+
+  // The process starts in the LAUNCHING state, and transitions to
+  // ALIVE once it can accept IPC messages.  It remains ALIVE only
+  // while remote content is being actively used from this process.
+  // After the state becaomes DEAD, some previously scheduled IPC
+  // traffic may still pass through.
+  enum class LifecycleState : uint8_t {
+    LAUNCHING,
+    ALIVE,
+    DEAD,
+  };
+
+  LifecycleState mLifecycleState;
 
   bool mShuttingDown;
   bool mIsForBrowser;
 
   // Whether this process is recording or replaying its execution, and any
   // associated recording file.
   RecordReplayState mRecordReplayState;
   nsString mRecordingFile;