Bug 1205016 - double ClosingService thread stack size to avoid stack overflow. r=glandium, a=sledru
☠☠ backed out by c4ed84ecb004 ☠ ☠
authorLee Salzman <lsalzman@mozilla.com>
Wed, 16 Sep 2015 20:31:43 -0400
changeset 289306 52f2a71f9dd49bcffbdd802ab01de519893274fe
parent 289305 a064e4cbc639f50f09d55bd05f829b17c09afed6
child 289307 f188a8e5938111813e8b6da06435a235375b2b99
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglandium, sledru
bugs1205016
milestone42.0a2
Bug 1205016 - double ClosingService thread stack size to avoid stack overflow. r=glandium, a=sledru
netwerk/base/ClosingService.cpp
new file mode 100644
--- /dev/null
+++ b/netwerk/base/ClosingService.cpp
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClosingService.h"
+#include "nsIOService.h"
+
+class ClosingLayerSecret
+{
+public:
+  explicit ClosingLayerSecret(mozilla::net::ClosingService *aClosingService)
+    : mClosingService(aClosingService)
+  {
+  }
+
+  ~ClosingLayerSecret()
+  {
+    mClosingService = nullptr;
+  }
+
+  nsRefPtr<mozilla::net::ClosingService> mClosingService;
+};
+
+namespace mozilla {
+namespace net {
+
+static PRIOMethods sTcpUdpPRCloseLayerMethods;
+static PRIOMethods *sTcpUdpPRCloseLayerMethodsPtr = nullptr;
+static PRDescIdentity sTcpUdpPRCloseLayerId;
+
+static PRStatus
+TcpUdpPRCloseLayerClose(PRFileDesc *aFd)
+{
+  if (!aFd) {
+    return PR_FAILURE;
+  }
+
+  PRFileDesc* layer = PR_PopIOLayer(aFd, PR_TOP_IO_LAYER);
+  MOZ_RELEASE_ASSERT(layer &&
+                     layer->identity == sTcpUdpPRCloseLayerId,
+                     "Closing Layer not on top of stack");
+
+  ClosingLayerSecret *closingLayerSecret =
+    reinterpret_cast<ClosingLayerSecret *>(layer->secret);
+
+  PRStatus status = PR_SUCCESS;
+
+  if (aFd) {
+    // If this is called during shutdown do not call ..method->close(fd) and
+    // let it leak.
+    if (gIOService->IsShutdown()) {
+      // If the ClosingService layer is the first layer above PR_NSPR_IO_LAYER
+      // we are not going to leak anything, but the PR_Close will not be called.
+      PR_Free(aFd);
+    } else if (closingLayerSecret->mClosingService) {
+      closingLayerSecret->mClosingService->PostRequest(aFd);
+    } else {
+      // Socket is created before closing service has been started or there was
+      // a problem with starting it.
+      PR_Close(aFd);
+    }
+  }
+
+  layer->secret = nullptr;
+  layer->dtor(layer);
+  delete closingLayerSecret;
+  return status;
+}
+
+ClosingService* ClosingService::sInstance = nullptr;
+
+ClosingService::ClosingService()
+  : mShutdown(false)
+  , mMonitor("ClosingService.mMonitor")
+{
+  MOZ_ASSERT(!sInstance,
+             "multiple ClosingService instances!");
+}
+
+// static
+void
+ClosingService::Start()
+{
+  if (!sTcpUdpPRCloseLayerMethodsPtr) {
+    sTcpUdpPRCloseLayerId =  PR_GetUniqueIdentity("TCP and UDP PRClose layer");
+    PR_ASSERT(PR_INVALID_IO_LAYER != sTcpUdpPRCloseLayerId);
+
+    sTcpUdpPRCloseLayerMethods = *PR_GetDefaultIOMethods();
+    sTcpUdpPRCloseLayerMethods.close = TcpUdpPRCloseLayerClose;
+    sTcpUdpPRCloseLayerMethodsPtr = &sTcpUdpPRCloseLayerMethods;
+  }
+
+  if (!sInstance) {
+    ClosingService* service = new ClosingService();
+    if (NS_SUCCEEDED(service->StartInternal())) {
+      NS_ADDREF(service);
+      sInstance = service;
+    } else {
+      delete service;
+    }
+  }
+}
+
+nsresult
+ClosingService::StartInternal()
+{
+  mThread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
+                            PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+                            PR_JOINABLE_THREAD, 32 * 1024);
+  if (!mThread) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+// static
+nsresult
+ClosingService::AttachIOLayer(PRFileDesc *aFd)
+{
+  if (!sTcpUdpPRCloseLayerMethodsPtr) {
+    return NS_OK;
+  }
+
+  PRFileDesc * layer;
+  PRStatus     status;
+
+  layer = PR_CreateIOLayerStub(sTcpUdpPRCloseLayerId,
+                               sTcpUdpPRCloseLayerMethodsPtr);
+
+  if (!layer) {
+    return NS_OK;
+  }
+
+  ClosingLayerSecret *secret = new ClosingLayerSecret(sInstance);
+  layer->secret = reinterpret_cast<PRFilePrivate *>(secret);
+
+  status = PR_PushIOLayer(aFd, PR_NSPR_IO_LAYER, layer);
+
+  if (status == PR_FAILURE) {
+    delete secret;
+    PR_DELETE(layer);
+  }
+  return NS_OK;
+}
+
+void
+ClosingService::PostRequest(PRFileDesc *aFd)
+{
+  mozilla::MonitorAutoLock mon(mMonitor);
+
+  // Check if shutdown is called.
+  if (mShutdown) {
+    // Let the socket leak. We are in shutdown and some PRClose can take a long
+    // time. To prevent shutdown crash (bug 1152046) do not accept sockets any
+    // more.
+    // If the ClosingService layer is the first layer above PR_NSPR_IO_LAYER
+    // we are not going to leak anything, but PR_Close will not be called.
+    PR_Free(aFd);
+    return;
+  }
+
+  mQueue.AppendElement(aFd);
+  if (mQueue.Length() == 1) {
+    mon.Notify();
+  }
+}
+
+// static
+void
+ClosingService::Shutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (sInstance) {
+    sInstance->ShutdownInternal();
+    NS_RELEASE(sInstance);
+  }
+}
+
+void
+ClosingService::ShutdownInternal()
+{
+  {
+    mozilla::MonitorAutoLock mon(mMonitor);
+    mShutdown = true;
+    // If it is waiting on the empty queue, wake it up.
+    if (mQueue.Length() == 0) {
+      mon.Notify();
+    }
+  }
+
+  if (mThread) {
+    PR_JoinThread(mThread);
+    mThread = nullptr;
+  }
+}
+
+void
+ClosingService::ThreadFunc()
+{
+  for (;;) {
+    PRFileDesc *fd;
+    {
+      mozilla::MonitorAutoLock mon(mMonitor);
+      while (!mShutdown && (mQueue.Length() == 0)) {
+        mon.Wait();
+      }
+
+      if (mShutdown) {
+        // If we are in shutdown leak the rest of the sockets.
+        for (uint32_t i = 0; i < mQueue.Length(); i++) {
+          fd = mQueue[i];
+          // If the ClosingService layer is the first layer above
+          // PR_NSPR_IO_LAYER we are not going to leak anything, but PR_Close
+          // will not be called.
+          PR_Free(fd);
+        }
+        mQueue.Clear();
+        return;
+      }
+
+      fd = mQueue[0];
+      mQueue.RemoveElementAt(0);
+    }
+    // Leave lock before closing socket. It can block for a long time and in
+    // case we accidentally attach this layer twice this would cause deadlock.
+
+    bool tcp = (PR_GetDescType(PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER)) ==
+                PR_DESC_SOCKET_TCP);
+
+    PRIntervalTime closeStarted = PR_IntervalNow();
+    fd->methods->close(fd);
+
+    // Post telemetry.
+    if (tcp) {
+      SendPRCloseTelemetry(closeStarted,
+        Telemetry::PRCLOSE_TCP_BLOCKING_TIME_NORMAL,
+        Telemetry::PRCLOSE_TCP_BLOCKING_TIME_SHUTDOWN,
+        Telemetry::PRCLOSE_TCP_BLOCKING_TIME_CONNECTIVITY_CHANGE,
+        Telemetry::PRCLOSE_TCP_BLOCKING_TIME_LINK_CHANGE,
+        Telemetry::PRCLOSE_TCP_BLOCKING_TIME_OFFLINE);
+    } else {
+      SendPRCloseTelemetry(closeStarted,
+        Telemetry::PRCLOSE_UDP_BLOCKING_TIME_NORMAL,
+        Telemetry::PRCLOSE_UDP_BLOCKING_TIME_SHUTDOWN,
+        Telemetry::PRCLOSE_UDP_BLOCKING_TIME_CONNECTIVITY_CHANGE,
+        Telemetry::PRCLOSE_UDP_BLOCKING_TIME_LINK_CHANGE,
+        Telemetry::PRCLOSE_UDP_BLOCKING_TIME_OFFLINE);
+    }
+  }
+}
+
+void
+ClosingService::SendPRCloseTelemetry(PRIntervalTime aStart,
+                                     mozilla::Telemetry::ID aIDNormal,
+                                     mozilla::Telemetry::ID aIDShutdown,
+                                     mozilla::Telemetry::ID aIDConnectivityChange,
+                                     mozilla::Telemetry::ID aIDLinkChange,
+                                     mozilla::Telemetry::ID aIDOffline)
+{
+    PRIntervalTime now = PR_IntervalNow();
+    if (gIOService->IsShutdown()) {
+        Telemetry::Accumulate(aIDShutdown,
+                              PR_IntervalToMilliseconds(now - aStart));
+
+    } else if (PR_IntervalToSeconds(now - gIOService->LastConnectivityChange())
+               < 60) {
+        Telemetry::Accumulate(aIDConnectivityChange,
+                              PR_IntervalToMilliseconds(now - aStart));
+    } else if (PR_IntervalToSeconds(now - gIOService->LastNetworkLinkChange())
+               < 60) {
+        Telemetry::Accumulate(aIDLinkChange,
+                              PR_IntervalToMilliseconds(now - aStart));
+
+    } else if (PR_IntervalToSeconds(now - gIOService->LastOfflineStateChange())
+               < 60) {
+        Telemetry::Accumulate(aIDOffline,
+                              PR_IntervalToMilliseconds(now - aStart));
+    } else {
+        Telemetry::Accumulate(aIDNormal,
+                              PR_IntervalToMilliseconds(now - aStart));
+    }
+}
+
+} //namwspacw mozilla
+} //namespace net