Bug 1338493 - Part 2, move HttpBackgroundChannelChild to socket transport thread. r?mayhemer draft
authorShih-Chiang Chien <schien@mozilla.com>
Wed, 28 Jun 2017 22:04:17 +0800
changeset 602122 5962ce1243e3e2d34dcf54f53d50c5c6c9060de9
parent 602121 923eab1f89db95bbc25288b4ebe7e187575b7561
child 635469 79fad38fe39aa2bafbc7a2576a85edc5d9aaf13a
push id66286
push userschien@mozilla.com
push dateThu, 29 Jun 2017 17:52:09 +0000
reviewersmayhemer
bugs1338493
milestone56.0a1
Bug 1338493 - Part 2, move HttpBackgroundChannelChild to socket transport thread. r?mayhemer Move HttpBackgroundChannelChild from main thread to socket transport thread. Allow HttpChannelChild.mBgChild to be used on both main thread and STS thread under mutex protection. MozReview-Commit-ID: 9WAXmJLr8HT
netwerk/protocol/http/HttpBackgroundChannelChild.cpp
netwerk/protocol/http/HttpBackgroundChannelChild.h
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.h
netwerk/protocol/http/nsHttpChannel.cpp
--- a/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
@@ -6,22 +6,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // HttpLog.h should generally be included first
 #include "HttpLog.h"
 
 #include "HttpBackgroundChannelChild.h"
 
 #include "HttpChannelChild.h"
-#include "MainThreadUtils.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Unused.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
+#include "nsSocketTransportService2.h"
 
 using mozilla::ipc::BackgroundChild;
 using mozilla::ipc::IPCResult;
 
 namespace mozilla {
 namespace net {
 
 // Callbacks for PBackgroundChild creation
@@ -30,32 +30,33 @@ class BackgroundChannelCreateCallback fi
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
 
   explicit BackgroundChannelCreateCallback(HttpBackgroundChannelChild* aBgChild)
     : mBgChild(aBgChild)
   {
+    MOZ_ASSERT(OnSocketThread());
     MOZ_ASSERT(aBgChild);
   }
 
 private:
   virtual ~BackgroundChannelCreateCallback() { }
 
   RefPtr<HttpBackgroundChannelChild> mBgChild;
 };
 
 NS_IMPL_ISUPPORTS(BackgroundChannelCreateCallback,
                   nsIIPCBackgroundChildCreateCallback)
 
 void
 BackgroundChannelCreateCallback::ActorCreated(PBackgroundChild* aActor)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
   MOZ_ASSERT(aActor);
   MOZ_ASSERT(mBgChild);
 
   if (!mBgChild->mChannelChild) {
     // HttpChannelChild is closed during PBackground creation,
     // abort the rest of steps.
     return;
   }
@@ -65,22 +66,24 @@ BackgroundChannelCreateCallback::ActorCr
                                                      channelId)) {
     ActorFailed();
     return;
   }
 
   // hold extra reference for IPDL
   RefPtr<HttpBackgroundChannelChild> child = mBgChild;
   Unused << child.forget().take();
