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 290591 c110812ef14b632e5ed5da9ca823d7492ffa38a4
parent 290590 7c4176c6d8801496127e4643324ef8cf555b38cb
child 290592 63d6812ce8ccc5968bb0a5359a5ac9052fc87e48
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdragana
bugs1256022
milestone48.0a1
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>