Back out bug 1103036 to resolve shutdown hangs a=backout
authorBill McCloskey <wmccloskey@mozilla.com>
Tue, 17 Mar 2015 11:24:33 -0700
changeset 258461 8a5486269821
parent 258460 56f805ac34ce
child 258462 bf3ca76f10c3
push id4675
push userwmccloskey@mozilla.com
push date2015-04-14 21:16 +0000
treeherdermozilla-beta@8a5486269821 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1103036
milestone38.0
Back out bug 1103036 to resolve shutdown hangs a=backout
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2558,30 +2558,16 @@ ContentChild::RecvLoadPluginResult(const
 bool
 ContentChild::RecvAssociatePluginId(const uint32_t& aPluginId,
                                     const base::ProcessId& aProcessId)
 {
     plugins::PluginModuleContentParent::AssociatePluginId(aPluginId, aProcessId);
     return true;
 }
 
-bool
-ContentChild::RecvShutdown()
-{
-    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
-    if (os) {
-        os->NotifyObservers(this, "content-child-shutdown", nullptr);
-    }
-
-    // Ignore errors here. If this fails, the parent will kill us after a
-    // timeout.
-    unused << SendFinishShutdown();
-    return true;
-}
-
 PBrowserOrId
 ContentChild::GetBrowserOrId(TabChild* aTabChild)
 {
     if (!aTabChild ||
         this == aTabChild->Manager()) {
         return PBrowserOrId(aTabChild);
     }
     else {
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -379,17 +379,16 @@ public:
                                       const bool& aResult) override;
 
     virtual bool RecvStartProfiler(const uint32_t& aEntries,
                                    const double& aInterval,
                                    nsTArray<nsCString>&& aFeatures,
                                    nsTArray<nsCString>&& aThreadNameFilters) override;
     virtual bool RecvStopProfiler() override;
     virtual bool RecvGetProfile(nsCString* aProfile) override;
-    virtual bool RecvShutdown() override;
 
 #ifdef ANDROID
     gfxIntSize GetScreenSize() { return mScreenSize; }
 #endif
 
     // Get the directory for IndexedDB files. We query the parent for this and
     // cache the value
     nsString &GetIndexedDBPath();
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -115,17 +115,16 @@
 #include "nsIObserverService.h"
 #include "nsIPresShell.h"
 #include "nsIScriptError.h"
 #include "nsISiteSecurityService.h"
 #include "nsISpellChecker.h"
 #include "nsIStyleSheet.h"
 #include "nsISupportsPrimitives.h"
 #include "nsISystemMessagesInternal.h"
-#include "nsITimer.h"
 #include "nsIURIFixup.h"
 #include "nsIWindowWatcher.h"
 #include "nsIXULRuntime.h"
 #include "nsMemoryInfoDumper.h"
 #include "nsMemoryReporterManager.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStyleSheetService.h"
 #include "nsThreadUtils.h"
@@ -586,17 +585,16 @@ static uint64_t gContentChildID = 1;
 
 // We want the prelaunched process to know that it's for apps, but not
 // actually for any app in particular.  Use a magic manifest URL.
 // Can't be a static constant.
 #define MAGIC_PREALLOCATED_APP_MANIFEST_URL NS_LITERAL_STRING("{{template}}")
 
 static const char* sObserverTopics[] = {
     "xpcom-shutdown",
-    "profile-before-change",
     NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC,
     "child-memory-reporter-request",
     "memory-pressure",
     "child-gc-request",
     "child-cc-request",
     "child-mmu-request",
     "last-pb-context-exited",
     "file-watcher-update",
@@ -1543,66 +1541,43 @@ ContentParent::TransformPreallocatedInto
     // Reset mAppManifestURL, mIsForBrowser and mOSPrivileges for browser.
     mMetamorphosed = true;
     mOpener = aOpener;
     mAppManifestURL.Truncate();
     mIsForBrowser = true;
 }
 
 void
-ContentParent::ShutDownProcess(ShutDownMethod aMethod)
-{
-#ifdef MOZ_NUWA_PROCESS
-    if (aMethod == SEND_SHUTDOWN_MESSAGE && IsNuwaProcess()) {
-        // We shouldn't send shutdown messages to frozen Nuwa processes,
-        // so just close the channel.
-        aMethod = CLOSE_CHANNEL;
-    }
-#endif
-
-    // Shutting down by sending a shutdown message works differently than the
-    // other methods. We first call Shutdown() in the child. After the child is
-    // ready, it calls FinishShutdown() on us. Then we close the channel.
-    if (aMethod == SEND_SHUTDOWN_MESSAGE) {
-        if (mIPCOpen && !mShutdownPending && SendShutdown()) {
-            mShutdownPending = true;
-            // Start the force-kill timer if we haven't already.
-            StartForceKillTimer();
-        }
-
-        // If call was not successful, the channel must have been broken
-        // somehow, and we will clean up the error in ActorDestroy.
-        return;
-    }
-
+ContentParent::ShutDownProcess(bool aCloseWithError)
+{
     using mozilla::dom::quota::QuotaManager;
 
     if (QuotaManager* quotaManager = QuotaManager::Get()) {
         quotaManager->AbortCloseStoragesForProcess(this);
     }
 
     // If Close() fails with an error, we'll end up back in this function, but
-    // with aMethod = CLOSE_CHANNEL_WITH_ERROR.  It's important that we call
+    // with aCloseWithError = true.  It's important that we call
     // CloseWithError() in this case; see bug 895204.
 
-    if (aMethod == CLOSE_CHANNEL && !mCalledClose) {
+    if (!aCloseWithError && !mCalledClose) {
         // Close() can only be called once: It kicks off the destruction
         // sequence.
         mCalledClose = true;
         Close();
 #ifdef MOZ_NUWA_PROCESS
         // Kill Nuwa process forcibly to break its IPC channels and finalize
         // corresponding parents.
         if (IsNuwaProcess()) {
             KillHard("ShutDownProcess");
         }
 #endif
     }
 
-    if (aMethod == CLOSE_CHANNEL_WITH_ERROR && !mCalledCloseWithError) {
+    if (aCloseWithError && !mCalledCloseWithError) {
         MessageChannel* channel = GetIPCChannel();
         if (channel) {
             mCalledCloseWithError = true;
             channel->CloseWithError();
         }
     }
 
     const InfallibleTArray<POfflineCacheUpdateParent*>& ocuParents =
@@ -1619,27 +1594,16 @@ ContentParent::ShutDownProcess(ShutDownM
 
     // A ContentParent object might not get freed until after XPCOM shutdown has
     // shut down the cycle collector.  But by then it's too late to release any
     // CC'ed objects, so we need to null them out here, while we still can.  See
     // bug 899761.
     ShutDownMessageManager();
 }
 
-bool
-ContentParent::RecvFinishShutdown()
-{
-    // At this point, we already called ShutDownProcess once with
-    // SEND_SHUTDOWN_MESSAGE. To actually close the channel, we call
-    // ShutDownProcess again with CLOSE_CHANNEL.
-    MOZ_ASSERT(mShutdownPending);
-    ShutDownProcess(CLOSE_CHANNEL);
-    return true;
-}
-
 void
 ContentParent::ShutDownMessageManager()
 {
   if (!mMessageManager) {
     return;
   }
 
   mMessageManager->ReceiveMessage(
@@ -1824,41 +1788,28 @@ struct DelayedDeleteContentParentTask : 
     nsRefPtr<ContentParent> mObj;
 };
 
 }
 
 void
 ContentParent::ActorDestroy(ActorDestroyReason why)
 {
-    if (mForceKillTimer) {
-        mForceKillTimer->Cancel();
-        mForceKillTimer = nullptr;
+    if (mForceKillTask) {
+        mForceKillTask->Cancel();
+        mForceKillTask = nullptr;
     }
 
-    // Signal shutdown completion regardless of error state, so we can
-    // finish waiting in the xpcom-shutdown/profile-before-change observer.
-    mIPCOpen = false;
+    ShutDownMessageManager();
 
     if (mHangMonitorActor) {
         ProcessHangMonitor::RemoveProcess(mHangMonitorActor);
         mHangMonitorActor = nullptr;
     }
 
-    if (why == NormalShutdown && !mCalledClose) {
-        // If we shut down normally but haven't called Close, assume somebody
-        // else called Close on us. In that case, we still need to call
-        // ShutDownProcess below to perform other necessary clean up.
-        mCalledClose = true;
-    }
-
-    // Make sure we always clean up.
-    ShutDownProcess(why == NormalShutdown ? CLOSE_CHANNEL
-                                          : CLOSE_CHANNEL_WITH_ERROR);
-
     nsRefPtr<ContentParent> kungFuDeathGrip(this);
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
         size_t length = ArrayLength(sObserverTopics);
         for (size_t i = 0; i < length; ++i) {
             obs->RemoveObserver(static_cast<nsIObserver*>(this),
                                 sObserverTopics[i]);
         }
@@ -1886,16 +1837,18 @@ ContentParent::ActorDestroy(ActorDestroy
         sNuwaPrefUpdates = nullptr;
     }
 #endif
 
     RecvRemoveGeolocationListener();
 
     mConsoleService = nullptr;
 
+    MarkAsDead();
+
     if (obs) {
         nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
 
         props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID);
 
         if (AbnormalShutdown == why) {
             Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT,
                                   NS_LITERAL_CSTRING("content"), 1);
@@ -1937,16 +1890,21 @@ ContentParent::ActorDestroy(ActorDestroy
             }
 #endif
         }
         obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", nullptr);
     }
 
     mIdleListeners.Clear();
 
+    // If the child process was terminated due to a SIGKIL, ShutDownProcess
+    // might not have been called yet.  We must call it to ensure that our
+    // channel is closed, etc.
+    ShutDownProcess(/* closeWithError */ true);
+
     MessageLoop::current()->
         PostTask(FROM_HERE,
                  NewRunnableFunction(DelayedDeleteSubprocess, mSubprocess));
     mSubprocess = nullptr;
 
     // IPDL rules require actors to live on past ActorDestroy, but it
     // may be that the kungFuDeathGrip above is the last reference to
     // |this|.  If so, when we go out of scope here, we're deleted and
@@ -1960,17 +1918,17 @@ ContentParent::ActorDestroy(ActorDestroy
     ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
     nsTArray<ContentParentId> childIDArray =
         cpm->GetAllChildProcessById(this->ChildID());
     for(uint32_t i = 0; i < childIDArray.Length(); i++) {
         ContentParent* cp = cpm->GetContentProcessById(childIDArray[i]);
         MessageLoop::current()->PostTask(
             FROM_HERE,
             NewRunnableMethod(cp, &ContentParent::ShutDownProcess,
-                              CLOSE_CHANNEL));
+                              /* closeWithError */ false));
     }
     cpm->RemoveContentProcess(this->ChildID());
 }
 
 void
 ContentParent::NotifyTabDestroying(PBrowserParent* aTab)
 {
     // There can be more than one PBrowser for a given app process
@@ -1982,56 +1940,44 @@ ContentParent::NotifyTabDestroying(PBrow
     ++mNumDestroyingTabs;
     if (mNumDestroyingTabs != numLiveTabs) {
         return;
     }
 
     // We're dying now, so prevent this content process from being
     // recycled during its shutdown procedure.
     MarkAsDead();
-    StartForceKillTimer();
-}
-
-void
-ContentParent::StartForceKillTimer()
-{
-    if (mForceKillTimer || !mIPCOpen) {
-        return;
-    }
-
+
+    MOZ_ASSERT(!mForceKillTask);
     int32_t timeoutSecs =
         Preferences::GetInt("dom.ipc.tabs.shutdownTimeoutSecs", 5);
     if (timeoutSecs > 0) {
-        mForceKillTimer = do_CreateInstance("@mozilla.org/timer;1");
-        MOZ_ASSERT(mForceKillTimer);
-        mForceKillTimer->InitWithFuncCallback(ContentParent::ForceKillTimerCallback,
-                                              this,
-                                              timeoutSecs * 1000,
-                                              nsITimer::TYPE_ONE_SHOT);
+        MessageLoop::current()->PostDelayedTask(
+            FROM_HERE,
+            mForceKillTask = NewRunnableMethod(this, &ContentParent::KillHard, nullptr),
+            timeoutSecs * 1000);
     }
 }
 
 void
 ContentParent::NotifyTabDestroyed(PBrowserParent* aTab,
                                   bool aNotifiedDestroying)
 {
     if (aNotifiedDestroying) {
         --mNumDestroyingTabs;
     }
 
     // There can be more than one PBrowser for a given app process
     // because of popup windows.  When the last one closes, shut
     // us down.
     if (ManagedPBrowserParent().Length() == 1) {
-        // In the case of normal shutdown, send a shutdown message to child to
-        // allow it to perform shutdown tasks.
         MessageLoop::current()->PostTask(
             FROM_HERE,
             NewRunnableMethod(this, &ContentParent::ShutDownProcess,
-                              SEND_SHUTDOWN_MESSAGE));
+                              /* force */ false));
     }
 }
 
 jsipc::CPOWManager*
 ContentParent::GetCPOWManager()
 {
     if (ManagedPJavaScriptParent().Length()) {
         return CPOWManagerFor(ManagedPJavaScriptParent()[0]);
@@ -2060,27 +2006,26 @@ ContentParent::GetTestShellSingleton()
 }
 
 void
 ContentParent::InitializeMembers()
 {
     mSubprocess = nullptr;
     mChildID = gContentChildID++;
     mGeolocationWatchID = -1;
+    mForceKillTask = nullptr;
     mNumDestroyingTabs = 0;
     mIsAlive = true;
     mMetamorphosed = false;
     mSendPermissionUpdates = false;
     mSendDataStoreInfos = false;
     mCalledClose = false;
     mCalledCloseWithError = false;
     mCalledKillHard = false;
     mCreatedPairedMinidumps = false;
-    mShutdownPending = false;
-    mIPCOpen = true;
     mHangMonitorActor = nullptr;
 }
 
 ContentParent::ContentParent(mozIApplication* aApp,
                              ContentParent* aOpener,
                              bool aIsForBrowser,
                              bool aIsForPreallocated,
                              ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */,
@@ -2245,18 +2190,18 @@ ContentParent::ContentParent(ContentPare
                  false  /* Send registered chrome */);
 
     ContentProcessManager::GetSingleton()->AddContentProcess(this);
 }
 #endif  // MOZ_NUWA_PROCESS
 
 ContentParent::~ContentParent()
 {
-    if (mForceKillTimer) {
-        mForceKillTimer->Cancel();
+    if (mForceKillTask) {
+        mForceKillTask->Cancel();
     }
 
     if (OtherProcess())
         base::CloseProcessHandle(OtherProcess());
 
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
     // We should be removed from all these lists in ActorDestroy.
@@ -2808,27 +2753,18 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
 NS_INTERFACE_MAP_END
 
 NS_IMETHODIMP
 ContentParent::Observe(nsISupports* aSubject,
                        const char* aTopic,
                        const char16_t* aData)
 {
-    if (mSubprocess && (!strcmp(aTopic, "profile-before-change") ||
-                        !strcmp(aTopic, "xpcom-shutdown"))) {
-        // Okay to call ShutDownProcess multiple times.
-        ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
-
-        // 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.
-        while (mIPCOpen) {
-            NS_ProcessNextEvent(nullptr, true);
-        }
+    if (!strcmp(aTopic, "xpcom-shutdown") && mSubprocess) {
+        ShutDownProcess(/* closeWithError */ false);
         NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess");
     }
 
     if (!mIsAlive || !mSubprocess)
         return NS_OK;
 
     // listening for memory pressure event
     if (!strcmp(aTopic, "memory-pressure") &&
@@ -3232,34 +3168,27 @@ ContentParent::AllocPRemoteSpellcheckEng
 
 bool
 ContentParent::DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent *parent)
 {
     delete parent;
     return true;
 }
 
-/* static */ void
-ContentParent::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure)
-{
-    auto self = static_cast<ContentParent*>(aClosure);
-    self->KillHard("ShutDownKill");
-}
-
 void
 ContentParent::KillHard(const char* aReason)
 {
     // On Windows, calling KillHard multiple times causes problems - the
     // process handle becomes invalid on the first call, causing a second call
     // to crash our process - more details in bug 890840.
     if (mCalledKillHard) {
         return;
     }
     mCalledKillHard = true;
-    mForceKillTimer = nullptr;
+    mForceKillTask = nullptr;
 
 #if defined(MOZ_CRASHREPORTER) && !defined(MOZ_B2G)
     if (ManagedPCrashReporterParent().Length() > 0) {
         CrashReporterParent* crashReporter =
             static_cast<CrashReporterParent*>(ManagedPCrashReporterParent()[0]);
 
         // We're about to kill the child process associated with this
         // ContentParent. Something has gone wrong to get us here,
@@ -3278,17 +3207,17 @@ ContentParent::KillHard(const char* aRea
             crashReporter->AnnotateCrashReport(
                 NS_LITERAL_CSTRING("additional_minidumps"),
                 additionalDumps);
             if (IsKillHardAnnotationSet()) {
               crashReporter->AnnotateCrashReport(
                   NS_LITERAL_CSTRING("kill_hard"),
                   GetKillHardAnnotation());
             }
-            nsDependentCString reason(aReason);
+            nsDependentCString reason(aReason ? aReason : "");
             crashReporter->AnnotateCrashReport(
                 NS_LITERAL_CSTRING("ipc_channel_error"),
                 reason);
         }
     }
 #endif
     if (!KillProcess(OtherProcess(), 1, false)) {
         NS_WARNING("failed to kill subprocess!");
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -28,17 +28,16 @@
 #define CHILD_PROCESS_SHUTDOWN_MESSAGE NS_LITERAL_STRING("child-process-shutdown")
 
 class mozIApplication;
 class nsConsoleService;
 class nsICycleCollectorLogSink;
 class nsIDOMBlob;
 class nsIDumpGCAndCCLogsCallback;
 class nsIMemoryReporter;
-class nsITimer;
 class ParentIdleListener;
 
 namespace mozilla {
 class PRemoteSpellcheckEngineParent;
 
 namespace ipc {
 class OptionalURIParams;
 class PFileDescriptorSetParent;
@@ -343,18 +342,16 @@ public:
                                        const URIParams& aDocumentURI,
                                        const bool& stickDocument,
                                        const TabId& aTabId) override;
     virtual bool
     DeallocPOfflineCacheUpdateParent(POfflineCacheUpdateParent* aActor) override;
 
     virtual bool RecvSetOfflinePermission(const IPC::Principal& principal) override;
 
-    virtual bool RecvFinishShutdown() override;
-
 protected:
     void OnChannelConnected(int32_t pid) override;
     virtual void ActorDestroy(ActorDestroyReason why) override;
     void OnNuwaForkTimeout();
 
     bool ShouldContinueFromReplyTimeout() override;
 
 private:
@@ -450,49 +447,32 @@ private:
 
     /**
      * Mark this ContentParent as dead for the purposes of Get*().
      * This method is idempotent.
      */
     void MarkAsDead();
 
     /**
-     * How we will shut down this ContentParent and its subprocess.
-     */
-    enum ShutDownMethod {
-        // Send a shutdown message and wait for FinishShutdown call back.
-        SEND_SHUTDOWN_MESSAGE,
-        // Close the channel ourselves and let the subprocess clean up itself.
-        CLOSE_CHANNEL,
-        // Close the channel with error and let the subprocess clean up itself.
-        CLOSE_CHANNEL_WITH_ERROR,
-    };
-
-    /**
      * Exit the subprocess and vamoose.  After this call IsAlive()
      * will return false and this ContentParent will not be returned
      * by the Get*() funtions.  However, the shutdown sequence itself
      * may be asynchronous.
      *
-     * If aMethod is CLOSE_CHANNEL_WITH_ERROR and this is the first call
-     * to ShutDownProcess, then we'll close our channel using CloseWithError()
+     * If aCloseWithError is true and this is the first call to
+     * ShutDownProcess, then we'll close our channel using CloseWithError()
      * rather than vanilla Close().  CloseWithError() indicates to IPC that this
      * is an abnormal shutdown (e.g. a crash).
      */
-    void ShutDownProcess(ShutDownMethod aMethod);
+    void ShutDownProcess(bool aCloseWithError);
 
     // Perform any steps necesssary to gracefully shtudown the message
     // manager and null out mMessageManager.
     void ShutDownMessageManager();
 
-    // Start the force-kill timer on shutdown.
-    void StartForceKillTimer();
-
-    static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
-
     PCompositorParent*
     AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
                            base::ProcessId aOtherProcess) override;
     PImageBridgeParent*
     AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport,
                             base::ProcessId aOtherProcess) override;
 
     PSharedBufferManagerParent*
@@ -814,17 +794,17 @@ private:
      * expensive.
      */
     nsString mAppName;
 
     // 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;
+    CancelableTask* mForceKillTask;
     // 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 is ready to be used to host remote tabs.
     // This must not be used for new purposes after mIsAlive goes to
     // false, but some previously scheduled IPC traffic may still pass
     // through.
@@ -840,18 +820,16 @@ private:
     bool mIsNuwaProcess;
 
     // These variables track whether we've called Close(), CloseWithError()
     // and KillHard() on our channel.
     bool mCalledClose;
     bool mCalledCloseWithError;
     bool mCalledKillHard;
     bool mCreatedPairedMinidumps;
-    bool mShutdownPending;
-    bool mIPCOpen;
 
     friend class CrashReporterParent;
 
     nsRefPtr<nsConsoleService>  mConsoleService;
     nsConsoleService* GetConsoleService();
 
     nsDataHashtable<nsUint64HashKey, nsRefPtr<ParentIdleListener> > mIdleListeners;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -542,22 +542,16 @@ child:
     async StartProfiler(uint32_t aEntries, double aInterval, nsCString[] aFeatures,
                         nsCString[] aThreadNameFilters);
     async StopProfiler();
     prio(high) sync GetProfile()
       returns (nsCString aProfile);
 
     NuwaFreeze();
 
-    /**
-     * Notify the child to shutdown. The child will in turn call FinishShutdown
-     * and let the parent close the channel.
-     */
-    async Shutdown();
-
     async LoadProcessScript(nsString url);
 
 parent:
     /**
      * Tell the parent process a new accessible document has been created.
      * aParentDoc is the accessible document it was created in if any, and
      * aParentAcc is the id of the accessible in that document the new document
      * is a child of.
@@ -891,21 +885,15 @@ parent:
 
     /**
      * Sets "offline-app" permission for the principal.  Called when we hit
      * a web app with the manifest attribute in <html> and
      * offline-apps.allow_by_default is set to true.
      */
     SetOfflinePermission(Principal principal);
 
-    /**
-     * Notifies the parent to continue shutting down after the child performs
-     * its shutdown tasks.
-     */
-    async FinishShutdown();
-
 both:
      AsyncMessage(nsString aMessage, ClonedMessageData aData,
                   CpowEntry[] aCpows, Principal aPrincipal);
 };
 
 }
 }
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -685,20 +685,17 @@ private:
     }
 
     NS_IMETHOD
     Run()
     {
         MOZ_ASSERT(NS_IsMainThread());
         MOZ_ASSERT(mTabChild);
 
-        // Check in case ActorDestroy was called after RecvDestroy message.
-        if (mTabChild->IPCOpen()) {
-            unused << PBrowserChild::Send__delete__(mTabChild);
-        }
+        unused << PBrowserChild::Send__delete__(mTabChild);
 
         mTabChild = nullptr;
         return NS_OK;
     }
 };
 
 namespace {
 StaticRefPtr<TabChild> sPreallocatedTab;
@@ -854,17 +851,16 @@ TabChild::TabChild(nsIContentChild* aMan
   , mUpdateHitRegion(false)
   , mIgnoreKeyPressEvent(false)
   , mSetTargetAPZCCallback(new TabChildSetTargetAPZCCallback(this))
   , mHasValidInnerSize(false)
   , mDestroyed(false)
   , mUniqueId(aTabId)
   , mDPI(0)
   , mDefaultScale(0)
-  , mIPCOpen(true)
   , mParentIsActive(false)
 {
   // preloaded TabChild should not be added to child map
   if (mUniqueId) {
     MOZ_ASSERT(NestedTabChildMap().find(mUniqueId) == NestedTabChildMap().end());
     NestedTabChildMap()[mUniqueId] = this;
   }
 }
@@ -1604,18 +1600,16 @@ TabChild::DestroyWindow()
     }
 
     mCachedFileDescriptorInfos.Clear();
 }
 
 void
 TabChild::ActorDestroy(ActorDestroyReason why)
 {
-  mIPCOpen = false;
-
   DestroyWindow();
 
   if (mTabChildGlobal) {
     // The messageManager relays messages via the TabChild which
     // no longer exists.
     static_cast<nsFrameMessageManager*>
       (mTabChildGlobal->mMessageManager.get())->Disconnect();
     mTabChildGlobal->mMessageManager = nullptr;
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -479,18 +479,16 @@ public:
      * Native widget remoting protocol for use with windowed plugins with e10s.
      */
     PPluginWidgetChild* AllocPPluginWidgetChild() override;
     bool DeallocPPluginWidgetChild(PPluginWidgetChild* aActor) override;
     nsresult CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut);
 
     nsIntPoint GetChromeDisplacement() { return mChromeDisp; };
 
-    bool IPCOpen() { return mIPCOpen; }
-
     bool ParentIsActive()
     {
       return mParentIsActive;
     }
 
 protected:
     virtual ~TabChild();
 
@@ -621,17 +619,16 @@ private:
     nsRefPtr<SetTargetAPZCCallback> mSetTargetAPZCCallback;
     bool mHasValidInnerSize;
     bool mDestroyed;
     // Position of tab, relative to parent widget (typically the window)
     nsIntPoint mChromeDisp;
     TabId mUniqueId;
     float mDPI;
     double mDefaultScale;
-    bool mIPCOpen;
     bool mParentIsActive;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
 }
 }