Bug 1140804, make sure to close WebSocket channel even in workers, r=khuey CLOSED TREE
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Thu, 19 Mar 2015 00:38:02 +0200
changeset 265698 7be28b1e114c9c2443cd0a1611a49d90374d28f2
parent 265697 4c2c50594967c482da30466957afb0e3959f191e
child 265699 828959dd93fbd97846d6ae4afb962d399413dabc
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs1140804
milestone39.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 1140804, make sure to close WebSocket channel even in workers, r=khuey CLOSED TREE
dom/base/WebSocket.cpp
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -120,17 +120,18 @@ public:
 
   nsresult ParseURL(const nsAString& aURL);
   nsresult InitializeConnection();
 
   // These methods when called can release the WebSocket object
   void FailConnection(uint16_t reasonCode,
                       const nsACString& aReasonString = EmptyCString());
   nsresult CloseConnection(uint16_t reasonCode,
-                           const nsACString& aReasonString = EmptyCString());
+                           const nsACString& aReasonString = EmptyCString(),
+                           bool aCanceling = false);
   nsresult Disconnect();
   void DisconnectInternal();
 
   nsresult ConsoleError();
   nsresult PrintErrorOnConsole(const char* aBundleURI,
                                const char16_t* aError,
                                const char16_t** aFormatStrings,
                                uint32_t aFormatStringsLen);
@@ -411,16 +412,38 @@ private:
   // A raw pointer because this runnable is sync.
   WebSocketImpl* mImpl;
 
   uint16_t mReasonCode;
   const nsACString& mReasonString;
   nsresult mRv;
 };
 
+class CancelWebSocketRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  CancelWebSocketRunnable(nsIWebSocketChannel* aChannel, uint16_t aReasonCode,
+                          const nsACString& aReasonString)
+    : mChannel(aChannel)
+    , mReasonCode(aReasonCode)
+    , mReasonString(aReasonString)
+  {}
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    mChannel->Close(mReasonCode, mReasonString);
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIWebSocketChannel> mChannel;
+  uint16_t mReasonCode;
+  nsCString mReasonString;
+};
+
 class MOZ_STACK_CLASS MaybeDisconnect
 {
 public:
   explicit MaybeDisconnect(WebSocketImpl* aImpl)
     : mImpl(aImpl)
   {
   }
 
@@ -441,19 +464,21 @@ public:
 private:
   WebSocketImpl* mImpl;
 };
 
 } // anonymous namespace
 
 nsresult
 WebSocketImpl::CloseConnection(uint16_t aReasonCode,
-                               const nsACString& aReasonString)
+                               const nsACString& aReasonString,
+                               bool aCanceling)
 {
   AssertIsOnTargetThread();
+  MOZ_ASSERT(!NS_IsMainThread() || !aCanceling);
 
   if (mDisconnectingOrDisconnected) {
     return NS_OK;
   }
 
   // If this method is called because the worker is going away, we will not
   // receive the OnStop() method and we have to disconnect the WebSocket and
   // release the WorkerFeature.
@@ -470,16 +495,22 @@ WebSocketImpl::CloseConnection(uint16_t 
     mWebSocket->SetReadyState(WebSocket::CLOSING);
 
     // The channel has to be closed on the main-thread.
 
     if (NS_IsMainThread()) {
       return mChannel->Close(aReasonCode, aReasonString);
     }
 
+    if (aCanceling) {
+      nsRefPtr<CancelWebSocketRunnable> runnable =
+        new CancelWebSocketRunnable(mChannel, aReasonCode, aReasonString);
+      return NS_DispatchToMainThread(runnable);
+    }
+
     nsRefPtr<CloseRunnable> runnable =
       new CloseRunnable(this, aReasonCode, aReasonString);
     runnable->Dispatch(mWorkerPrivate->GetJSContext());
     return runnable->ErrorCode();
   }
 
   // No channel, but not disconnected: canceled or failed early
   //
@@ -504,16 +535,24 @@ WebSocketImpl::CloseConnection(uint16_t 
   return NS_OK;
 }
 
 nsresult
 WebSocketImpl::ConsoleError()
 {
   AssertIsOnTargetThread();
 
+  {
+    MutexAutoLock lock(mMutex);
+    if (mWorkerShuttingDown) {
+      // Too late to report anything, bail out.
+      return NS_OK;
+    }
+  }
+
   NS_ConvertUTF8toUTF16 specUTF16(mURI);
   const char16_t* formatStrings[] = { specUTF16.get() };
 
   if (mWebSocket->ReadyState() < WebSocket::OPEN) {
     PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
                         MOZ_UTF16("connectionFailure"),
                         formatStrings, ArrayLength(formatStrings));
   } else {
@@ -1989,17 +2028,18 @@ public:
     MOZ_ASSERT(aStatus > workers::Running);
 
     if (aStatus >= Canceling) {
       {
         MutexAutoLock lock(mWebSocketImpl->mMutex);
         mWebSocketImpl->mWorkerShuttingDown = true;
       }
 
-      mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
+      mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY,
+                                      EmptyCString(), true);
     }
 
     return true;
   }
 
   bool Suspend(JSContext* aCx) override
   {
     {
@@ -2575,24 +2615,24 @@ public:
     , mWebSocketImpl(aImpl)
     , mEvent(aEvent)
   {
   }
 
   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
+    aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true);
 
     // No messages when disconnected.
     if (mWebSocketImpl->mDisconnectingOrDisconnected) {
       NS_WARNING("Dispatching a WebSocket event after the disconnection!");
       return true;
     }
 
-    aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true);
     return !NS_FAILED(mEvent->Run());
   }
 
   void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
   {
     aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false);
   }
 
@@ -2617,32 +2657,32 @@ private:
 NS_IMETHODIMP
 WebSocketImpl::Dispatch(nsIRunnable* aEvent, uint32_t aFlags)
 {
   // If the target is the main-thread we can just dispatch the runnable.
   if (mIsMainThread) {
     return NS_DispatchToMainThread(aEvent);
   }
 
-  // If the target is a worker, we have to use a custom WorkerRunnableDispatcher
-  // runnable.
-  nsRefPtr<WorkerRunnableDispatcher> event =
-    new WorkerRunnableDispatcher(this, mWorkerPrivate, aEvent);
-
   MutexAutoLock lock(mMutex);
   if (mWorkerShuttingDown) {
     return NS_OK;
   }
 
   MOZ_ASSERT(mWorkerPrivate);
 
 #ifdef DEBUG
   MOZ_ASSERT(HasFeatureRegistered());
 #endif
 
+  // If the target is a worker, we have to use a custom WorkerRunnableDispatcher
+  // runnable.
+  nsRefPtr<WorkerRunnableDispatcher> event =
+    new WorkerRunnableDispatcher(this, mWorkerPrivate, aEvent);
+
   if (!event->Dispatch(nullptr)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP