Bug 1256022 - dom/network slow GC on mochitest fix r=dragana
authorPatrick McManus <mcmanus@ducksong.com>
Mon, 21 Mar 2016 13:10:30 -0400
changeset 290058 f57f85dc94e4fad0f348d35c176a5b63b972bd65
parent 290057 e2914c063e387723b45f31b5b81bb8c6ba49b1b1
child 290059 34046c232ee1f7324ac586cf0b72374764d8f513
push id30114
push usercbook@mozilla.com
push dateThu, 24 Mar 2016 15:15:54 +0000
treeherdermozilla-central@24c5fbde4488 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdragana
bugs1256022
milestone48.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 1256022 - dom/network slow GC on mochitest fix r=dragana
dom/network/TCPSocket.cpp
dom/network/TCPSocket.h
dom/network/tests/test_tcpsocket_enabled_with_perm.html
dom/network/tests/test_tcpsocket_legacy.html
--- a/dom/network/TCPSocket.cpp
+++ b/dom/network/TCPSocket.cpp
@@ -157,16 +157,17 @@ TCPSocket::TCPSocket(nsIGlobalObject* aG
   , mSsl(aSsl)
   , mAsyncCopierActive(false)
   , mWaitingForDrain(false)
   , mInnerWindowID(0)
   , mBufferedAmount(0)
   , mSuspendCount(0)
   , mTrackingNumber(0)
   , mWaitingForStartTLS(false)
+  , mObserversActive(false)
 #ifdef MOZ_WIDGET_GONK
   , mTxBytes(0)
   , mRxBytes(0)
   , mAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID)
   , mInIsolatedMozBrowser(false)
 #endif
 {
   if (aGlobal) {
@@ -174,16 +175,23 @@ TCPSocket::TCPSocket(nsIGlobalObject* aG
     if (window) {
       mInnerWindowID = window->WindowID();
     }
   }
 }
 
 TCPSocket::~TCPSocket()
 {
+  if (mObserversActive) {
+    nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
+    if (obs) {
+      obs->RemoveObserver(this, "inner-window-destroyed");
+      obs->RemoveObserver(this, "profile-change-net-teardown");
+    }
+  }
 }
 
 nsresult
 TCPSocket::CreateStream()
 {
   nsresult rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(mSocketInputStream));
   NS_ENSURE_SUCCESS(rv, rv);
   rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, getter_AddRefs(mSocketOutputStream));
@@ -253,17 +261,19 @@ TCPSocket::InitWithUnconnectedTransport(
   return NS_OK;
 }
 
 nsresult
 TCPSocket::Init()
 {
   nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
   if (obs) {
-    obs->AddObserver(this, "inner-window-destroyed", true);
+    mObserversActive = true;
+    obs->AddObserver(this, "inner-window-destroyed", true); // weak reference
+    obs->AddObserver(this, "profile-change-net-teardown", true); // weak ref
   }
 
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     mReadyState = TCPReadyState::Connecting;
     mSocketBridgeChild = new TCPSocketChild(mHost, mPort);
     mSocketBridgeChild->SendOpen(this, mSsl, mUseArrayBuffers);
     return NS_OK;
   }
@@ -371,16 +381,17 @@ CopierCallbacks::OnStartRequest(nsIReque
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 CopierCallbacks::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
 {
   mOwner->NotifyCopyComplete(aStatus);
+  mOwner = nullptr;
   return NS_OK;
 }
 } // unnamed namespace
 
 nsresult
 TCPSocket::EnsureCopying()
 {
   if (mAsyncCopierActive) {
@@ -437,17 +448,20 @@ TCPSocket::NotifyCopyComplete(nsresult a
   // If we have a connected child, we let the child decide whether
   // ondrain should be dispatched.
   if (mWaitingForDrain && !mSocketBridgeParent) {
     mWaitingForDrain = false;
     FireEvent(NS_LITERAL_STRING("drain"));
   }
 
   if (mReadyState == TCPReadyState::Closing) {
-    mSocketOutputStream->Close();
+    if (mSocketOutputStream) {
+      mSocketOutputStream->Close();
+      mSocketOutputStream = nullptr;
+    }
     mReadyState = TCPReadyState::Closed;
     FireEvent(NS_LITERAL_STRING("close"));
   }
 }
 
 void
 TCPSocket::ActivateTLS()
 {
@@ -629,16 +643,19 @@ TCPSocket::MaybeReportErrorAndCloseIfOpe
   SaveNetworkStats(true);
 #endif
 
   // If we're closed, we've already reported the error or just don't need to
   // report the error.
   if (mReadyState == TCPReadyState::Closed) {
     return NS_OK;
   }
+
+  // go through ::Closing state and then mark ::Closed
+  Close();
   mReadyState = TCPReadyState::Closed;
 
   if (NS_FAILED(status)) {
     // Convert the status code to an appropriate error message.
 
     nsString errorType, errName;
 
     // security module? (and this is an error)
@@ -753,21 +770,29 @@ TCPSocket::Close()
   mReadyState = TCPReadyState::Closing;
 
   if (mSocketBridgeChild) {
     mSocketBridgeChild->SendClose();
     return;
   }
 
   uint32_t count = 0;
-  mMultiplexStream->GetCount(&count);
+  if (mMultiplexStream) {
+    mMultiplexStream->GetCount(&count);
+  }
   if (!count) {
-    mSocketOutputStream->Close();
+    if (mSocketOutputStream) {
+      mSocketOutputStream->Close();
+      mSocketOutputStream = nullptr;
+    }
   }
-  mSocketInputStream->Close();
+  if (mSocketInputStream) {
+    mSocketInputStream->Close();
+    mSocketInputStream = nullptr;
+  }
 }
 
 void
 TCPSocket::SendWithTrackingNumber(const nsACString& aData,
                                   const uint32_t& aTrackingNumber,
                                   mozilla::ErrorResult& aRv)
 {
   MOZ_ASSERT(mSocketBridgeParent);
@@ -950,16 +975,19 @@ TCPSocket::Constructor(const GlobalObjec
   }
 
   return socket.forget();
 }
 
 nsresult
 TCPSocket::CreateInputStreamPump()
 {
+  if (!mSocketInputStream) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
   nsresult rv;
   mInputStreamPump = do_CreateInstance("@mozilla.org/network/input-stream-pump;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mInputStreamPump->Init(mSocketInputStream, -1, -1, 0, 0, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint64_t suspendCount = mSuspendCount;
@@ -1171,23 +1199,20 @@ TCPSocket::Observe(nsISupports* aSubject
     NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
     uint64_t innerID;
     nsresult rv = wrapper->GetData(&innerID);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (innerID == mInnerWindowID) {
-      nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
-      if (obs) {
-        obs->RemoveObserver(this, "inner-window-destroyed");
-      }
-
       Close();
     }
+  } else if (!strcmp(aTopic, "profile-change-net-teardown")) {
+    Close();
   }
 
   return NS_OK;
 }
 
 /* static */
 bool
 TCPSocket::ShouldTCPSocketExist(JSContext* aCx, JSObject* aGlobal)
--- a/dom/network/TCPSocket.h
+++ b/dom/network/TCPSocket.h
@@ -234,16 +234,18 @@ private:
   uint32_t mTrackingNumber;
 
   // True if this socket has been upgraded to secure after the initial connection,
   // but the actual upgrade is waiting for an in-progress copy operation to complete.
   bool mWaitingForStartTLS;
   // The buffered data awaiting the TLS upgrade to finish.
   nsTArray<nsCOMPtr<nsIInputStream>> mPendingDataAfterStartTLS;
 
+  bool mObserversActive;
+
 #ifdef MOZ_WIDGET_GONK
   // Number of bytes sent.
   uint32_t mTxBytes;
   // Number of bytes received.
   uint32_t mRxBytes;
   // The app that owns this socket.
   uint32_t mAppId;
   // Was this socket created inside of an isolated browser frame?
--- a/dom/network/tests/test_tcpsocket_enabled_with_perm.html
+++ b/dom/network/tests/test_tcpsocket_enabled_with_perm.html
@@ -21,14 +21,17 @@ SpecialPowers.pushPrefEnv({"set": [['dom
   SpecialPowers.pushPermissions([{type: "tcp-socket", allow: true, context: document}], runTest);
 });
 
 function runTest() {
   ok('TCPSocket' in this, "TCPSocket should be accessible if dom.mozTCPSocket.enabled is true");
 
   ok(new TCPSocket('localhost', 80), "TCPSocket constructor should work for content that has the tcp-socket permission");
   ok(navigator.mozTCPSocket.open('localhost', 80), "navigator.mozTCPSocket.open should work for content that has the tcp-socket permission");
+  // This just helps the test harness clean up quickly
+  SpecialPowers.forceCC();
+  SpecialPowers.forceGC();
   SimpleTest.finish();
 }
 </script>
 </pre>
 </body>
 </html>
--- a/dom/network/tests/test_tcpsocket_legacy.html
+++ b/dom/network/tests/test_tcpsocket_legacy.html
@@ -35,22 +35,31 @@ Test of legacy navigator interface for o
     // test was using.
     var serverPort = 8085;
 
     var listeningServer = navigator.mozTCPSocket.listen(serverPort,
                                                         { binaryType: 'arraybuffer' },
                                                         -1);
     listeningServer.onconnect = function(ev) {
       ok(true, "got server connect");
+      listeningServer.close();
+      listeningServer = null;
       ev.socket.close()
     }
 
     var clientSocket = navigator.mozTCPSocket.open('127.0.0.1', serverPort,
                                                    { binaryType: 'arraybuffer' });
     clientSocket.onopen = function() { ok(true, "got client open"); }
     clientSocket.onclose = function() {
       ok(true, "got client close");
-      SimpleTest.finish();
+      clientSocket.close();
+      clientSocket = null;
+      setTimeout(function() {
+        // This just helps the test harness clean up quickly
+        SpecialPowers.forceCC();
+        SpecialPowers.forceGC();
+        SimpleTest.finish();
+      }, 0);
     }
   }
 </script>
 </body>
 </html>