+
+  mBgChild->mChannelChild->OnBackgroundChildReady(mBgChild);
 }
 
 void
 BackgroundChannelCreateCallback::ActorFailed()
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
   MOZ_ASSERT(mBgChild);
 
   mBgChild->OnBackgroundChannelCreationFailed();
 }
 
 // HttpBackgroundChannelChild
 HttpBackgroundChannelChild::HttpBackgroundChannelChild()
 {
@@ -90,44 +93,44 @@ HttpBackgroundChannelChild::~HttpBackgro
 {
 }
 
 nsresult
 HttpBackgroundChannelChild::Init(HttpChannelChild* aChannelChild)
 {
   LOG(("HttpBackgroundChannelChild::Init [this=%p httpChannel=%p channelId=%"
        PRIu64 "]\n", this, aChannelChild, aChannelChild->ChannelId()));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
   NS_ENSURE_ARG(aChannelChild);
 
   mChannelChild = aChannelChild;
 
   if (NS_WARN_IF(!CreateBackgroundChannel())) {
     mChannelChild = nullptr;
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 void
 HttpBackgroundChannelChild::OnChannelClosed()
 {
   LOG(("HttpBackgroundChannelChild::OnChannelClosed [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
 
   // HttpChannelChild is not going to handle any incoming message.
   mChannelChild = nullptr;
 }
 
 void
 HttpBackgroundChannelChild::OnStartRequestReceived()
 {
   LOG(("HttpBackgroundChannelChild::OnStartRequestReceived [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
   MOZ_ASSERT(mChannelChild);
   MOZ_ASSERT(!mStartReceived); // Should only be called once.
 
   mStartReceived = true;
 
   nsTArray<nsCOMPtr<nsIRunnable>> runnables;
   runnables.SwapElements(mQueuedRunnables);
 
@@ -141,81 +144,82 @@ HttpBackgroundChannelChild::OnStartReque
   MOZ_ASSERT(mQueuedRunnables.IsEmpty());
 }
 
 void
 HttpBackgroundChannelChild::OnBackgroundChannelCreationFailed()
 {
   LOG(("HttpBackgroundChannelChild::OnBackgroundChannelCreationFailed"
        " [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
 
   if (mChannelChild) {
     RefPtr<HttpChannelChild> channelChild = mChannelChild.forget();
-    channelChild->FailedAsyncOpen(NS_ERROR_UNEXPECTED);
+    channelChild->OnBackgroundChildDestroyed(this);
   }
 }
 
 bool
 HttpBackgroundChannelChild::CreateBackgroundChannel()
 {
   LOG(("HttpBackgroundChannelChild::CreateBackgroundChannel [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
 
   RefPtr<BackgroundChannelCreateCallback> callback =
     new BackgroundChannelCreateCallback(this);
 
   return BackgroundChild::GetOrCreateForCurrentThread(callback);
 }
 
 bool
 HttpBackgroundChannelChild::IsWaitingOnStartRequest()
 {
+  MOZ_ASSERT(OnSocketThread());
   // Need to wait for OnStartRequest if it is sent by
   // parent process but not received by content process.
   return (mStartSent && !mStartReceived);
 }
 
 // PHttpBackgroundChannelChild
 IPCResult
 HttpBackgroundChannelChild::RecvOnStartRequestSent()
 {
   LOG(("HttpBackgroundChannelChild::RecvOnStartRequestSent [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
   MOZ_ASSERT(!mStartSent); // Should only receive this message once.
 
   mStartSent = true;
   return IPC_OK();
 }
 
 IPCResult
 HttpBackgroundChannelChild::RecvOnTransportAndData(
                                                const nsresult& aChannelStatus,
                                                const nsresult& aTransportStatus,
                                                const uint64_t& aOffset,
                                                const uint32_t& aCount,
                                                const nsCString& aData)
 {
   LOG(("HttpBackgroundChannelChild::RecvOnTransportAndData [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
 
   if (NS_WARN_IF(!mChannelChild)) {
     return IPC_OK();
   }
 
   if (IsWaitingOnStartRequest()) {
     LOG(("  > pending until OnStartRequest [offset=%" PRIu64 " count=%" PRIu32
          "]\n", aOffset, aCount));
 
     mQueuedRunnables.AppendElement(NewRunnableMethod<const nsresult,
                                                      const nsresult,
                                                      const uint64_t,
                                                      const uint32_t,
                                                      const nsCString>(
-      "net::HttpBackgroundChannelChild::RecvOnTransportAndData",
+      "HttpBackgroundChannelChild::RecvOnTransportAndData",
       this,
       &HttpBackgroundChannelChild::RecvOnTransportAndData,
       aChannelStatus,
       aTransportStatus,
       aOffset,
       aCount,
       aData));
 
@@ -232,29 +236,29 @@ HttpBackgroundChannelChild::RecvOnTransp
 }
 
 IPCResult
 HttpBackgroundChannelChild::RecvOnStopRequest(
                                             const nsresult& aChannelStatus,
                                             const ResourceTimingStruct& aTiming)
 {
   LOG(("HttpBackgroundChannelChild::RecvOnStopRequest [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
 
   if (NS_WARN_IF(!mChannelChild)) {
     return IPC_OK();
   }
 
   if (IsWaitingOnStartRequest()) {
     LOG(("  > pending until OnStartRequest [status=%" PRIx32 "]\n",
          static_cast<uint32_t>(aChannelStatus)));
 
     mQueuedRunnables.AppendElement(
       NewRunnableMethod<const nsresult, const ResourceTimingStruct>(
-        "net::HttpBackgroundChannelChild::RecvOnStopRequest",
+        "HttpBackgroundChannelChild::RecvOnStopRequest",
         this,
         &HttpBackgroundChannelChild::RecvOnStopRequest,
         aChannelStatus,
         aTiming));
 
     return IPC_OK();
   }
 
@@ -264,29 +268,29 @@ HttpBackgroundChannelChild::RecvOnStopRe
 }
 
 IPCResult
 HttpBackgroundChannelChild::RecvOnProgress(const int64_t& aProgress,
                                            const int64_t& aProgressMax)
 {
   LOG(("HttpBackgroundChannelChild::RecvOnProgress [this=%p progress=%"
        PRId64 " max=%" PRId64 "]\n", this, aProgress, aProgressMax));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
 
   if (NS_WARN_IF(!mChannelChild)) {
     return IPC_OK();
   }
 
   if (IsWaitingOnStartRequest()) {
     LOG(("  > pending until OnStartRequest [progress=%" PRId64 " max=%"
          PRId64 "]\n", aProgress, aProgressMax));
 
     mQueuedRunnables.AppendElement(
       NewRunnableMethod<const int64_t, const int64_t>(
-        "net::HttpBackgroundChannelChild::RecvOnProgress",
+        "HttpBackgroundChannelChild::RecvOnProgress",
         this,
         &HttpBackgroundChannelChild::RecvOnProgress,
         aProgress,
         aProgressMax));
 
     return IPC_OK();
   }
 
@@ -295,168 +299,183 @@ HttpBackgroundChannelChild::RecvOnProgre
   return IPC_OK();
 }
 
 IPCResult
 HttpBackgroundChannelChild::RecvOnStatus(const nsresult& aStatus)
 {
   LOG(("HttpBackgroundChannelChild::RecvOnStatus [this=%p status=%"
        PRIx32 "]\n", this, static_cast<uint32_t>(aStatus)));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
 
   if (NS_WARN_IF(!mChannelChild)) {
     return IPC_OK();
   }
 
   if (IsWaitingOnStartRequest()) {
     LOG(("  > pending until OnStartRequest [status=%" PRIx32 "]\n",
          static_cast<uint32_t>(aStatus)));
 
     mQueuedRunnables.AppendElement(NewRunnableMethod<const nsresult>(
-      "net::HttpBackgroundChannelChild::RecvOnStatus",
+      "HttpBackgroundChannelChild::RecvOnStatus",
       this,
       &HttpBackgroundChannelChild::RecvOnStatus,
       aStatus));
 
     return IPC_OK();
   }
 
   mChannelChild->ProcessOnStatus(aStatus);
 
   return IPC_OK();
 }
 
 IPCResult
 HttpBackgroundChannelChild::RecvFlushedForDiversion()
 {
   LOG(("HttpBackgroundChannelChild::RecvFlushedForDiversion [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
 
   if (NS_WARN_IF(!mChannelChild)) {
     return IPC_OK();
   }
 
   if (IsWaitingOnStartRequest()) {
     LOG(("  > pending until OnStartRequest\n"));
 
     mQueuedRunnables.AppendElement(NewRunnableMethod(
-      "net::HttpBackgroundChannelChild::RecvFlushedForDiversion",
+      "HttpBackgroundChannelChild::RecvFlushedForDiversion",
       this,
       &HttpBackgroundChannelChild::RecvFlushedForDiversion));
 
     return IPC_OK();
   }
 
   mChannelChild->ProcessFlushedForDiversion();
 
   return IPC_OK();
 }
 
 IPCResult
 HttpBackgroundChannelChild::RecvDivertMessages()
 {
   LOG(("HttpBackgroundChannelChild::RecvDivertMessages [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
 
   if (NS_WARN_IF(!mChannelChild)) {
     return IPC_OK();
   }
 
   if (IsWaitingOnStartRequest()) {
     LOG(("  > pending until OnStartRequest\n"));
 
     mQueuedRunnables.AppendElement(
-      NewRunnableMethod("net::HttpBackgroundChannelChild::RecvDivertMessages",
+      NewRunnableMethod("HttpBackgroundChannelChild::RecvDivertMessages",
                         this,
                         &HttpBackgroundChannelChild::RecvDivertMessages));
 
     return IPC_OK();
   }
 
   mChannelChild->ProcessDivertMessages();
 
   return IPC_OK();
 }
 
 IPCResult
 HttpBackgroundChannelChild::RecvNotifyTrackingProtectionDisabled()
 {
   LOG(("HttpBackgroundChannelChild::RecvNotifyTrackingProtectionDisabled [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
 
   if (NS_WARN_IF(!mChannelChild)) {
     return IPC_OK();
   }
 
   // NotifyTrackingProtectionDisabled has no order dependency to OnStartRequest.
   // It this be handled as soon as possible
   mChannelChild->ProcessNotifyTrackingProtectionDisabled();
 
   return IPC_OK();
 }
 
 IPCResult
 HttpBackgroundChannelChild::RecvNotifyTrackingResource()
 {
   LOG(("HttpBackgroundChannelChild::RecvNotifyTrackingResource [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
 
   if (NS_WARN_IF(!mChannelChild)) {
     return IPC_OK();
   }
 
   // NotifyTrackingResource has no order dependency to OnStartRequest.
   // It this be handled as soon as possible
   mChannelChild->ProcessNotifyTrackingResource();
 
   return IPC_OK();
 }
 
 IPCResult
 HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo(const ClassifierInfo& info)
 {
   LOG(("HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
 
   if (NS_WARN_IF(!mChannelChild)) {
     return IPC_OK();
   }
 
   // SetClassifierMatchedInfo has no order dependency to OnStartRequest.
   // It this be handled as soon as possible
   mChannelChild->ProcessSetClassifierMatchedInfo(info.list(), info.provider(), info.prefix());
 
   return IPC_OK();
 }
 
 void
 HttpBackgroundChannelChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   LOG(("HttpBackgroundChannelChild::ActorDestroy[this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!OnSocketThread()) {
+    // PBackgroundChild might be destroyed during shutdown and
+    // ActorDestroy will be called on main thread directly.
+    // Simply disconnect with HttpChannelChild to release memory.
+    mChannelChild = nullptr;
+    RefPtr<HttpBackgroundChannelChild> self = this;
+    mQueuedRunnables.AppendElement(NS_NewRunnableFunction(
+      "HttpBackgroundChannelChild::ActorDestroyNonSTSThread", [self]() {
+        MOZ_ASSERT(NS_IsMainThread());
+        self->mChannelChild = nullptr;
+      }));
+    return;
+  }
+
+  MOZ_ASSERT(OnSocketThread());
 
   // Ensure all IPC messages received before ActorDestroy can be
   // handled correctly. If there is any pending IPC message, destroyed
   // mChannelChild until those messages are flushed.
   if (!mQueuedRunnables.IsEmpty()) {
     LOG(("  > pending until queued messages are flushed\n"));
     RefPtr<HttpBackgroundChannelChild> self = this;
     mQueuedRunnables.AppendElement(NS_NewRunnableFunction(
-      "net::HttpBackgroundChannelChild::ActorDestroy", [self]() {
+      "HttpBackgroundChannelChild::ActorDestroy", [self]() {
         MOZ_ASSERT(NS_IsMainThread());
         RefPtr<HttpChannelChild> channelChild = self->mChannelChild.forget();
 
         if (channelChild) {
-          channelChild->OnBackgroundChildDestroyed();
+          channelChild->OnBackgroundChildDestroyed(self);
         }
       }));
     return;
   }
 
   if (mChannelChild) {
     RefPtr<HttpChannelChild> channelChild = mChannelChild.forget();
 
-    channelChild->OnBackgroundChildDestroyed();
+    channelChild->OnBackgroundChildDestroyed(this);
   }
 }
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/HttpBackgroundChannelChild.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.h
@@ -90,22 +90,25 @@ private:
 
   // Associated HttpChannelChild for handling the channel events.
   // Will be removed while failed to create background channel,
   // destruction of the background channel, or explicitly dissociation
   // via OnChannelClosed callback.
   RefPtr<HttpChannelChild> mChannelChild;
 
   // True if OnStartRequest is received by HttpChannelChild.
+  // Should only access on STS thread.
   bool mStartReceived = false;
 
   // True if OnStartRequest is sent by HttpChannelParent.
+  // Should only access on STS thread.
   bool mStartSent = false;
 
   // Store pending messages that require to be handled after OnStartRequest.
   // Should be flushed after OnStartRequest is received and handled.
+  // Should only access on STS thread.
   nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_net_HttpBackgroundChannelChild_h
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -494,17 +494,17 @@ protected:
   // HTTP Upgrade Data
   nsCString                        mUpgradeProtocol;
   nsCOMPtr<nsIHttpUpgradeListener> mUpgradeProtocolCallback;
 
   // Resumable channel specific data
   nsCString                         mEntityID;
   uint64_t                          mStartPos;
 
-  nsresult                          mStatus;
+  Atomic<nsresult, ReleaseAcquire>  mStatus;
   uint32_t                          mLoadFlags;
   uint32_t                          mCaps;
   uint32_t                          mClassOfService;
   int16_t                           mPriority;
   uint8_t                           mRedirectionLimit;
 
   uint32_t                          mApplyConversion            : 1;
   uint32_t                          mIsPending                  : 1;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -45,16 +45,17 @@
 #include "nsContentSecurityManager.h"
 #include "nsIDeprecationWarner.h"
 #include "nsICompressConvStats.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIEventTarget.h"
 #include "nsRedirectHistoryEntry.h"
+#include "nsSocketTransportService2.h"
 #include "nsStreamUtils.h"
 #include "nsThreadUtils.h"
 
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 #endif
 
 using namespace mozilla::dom;
@@ -172,16 +173,17 @@ HttpChannelChild::HttpChannelChild()
   , mSuspendSent(false)
   , mSynthesizedResponse(false)
   , mShouldInterceptSubsequentRedirect(false)
   , mRedirectingForSubsequentSynthesizedResponse(false)
   , mPostRedirectChannelShouldIntercept(false)
   , mPostRedirectChannelShouldUpgrade(false)
   , mShouldParentIntercept(false)
   , mSuspendParentAfterSynthesizeResponse(false)
+  , mBgChildMutex("HttpChannelChild::BgChildMutex")
   , mEventTargetMutex("HttpChannelChild::EventTargetMutex")
 {
   LOG(("Creating HttpChannelChild @%p\n", this));
 
   mChannelCreationTime = PR_Now();
   mChannelCreationTimestamp = TimeStamp::Now();
   mAsyncOpenTime = TimeStamp::Now();
   mEventQ = new ChannelEventQueue(static_cast<nsIHttpChannel*>(this));
@@ -290,29 +292,56 @@ HttpChannelChild::ReleaseIPDLReference()
   Release();
 }
 
 void
 HttpChannelChild::OnBackgroundChildReady(HttpBackgroundChannelChild* aBgChild)
 {
   LOG(("HttpChannelChild::OnBackgroundChildReady [this=%p, bgChild=%p]\n",
        this, aBgChild));
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mBgChild);
-
-  MOZ_ASSERT(mBgChild == aBgChild);
+  MOZ_ASSERT(OnSocketThread());
+
+  {
+    MutexAutoLock lock(mBgChildMutex);
+
+    // mBgChild might be removed or replaced while the original background
+    // channel is inited on STS thread.
+    if (mBgChild != aBgChild) {
+      return;
+    }
+
+    MOZ_ASSERT(mBgInitFailCallback);
+    mBgInitFailCallback = nullptr;
+  }
 }
 
 void
-HttpChannelChild::OnBackgroundChildDestroyed()
+HttpChannelChild::OnBackgroundChildDestroyed(HttpBackgroundChannelChild* aBgChild)
 {
   LOG(("HttpChannelChild::OnBackgroundChildDestroyed [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
-
-  mBgChild = nullptr;
+  MOZ_ASSERT(OnSocketThread());
+
+  nsCOMPtr<nsIRunnable> callback;
+  {
+    MutexAutoLock lock(mBgChildMutex);
+
+    // mBgChild might be removed or replaced while the original background
+    // channel is destroyed on STS thread.
+    if (aBgChild != mBgChild) {
+      return;
+    }
+
+    mBgChild = nullptr;
+    callback = mBgInitFailCallback.forget();
+  }
+
+  if (callback) {
+    nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
+    neckoTarget->Dispatch(callback, NS_DISPATCH_NORMAL);
+  }
 }
 
 class AssociateApplicationCacheEvent : public NeckoTargetChannelEvent<HttpChannelChild>
 {
   public:
     AssociateApplicationCacheEvent(HttpChannelChild* aChild,
                                    const nsCString &aGroupID,
                                    const nsCString &aClientID)
@@ -454,19 +483,33 @@ HttpChannelChild::RecvOnStartRequest(con
                                               useResponseHead, requestHeaders,
                                               isFromCache, cacheEntryAvailable,
                                               cacheFetchCount, cacheLastFetched,
                                               cacheExpirationTime, cachedCharset,
                                               securityInfoSerialization,
                                               selfAddr, peerAddr, cacheKey,
                                               altDataType, altDataLen));
 
-  MOZ_ASSERT(mBgChild);
-  if (mBgChild) {
-    mBgChild->OnStartRequestReceived();
+  {
+    // Child's mEventQ is to control the execution order of the IPC messages
+    // from both main thread IPDL and PBackground IPDL.
+    // To guarantee the ordering, PBackground IPC messages that are sent after
+    // OnStartRequest will be throttled until OnStartRequest hits the Child's
+    // mEventQ.
+    MutexAutoLock lock(mBgChildMutex);
+
+    if (mBgChild) {
+      MOZ_RELEASE_ASSERT(gSocketTransportService);
+      DebugOnly<nsresult> rv =
+        gSocketTransportService->Dispatch(
+          NewRunnableMethod(
+            "HttpBackgroundChannelChild::OnStartRequestReceived",
+            mBgChild, &HttpBackgroundChannelChild::OnStartRequestReceived),
+        NS_DISPATCH_NORMAL);
+    }
   }
 
   return IPC_OK();
 }
 
 void
 HttpChannelChild::OnStartRequest(const nsresult& channelStatus,
                                  const nsHttpResponseHead& responseHead,
@@ -692,17 +735,17 @@ class TransportAndDataEvent : public Cha
 void
 HttpChannelChild::ProcessOnTransportAndData(const nsresult& aChannelStatus,
                                             const nsresult& aTransportStatus,
                                             const uint64_t& aOffset,
                                             const uint32_t& aCount,
                                             const nsCString& aData)
 {
   LOG(("HttpChannelChild::ProcessOnTransportAndData [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
   MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
                      "Should not be receiving any more callbacks from parent!");
   mEventQ->RunOrEnqueue(new TransportAndDataEvent(this, aChannelStatus,
                                                   aTransportStatus, aData,
                                                   aOffset, aCount),
                         mDivertingToParent);
 }
 
@@ -751,29 +794,31 @@ HttpChannelChild::OnTransportAndData(con
   LOG(("HttpChannelChild::OnTransportAndData [this=%p]\n", this));
 
   if (!mCanceled && NS_SUCCEEDED(mStatus)) {
     mStatus = channelStatus;
   }
 
   // For diversion to parent, just SendDivertOnDataAvailable.
   if (mDivertingToParent) {
+    MOZ_ASSERT(NS_IsMainThread());
     MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
       "Should not be processing any more callbacks from parent!");
 
     SendDivertOnDataAvailable(data, offset, count);
     return;
   }
 
   if (mCanceled)
     return;
 
   if (mUnknownDecoderInvolved) {
     LOG(("UnknownDecoder is involved queue OnDataAvailable call. [this=%p]",
          this));
+    MOZ_ASSERT(NS_IsMainThread());
     mUnknownDecoderEventQ.AppendElement(
       MakeUnique<MaybeDivertOnDataHttpEvent>(this, data, offset, count));
   }
 
   // Hold queue lock throughout all three calls, else we might process a later
   // necko msg in between them.
   AutoEventEnqueuer ensureSerialDispatch(mEventQ);
 
@@ -918,17 +963,17 @@ class StopRequestEvent : public NeckoTar
   ResourceTimingStruct mTiming;
 };
 
 void
 HttpChannelChild::ProcessOnStopRequest(const nsresult& aChannelStatus,
                                        const ResourceTimingStruct& aTiming)
 {
   LOG(("HttpChannelChild::ProcessOnStopRequest [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
   MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
     "Should not be receiving any more callbacks from parent!");
 
   mEventQ->RunOrEnqueue(new StopRequestEvent(this, aChannelStatus, aTiming),
                         mDivertingToParent);
 }
 
 class MaybeDivertOnStopHttpEvent : public NeckoTargetChannelEvent<HttpChannelChild>
@@ -948,17 +993,18 @@ class MaybeDivertOnStopHttpEvent : publi
  private:
   nsresult mChannelStatus;
 };
 
 void
 HttpChannelChild::MaybeDivertOnStop(const nsresult& aChannelStatus)
 {
   LOG(("HttpChannelChild::MaybeDivertOnStop [this=%p, "
-       "mDivertingToParent=%d status=%" PRIx32 "]", this, mDivertingToParent,
+       "mDivertingToParent=%d status=%" PRIx32 "]", this,
+       static_cast<bool>(mDivertingToParent),
        static_cast<uint32_t>(aChannelStatus)));
   if (mDivertingToParent) {
     SendDivertOnStopRequest(aChannelStatus);
   }
 }
 
 void
 HttpChannelChild::OnStopRequest(const nsresult& channelStatus,
@@ -971,45 +1017,20 @@ HttpChannelChild::OnStopRequest(const ns
   if (mDivertingToParent) {
     MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
       "Should not be processing any more callbacks from parent!");
 
     SendDivertOnStopRequest(channelStatus);
     return;
   }
 
-  // In thread retargeting is enabled, there might be Runnable for
-  // DoOnStatus/DoOnProgress sit in the main thread event target. We need to
-  // ensure OnStopRequest is fired after that by postponing the
-  // ChannelEventQueue processing to the end of main thread event target.
-  // This workaround can be removed after bug 1338493 is complete.
-  if (mODATarget) {
-    {
-      MutexAutoLock lock(mEventTargetMutex);
-      mODATarget = nullptr;
-    }
-    mEventQ->Suspend();
-    UniquePtr<ChannelEvent> stopEvent =
-      MakeUnique<StopRequestEvent>(this, channelStatus, timing);
-    mEventQ->PrependEvent(stopEvent);
-
-    nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
-    MOZ_ASSERT(neckoTarget);
-
-    DebugOnly<nsresult> rv = neckoTarget->Dispatch(
-      NewRunnableMethod(
-        "net::ChannelEventQueue::Resume", mEventQ, &ChannelEventQueue::Resume),
-      NS_DISPATCH_NORMAL);
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-    return;
-  }
-
   if (mUnknownDecoderInvolved) {
    LOG(("UnknownDecoder is involved queue OnStopRequest call. [this=%p]",
         this));
+    MOZ_ASSERT(NS_IsMainThread());
     mUnknownDecoderEventQ.AppendElement(
       MakeUnique<MaybeDivertOnStopHttpEvent>(this, channelStatus));
   }
 
   nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener);
   if (conv) {
       conv->GetDecodedDataLength(&mDecodedBodySize);
   }
@@ -1155,17 +1176,17 @@ class ProgressEvent : public NeckoTarget
   int64_t mProgress, mProgressMax;
 };
 
 void
 HttpChannelChild::ProcessOnProgress(const int64_t& aProgress,
                                     const int64_t& aProgressMax)
 {
   LOG(("HttpChannelChild::ProcessOnProgress [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
   mEventQ->RunOrEnqueue(new ProgressEvent(this, aProgress, aProgressMax));
 }
 
 void
 HttpChannelChild::OnProgress(const int64_t& progress,
                              const int64_t& progressMax)
 {
   LOG(("HttpChannelChild::OnProgress [this=%p progress=%" PRId64 "/%" PRId64 "]\n",
@@ -1203,17 +1224,17 @@ class StatusEvent : public NeckoTargetCh
  private:
   nsresult mStatus;
 };
 
 void
 HttpChannelChild::ProcessOnStatus(const nsresult& aStatus)
 {
   LOG(("HttpChannelChild::ProcessOnStatus [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
   mEventQ->RunOrEnqueue(new StatusEvent(this, aStatus));
 }
 
 void
 HttpChannelChild::OnStatus(const nsresult& status)
 {
   LOG(("HttpChannelChild::OnStatus [this=%p status=%" PRIx32 "]\n",
        this, static_cast<uint32_t>(status)));
@@ -1294,31 +1315,36 @@ HttpChannelChild::FailedAsyncOpen(const 
   if (mIPCOpen) {
     TrySendDeletingChannel();
   }
 }
 
 void
 HttpChannelChild::CleanupBackgroundChannel()
 {
+  MutexAutoLock lock(mBgChildMutex);
+
   LOG(("HttpChannelChild::CleanupBackgroundChannel [this=%p bgChild=%p]\n",
        this, mBgChild.get()));
+
+  mBgInitFailCallback = nullptr;
+
   if (!mBgChild) {
     return;
   }
 
   RefPtr<HttpBackgroundChannelChild> bgChild = mBgChild.forget();
 
-  if (!NS_IsMainThread()) {
-    SystemGroup::Dispatch(
-      "HttpChannelChild::CleanupBackgroundChannel",
-      TaskCategory::Other,
-      NewRunnableMethod("net::HttpBackgroundChannelChild::OnChannelClosed",
-                        bgChild,
-                        &HttpBackgroundChannelChild::OnChannelClosed));
+  MOZ_RELEASE_ASSERT(gSocketTransportService);
+  if (!OnSocketThread()) {
+    gSocketTransportService->Dispatch(
+      NewRunnableMethod(
+        "HttpBackgroundChannelChild::OnChannelClosed",
+        bgChild, &HttpBackgroundChannelChild::OnChannelClosed),
+      NS_DISPATCH_NORMAL);
   } else {
     bgChild->OnChannelClosed();
   }
 }
 
 void
 HttpChannelChild::DoNotifyListenerCleanup()
 {
@@ -1680,38 +1706,51 @@ class HttpFlushedForDiversionEvent : pub
     mChild->FlushedForDiversion();
   }
 };
 
 void
 HttpChannelChild::ProcessFlushedForDiversion()
 {
   LOG(("HttpChannelChild::ProcessFlushedForDiversion [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
   MOZ_RELEASE_ASSERT(mDivertingToParent);
 
   mEventQ->RunOrEnqueue(new HttpFlushedForDiversionEvent(this), true);
 }
 
 void
 HttpChannelChild::ProcessNotifyTrackingProtectionDisabled()
 {
   LOG(("HttpChannelChild::ProcessNotifyTrackingProtectionDisabled [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsChannelClassifier::NotifyTrackingProtectionDisabled(this);
+  MOZ_ASSERT(OnSocketThread());
+
+  RefPtr<HttpChannelChild> self = this;
+  nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
+  mainTarget->Dispatch(
+    NS_NewRunnableFunction(
+      "nsChannelClassifier::NotifyTrackingProtectionDisabled",
+      [self]() {
+        nsChannelClassifier::NotifyTrackingProtectionDisabled(self);
+      }),
+    NS_DISPATCH_NORMAL);
 }
 
 void
 HttpChannelChild::ProcessNotifyTrackingResource()
 {
   LOG(("HttpChannelChild::ProcessNotifyTrackingResource [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
-
-  SetIsTrackingResource();
+  MOZ_ASSERT(OnSocketThread());
+
+  nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
+  mainTarget->Dispatch(
+    NewRunnableMethod(
+      "HttpChannelChild::SetIsTrackingResource",
+      this, &HttpChannelChild::SetIsTrackingResource),
+    NS_DISPATCH_NORMAL);
 }
 
 void
 HttpChannelChild::FlushedForDiversion()
 {
   LOG(("HttpChannelChild::FlushedForDiversion [this=%p]\n", this));
   MOZ_RELEASE_ASSERT(mDivertingToParent);
 
@@ -1724,32 +1763,46 @@ HttpChannelChild::FlushedForDiversion()
 }
 
 void
 HttpChannelChild::ProcessSetClassifierMatchedInfo(const nsCString& aList,
                                                   const nsCString& aProvider,
                                                   const nsCString& aPrefix)
 {
   LOG(("HttpChannelChild::ProcessSetClassifierMatchedInfo [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
-
-  SetMatchedInfo(aList, aProvider, aPrefix);
+  MOZ_ASSERT(OnSocketThread());
+
+  nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
+  mainTarget->Dispatch(
+    NewRunnableMethod<const nsCString, const nsCString, const nsCString>
+      ("HttpChannelChild::SetMatchedInfo",
+       this, &HttpChannelChild::SetMatchedInfo,
+       aList, aProvider, aPrefix),
+    NS_DISPATCH_NORMAL);
 }
 
 void
 HttpChannelChild::ProcessDivertMessages()
 {
   LOG(("HttpChannelChild::ProcessDivertMessages [this=%p]\n", this));
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(OnSocketThread());
   MOZ_RELEASE_ASSERT(mDivertingToParent);
-  MOZ_RELEASE_ASSERT(mSuspendCount > 0);
 
   // DivertTo() has been called on parent, so we can now start sending queued
   // IPDL messages back to parent listener.
-  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(Resume()));
+  nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
+  MOZ_ASSERT(neckoTarget);
+  nsresult rv =
+    neckoTarget->Dispatch(
+      NewRunnableMethod(
+        "HttpChannelChild::Resume",
+        this, &HttpChannelChild::Resume),
+      NS_DISPATCH_NORMAL);
+
+  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
 }
 
 // Returns true if has actually completed the redirect and cleaned up the
 // channel, or false the interception logic kicked in and we need to asyncly
 // call FinishInterceptedRedirect and CleanupRedirectingChannel.
 // The argument is an optional OverrideRunnable that we pass to the redirected
 // channel.
 bool
@@ -1861,22 +1914,39 @@ HttpChannelChild::ConnectParent(uint32_t
   if (!gNeckoChild->
         SendPHttpChannelConstructor(this, browser,
                                     slc,
                                     connectArgs)) {
     return NS_ERROR_FAILURE;
   }
 
   {
+    MutexAutoLock lock(mBgChildMutex);
+
     MOZ_ASSERT(!mBgChild);
+    MOZ_ASSERT(!mBgInitFailCallback);
+
+    mBgInitFailCallback = NewRunnableMethod<nsresult>(
+        "HttpChannelChild::OnRedirectVerifyCallback",
+        this, &HttpChannelChild::OnRedirectVerifyCallback,
+        NS_ERROR_FAILURE);
 
     RefPtr<HttpBackgroundChannelChild> bgChild =
       new HttpBackgroundChannelChild();
 
-    nsresult rv = bgChild->Init(this);
+    MOZ_RELEASE_ASSERT(gSocketTransportService);
+
+    RefPtr<HttpChannelChild> self = this;
+    nsresult rv =
+      gSocketTransportService->Dispatch(
+        NewRunnableMethod<RefPtr<HttpChannelChild>>(
+          "HttpBackgroundChannelChild::Init",
+          bgChild, &HttpBackgroundChannelChild::Init, Move(self)),
+        NS_DISPATCH_NORMAL);
+
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     mBgChild = bgChild.forget();
   }
 
   return NS_OK;
@@ -2097,17 +2167,18 @@ HttpChannelChild::Cancel(nsresult status
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelChild::Suspend()
 {
   LOG(("HttpChannelChild::Suspend [this=%p, mSuspendCount=%" PRIu32 ", "
-       "mDivertingToParent=%d]\n", this, mSuspendCount+1, mDivertingToParent));
+       "mDivertingToParent=%d]\n", this, mSuspendCount + 1,
+       static_cast<bool>(mDivertingToParent)));
   NS_ENSURE_TRUE(RemoteChannelExists() || mInterceptListener,
                  NS_ERROR_NOT_AVAILABLE);
 
   // SendSuspend only once, when suspend goes from 0 to 1.
   // Don't SendSuspend at all if we're diverting callbacks to the parent;
   // suspend will be called at the correct time in the parent itself.
   if (!mSuspendCount++ && !mDivertingToParent) {
     if (RemoteChannelExists()) {
@@ -2122,17 +2193,18 @@ HttpChannelChild::Suspend()
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelChild::Resume()
 {
   LOG(("HttpChannelChild::Resume [this=%p, mSuspendCount=%" PRIu32 ", "
-       "mDivertingToParent=%d]\n", this, mSuspendCount-1, mDivertingToParent));
+       "mDivertingToParent=%d]\n", this, mSuspendCount - 1,
+       static_cast<bool>(mDivertingToParent)));
   NS_ENSURE_TRUE(RemoteChannelExists() || mInterceptListener,
                  NS_ERROR_NOT_AVAILABLE);
   NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
 
   nsresult rv = NS_OK;
 
   // SendResume only once, when suspend count drops to 0.
   // Don't SendResume at all if we're diverting callbacks to the parent (unless
@@ -2506,28 +2578,51 @@ HttpChannelChild::ContinueAsyncOpen()
                         "SerializedLoadContext should not be null");
   if (!gNeckoChild->SendPHttpChannelConstructor(this, browser,
                                                 slc,
                                                 openArgs)) {
     return NS_ERROR_FAILURE;
   }
 
   {
+    MutexAutoLock lock(mBgChildMutex);
+
+    MOZ_RELEASE_ASSERT(gSocketTransportService);
+
     // Service worker might use the same HttpChannelChild to do async open
     // twice. Need to disconnect with previous background channel before
-    // creating the new one.
+    // creating the new one, to prevent receiving further notification
+    // from it.
     if (mBgChild) {
       RefPtr<HttpBackgroundChannelChild> prevBgChild = mBgChild.forget();
-      prevBgChild->OnChannelClosed();
+      gSocketTransportService->Dispatch(
+        NewRunnableMethod(
+          "HttpBackgroundChannelChild::OnChannelClosed",
+          prevBgChild, &HttpBackgroundChannelChild::OnChannelClosed),
+        NS_DISPATCH_NORMAL);
     }
 
+    MOZ_ASSERT(!mBgInitFailCallback);
+
+    mBgInitFailCallback = NewRunnableMethod<nsresult>(
+        "HttpChannelChild::FailedAsyncOpen",
+        this, &HttpChannelChild::FailedAsyncOpen,
+        NS_ERROR_FAILURE);
+
     RefPtr<HttpBackgroundChannelChild> bgChild =
       new HttpBackgroundChannelChild();
 
-    rv = bgChild->Init(this);
+    RefPtr<HttpChannelChild> self = this;
+    nsresult rv =
+      gSocketTransportService->Dispatch(
+        NewRunnableMethod<RefPtr<HttpChannelChild>>(
+          "HttpBackgroundChannelChild::Init",
+          bgChild, &HttpBackgroundChannelChild::Init, self),
+        NS_DISPATCH_NORMAL);
+
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     mBgChild = bgChild.forget();
   }
 
   return NS_OK;
@@ -3110,25 +3205,30 @@ HttpChannelChild::DivertToParent(Channel
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelChild::UnknownDecoderInvolvedKeepData()
 {
   LOG(("HttpChannelChild::UnknownDecoderInvolvedKeepData [this=%p]",
        this));
+  MOZ_ASSERT(NS_IsMainThread());
+
   mUnknownDecoderInvolved = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelChild::UnknownDecoderInvolvedOnStartRequestCalled()
 {
   LOG(("HttpChannelChild::UnknownDecoderInvolvedOnStartRequestCalled "
-       "[this=%p, mDivertingToParent=%d]", this, mDivertingToParent));
+       "[this=%p, mDivertingToParent=%d]", this,
+       static_cast<bool>(mDivertingToParent)));
+  MOZ_ASSERT(NS_IsMainThread());
+
   mUnknownDecoderInvolved = false;
 
   nsresult rv = NS_OK;
 
   if (mDivertingToParent) {
     rv = mEventQ->PrependEvents(mUnknownDecoderEventQ);
   }
   mUnknownDecoderEventQ.Clear();
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -115,17 +115,17 @@ public:
 
   void FlushedForDiversion();
 
   void OnCopyComplete(nsresult aStatus) override;
 
   // Callback while background channel is ready.
   void OnBackgroundChildReady(HttpBackgroundChannelChild* aBgChild);
   // Callback while background channel is destroyed.
-  void OnBackgroundChildDestroyed();
+  void OnBackgroundChildDestroyed(HttpBackgroundChannelChild* aBgChild);
 
 protected:
   mozilla::ipc::IPCResult RecvOnStartRequest(const nsresult& channelStatus,
                                              const nsHttpResponseHead& responseHead,
                                              const bool& useResponseHead,
                                              const nsHttpHeaderArray& requestHeaders,
                                              const bool& isFromCache,
                                              const bool& cacheEntryAvailable,
@@ -289,23 +289,23 @@ private:
   Atomic<bool> mIPCOpen;
   bool mKeptAlive;            // IPC kept open, but only for security info
   RefPtr<ChannelEventQueue> mEventQ;
 
   // If nsUnknownDecoder is involved OnStartRequest call will be delayed and
   // this queue keeps OnDataAvailable data until OnStartRequest is finally
   // called.
   nsTArray<UniquePtr<ChannelEvent>> mUnknownDecoderEventQ;
-  bool mUnknownDecoderInvolved;
+  Atomic<bool, ReleaseAcquire> mUnknownDecoderInvolved;
 
   // Once set, OnData and possibly OnStop will be diverted to the parent.
-  bool mDivertingToParent;
+  Atomic<bool, ReleaseAcquire> mDivertingToParent;
   // Once set, no OnStart/OnData/OnStop callbacks should be received from the
   // parent channel, nor dequeued from the ChannelEventQueue.
-  bool mFlushedForDiversion;
+  Atomic<bool, ReleaseAcquire> mFlushedForDiversion;
   // Set if SendSuspend is called. Determines if SendResume is needed when
   // diverting callbacks to parent.
   bool mSuspendSent;
 
   // Set if a response was synthesized, indicating that any forthcoming redirects
   // should be intercepted.
   bool mSynthesizedResponse;
 
@@ -327,18 +327,25 @@ private:
   // Set if the corresponding parent channel should force an interception to occur
   // before the network transaction is initiated.
   bool mShouldParentIntercept;
 
   // Set if the corresponding parent channel should suspend after a response
   // is synthesized.
   bool mSuspendParentAfterSynthesizeResponse;
 
+  // Used to ensure atomicity of mBgChild and mBgInitFailCallback
+  Mutex mBgChildMutex;
+
+  // Associated HTTP background channel
   RefPtr<HttpBackgroundChannelChild> mBgChild;
 
+  // Error handling procedure if failed to establish PBackground IPC
+  nsCOMPtr<nsIRunnable> mBgInitFailCallback;
+
   // Remove the association with background channel after OnStopRequest
   // or AsyncAbort.
   void CleanupBackgroundChannel();
 
   // Needed to call AsyncOpen in FinishInterceptedRedirect
   nsCOMPtr<nsIStreamListener> mInterceptedRedirectListener;
   nsCOMPtr<nsISupports> mInterceptedRedirectContext;
   // Needed to call CleanupRedirectingChannel in FinishInterceptedRedirect
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -4407,17 +4407,17 @@ nsHttpChannel::OnCacheEntryAvailableInte
                                              bool aNew,
                                              nsIApplicationCache* aAppCache,
                                              nsresult status)
 {
     nsresult rv;
 
     if (mCanceled) {
         LOG(("channel was canceled [this=%p status=%" PRIx32 "]\n",
-             this, static_cast<uint32_t>(mStatus)));
+             this, static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
         return mStatus;
     }
 
     if (aAppCache) {
         if (mApplicationCache == aAppCache && !mCacheEntry) {
             rv = OnOfflineCacheEntryAvailable(entry, aNew, aAppCache, status);
         }
         else if (mApplicationCacheForWrite == aAppCache && aNew && !mOfflineCacheEntry) {
@@ -5042,17 +5042,17 @@ void
 nsHttpChannel::CloseCacheEntry(bool doomOnFailure)
 {
     mCacheInputStream.CloseAndRelease();
 
     if (!mCacheEntry)
         return;
 
     LOG(("nsHttpChannel::CloseCacheEntry [this=%p] mStatus=%" PRIx32 " mCacheEntryIsWriteOnly=%x",
-         this, static_cast<uint32_t>(mStatus), mCacheEntryIsWriteOnly));
+         this, static_cast<uint32_t>(static_cast<nsresult>(mStatus)), mCacheEntryIsWriteOnly));
 
     // If we have begun to create or replace a cache entry, and that cache
     // entry is not complete and not resumable, then it needs to be doomed.
     // Otherwise, CheckCache will make the mistake of thinking that the
     // partial cache entry is complete.
 
     bool doom = false;
     if (mInitedCacheEntry) {
@@ -6738,17 +6738,18 @@ nsHttpChannel::ClearClassFlags(uint32_t 
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
                                 nsIProxyInfo *pi, nsresult status)
 {
     LOG(("nsHttpChannel::OnProxyAvailable [this=%p pi=%p status=%" PRIx32
          " mStatus=%" PRIx32 "]\n",
-         this, pi, static_cast<uint32_t>(status), static_cast<uint32_t>(mStatus)));
+         this, pi, static_cast<uint32_t>(status),
+         static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
     mProxyRequest = nullptr;
 
     nsresult rv;
 
     // If status is a failure code, then it means that we failed to resolve
     // proxy info.  That is a non-fatal error assuming it wasn't because the
     // request was canceled.  We just failover to DIRECT when proxy resolution
     // fails (failure can mean that the PAC URL could not be loaded).
@@ -6962,21 +6963,23 @@ nsHttpChannel::OnStartRequest(nsIRequest
 {
     nsresult rv;
 
     AUTO_PROFILER_LABEL("nsHttpChannel::OnStartRequest", NETWORK);
 
     if (!(mCanceled || NS_FAILED(mStatus)) && !WRONG_RACING_RESPONSE_SOURCE(request)) {
         // capture the request's status, so our consumers will know ASAP of any
         // connection failures, etc - bug 93581
-        request->GetStatus(&mStatus);
+        nsresult status;
+        request->GetStatus(&status);
+        mStatus = status;
     }
 
     LOG(("nsHttpChannel::OnStartRequest [this=%p request=%p status=%" PRIx32 "]\n",
-         this, request, static_cast<uint32_t>(mStatus)));
+         this, request, static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
 
     if (mRaceCacheWithNetwork) {
         LOG(("  racingNetAndCache - mFirstResponseSource:%d fromCache:%d fromNet:%d\n",
              mFirstResponseSource, request == mCachePump, request == mTransactionPump));
         if (mFirstResponseSource == RESPONSE_PENDING) {
             // When the cache wins mFirstResponseSource is set to RESPONSE_FROM_CACHE
             // earlier in ReadFromCache, so this must be a response from the network.
             MOZ_ASSERT(request == mTransactionPump);