Bug 1510934 - Change ContentParent's alive/dead tracking to launching/alive/dead. r=mrbkap, a=RyanVM
authorJed Davis <jld@mozilla.com>
Sat, 15 Dec 2018 01:38:27 +0000
changeset 509070 ad653a697179ac2d603c30d3c4db9dea7e5e7929
parent 509069 46828493249a63eccefa3ec6bbe4c0c94df4d650
child 509071 097f70517f666382f7606678ab018570f99de907
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap, RyanVM
bugs1510934, 1459212
milestone65.0
Bug 1510934 - Change ContentParent's alive/dead tracking to launching/alive/dead. r=mrbkap, a=RyanVM 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; }
@@ -1235,23 +1237,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;