Bug 1260218 - SocketTransportService socket expansion r=dragana
authorPatrick McManus <mcmanus@ducksong.com>
Wed, 23 Mar 2016 20:44:28 -0400
changeset 290956 7ce3240aca9f99201590c4f4289cf726dc36f0a6
parent 290955 c658510738da8197c3d0d9efcf23622bf8e715a0
child 290957 ef6a15d8cf47fdc7820dde93c9bdbc8446567065
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
bugs1260218
milestone48.0a1
Bug 1260218 - SocketTransportService socket expansion r=dragana
modules/libpref/init/all.js
netwerk/base/nsSocketTransportService2.cpp
netwerk/base/nsSocketTransportService2.h
toolkit/components/telemetry/Histograms.json
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1318,20 +1318,23 @@ pref("network.http.default-socket-type",
 // We set the timeout a little shorter to keep a reserve for cases when
 // the packet is lost or delayed on the route.
 pref("network.http.keep-alive.timeout", 115);
 
 // Timeout connections if an initial response is not received after 5 mins.
 pref("network.http.response.timeout", 300);
 
 // Limit the absolute number of http connections.
-// Note: the socket transport service will clamp the number below 256 if the OS
-// cannot allocate that many FDs, and it also always tries to reserve up to 250
-// file descriptors for things other than sockets.
+// Note: the socket transport service will clamp the number below this if the OS
+// cannot allocate that many FDs
+#ifdef ANDROID
 pref("network.http.max-connections", 256);
+#else
+pref("network.http.max-connections", 900);
+#endif
 
 // If NOT connecting via a proxy, then
 // a new connection will only be attempted if the number of active persistent
 // connections to the server is less then max-persistent-connections-per-server.
 pref("network.http.max-persistent-connections-per-server", 6);
 
 // If connecting via a proxy, then a
 // new connection will only be attempted if the number of active persistent
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -36,18 +36,18 @@ LazyLogModule gUDPSocketLog("UDPSocket")
 nsSocketTransportService *gSocketTransportService = nullptr;
 Atomic<PRThread*, Relaxed> gSocketThread;
 
 #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
 #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled"
 #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time"
 #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval"
 #define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
-#define SOCKET_LIMIT_TARGET 550U
-#define SOCKET_LIMIT_MIN     50U
+#define SOCKET_LIMIT_TARGET 1000U
+#define SOCKET_LIMIT_MIN      50U
 #define BLIP_INTERVAL_PREF "network.activity.blipIntervalMilliseconds"
 #define MAX_TIME_BETWEEN_TWO_POLLS "network.sts.max_time_for_events_between_two_polls"
 #define TELEMETRY_PREF "toolkit.telemetry.enabled"
 #define MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN "network.sts.max_time_for_pr_close_during_shutdown"
 
 uint32_t nsSocketTransportService::gMaxCount;
 PRCallOnceType nsSocketTransportService::gMaxCountInitOnce;
 
@@ -221,16 +221,45 @@ nsSocketTransportService::AttachSocket(P
     sock.mElapsedTime = 0;
 
     nsresult rv = AddToIdleList(&sock);
     if (NS_SUCCEEDED(rv))
         NS_ADDREF(handler);
     return rv;
 }
 
+// the number of sockets that can be attached at any given time is
+// limited.  this is done because some operating systems (e.g., Win9x)
+// limit the number of sockets that can be created by an application.
+// AttachSocket will fail if the limit is exceeded.  consumers should
+// call CanAttachSocket and check the result before creating a socket.
+
+bool
+nsSocketTransportService::CanAttachSocket()
+{
+    static bool reported900FDLimit = false;
+    static bool reported256FDLimit = false;
+
+    uint32_t total = mActiveCount + mIdleCount;
+    bool rv = total < gMaxCount;
+
+    if (mTelemetryEnabledPref) {
+        if (((total >= 900) || !rv) && !reported900FDLimit) {
+            reported900FDLimit = true;
+            Telemetry::Accumulate(Telemetry::NETWORK_SESSION_AT_900FD, true);
+        }
+        if ((total >= 256) && !reported256FDLimit) {
+            reported256FDLimit = true;
+            Telemetry::Accumulate(Telemetry::NETWORK_SESSION_AT_256FD, true);
+        }
+    }
+
+    return rv;
+}
+
 nsresult
 nsSocketTransportService::DetachSocket(SocketContext *listHead, SocketContext *sock)
 {
     SOCKET_LOG(("nsSocketTransportService::DetachSocket [handler=%p]\n", sock->mHandler));
     MOZ_ASSERT((listHead == mActiveList) || (listHead == mIdleList),
                "DetachSocket invalid head");
 
     // inform the handler that this socket is going away
@@ -373,37 +402,41 @@ nsSocketTransportService::MoveToPollList
     else
         RemoveFromIdleList(sock);
 }
 
 bool
 nsSocketTransportService::GrowActiveList()
 {
     int32_t toAdd = gMaxCount - mActiveListSize;
-    if (toAdd > 100)
+    if (toAdd > 100) {
         toAdd = 100;
-    if (toAdd < 1)
+    } else if (toAdd < 1) {
+        MOZ_ASSERT(false, "CanAttachSocket() should prevent this");
         return false;
-    
+    }
+
     mActiveListSize += toAdd;
     mActiveList = (SocketContext *)
         moz_xrealloc(mActiveList, sizeof(SocketContext) * mActiveListSize);
     mPollList = (PRPollDesc *)
         moz_xrealloc(mPollList, sizeof(PRPollDesc) * (mActiveListSize + 1));
     return true;
 }
 
 bool
 nsSocketTransportService::GrowIdleList()
 {
     int32_t toAdd = gMaxCount - mIdleListSize;
-    if (toAdd > 100)
+    if (toAdd > 100) {
         toAdd = 100;
-    if (toAdd < 1)
+    } else if (toAdd < 1) {
+        MOZ_ASSERT(false, "CanAttachSocket() should prevent this");
         return false;
+    }
 
     mIdleListSize += toAdd;
     mIdleList = (SocketContext *)
         moz_xrealloc(mIdleList, sizeof(SocketContext) * mIdleListSize);
     return true;
 }
 
 PRIntervalTime
@@ -789,17 +822,17 @@ nsSocketTransportService::Run()
     PR_SetCurrentThreadName("Socket Thread");
 
 #ifdef MOZ_NUWA_PROCESS
     if (IsNuwaProcess()) {
         NuwaMarkCurrentThread(nullptr, nullptr);
     }
 #endif
 
-    SOCKET_LOG(("STS thread init\n"));
+    SOCKET_LOG(("STS thread init %d sockets\n", gMaxCount));
 
     psm::InitializeSSLServerCertVerificationThreads();
 
     gSocketThread = PR_GetCurrentThread();
 
     mPollableEvent.reset(new PollableEvent());
     //
     // NOTE: per bug 190000, this failure could be caused by Zone-Alarm
@@ -1402,58 +1435,61 @@ nsSocketTransportService::ProbeMaxCount(
         }
     }
 
     // Free
     for (uint32_t index = 0 ; index < numAllocated; ++index)
         if (pfd[index].fd)
             PR_Close(pfd[index].fd);
 
+    Telemetry::Accumulate(Telemetry::NETWORK_PROBE_MAXCOUNT, gMaxCount);
     SOCKET_LOG(("Socket Limit Test max was confirmed at %d\n", gMaxCount));
 }
 #endif // windows
 
 PRStatus
 nsSocketTransportService::DiscoverMaxCount()
 {
     gMaxCount = SOCKET_LIMIT_MIN;
 
 #if defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
     // On unix and os x network sockets and file
     // descriptors are the same. OS X comes defaulted at 256,
     // most linux at 1000. We can reliably use [sg]rlimit to
-    // query that and raise it. We will try to raise it 250 past
-    // our target number of SOCKET_LIMIT_TARGET so that some descriptors
-    // are still available for other things.
+    // query that and raise it if needed.
 
     struct rlimit rlimitData;
-    if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1)
+    if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1) // rlimit broken - use min
         return PR_SUCCESS;
-    if (rlimitData.rlim_cur >=  SOCKET_LIMIT_TARGET + 250) {
+
+    if (rlimitData.rlim_cur >= SOCKET_LIMIT_TARGET) { // larger than target!
         gMaxCount = SOCKET_LIMIT_TARGET;
         return PR_SUCCESS;
     }
 
     int32_t maxallowed = rlimitData.rlim_max;
-    if (maxallowed == -1) {                       /* no limit */
-        maxallowed = SOCKET_LIMIT_TARGET + 250;
-    } else if ((uint32_t)maxallowed < SOCKET_LIMIT_MIN + 250) {
-        return PR_SUCCESS;
-    } else if ((uint32_t)maxallowed > SOCKET_LIMIT_TARGET + 250) {
-        maxallowed = SOCKET_LIMIT_TARGET + 250;
+    if ((uint32_t)maxallowed <= SOCKET_LIMIT_MIN) {
+        return PR_SUCCESS; // so small treat as if rlimit is broken
+    }
+
+    if ((maxallowed == -1) || // no hard cap - ok to set target
+        ((uint32_t)maxallowed >= SOCKET_LIMIT_TARGET)) {
+        maxallowed = SOCKET_LIMIT_TARGET;
     }
 
     rlimitData.rlim_cur = maxallowed;
     setrlimit(RLIMIT_NOFILE, &rlimitData);
-    if (getrlimit(RLIMIT_NOFILE, &rlimitData) != -1)
-        if (rlimitData.rlim_cur > SOCKET_LIMIT_MIN + 250)
-            gMaxCount = rlimitData.rlim_cur - 250;
+    if ((getrlimit(RLIMIT_NOFILE, &rlimitData) != -1) &&
+        (rlimitData.rlim_cur > SOCKET_LIMIT_MIN)) {
+        gMaxCount = rlimitData.rlim_cur;
+    }
 
 #elif defined(XP_WIN) && !defined(WIN_CE)
     // >= XP is confirmed to have at least 1000
+    PR_STATIC_ASSERT(SOCKET_LIMIT_TARGET <= 1000);
     gMaxCount = SOCKET_LIMIT_TARGET;
 #else
     // other platforms are harder to test - so leave at safe legacy value
 #endif
 
     return PR_SUCCESS;
 }
 
--- a/netwerk/base/nsSocketTransportService2.h
+++ b/netwerk/base/nsSocketTransportService2.h
@@ -89,26 +89,17 @@ public:
     nsSocketTransportService();
 
     // Max Socket count may need to get initialized/used by nsHttpHandler
     // before this class is initialized.
     static uint32_t gMaxCount;
     static PRCallOnceType gMaxCountInitOnce;
     static PRStatus DiscoverMaxCount();
 
-    //
-    // the number of sockets that can be attached at any given time is
-    // limited.  this is done because some operating systems (e.g., Win9x)
-    // limit the number of sockets that can be created by an application.
-    // AttachSocket will fail if the limit is exceeded.  consumers should
-    // call CanAttachSocket and check the result before creating a socket.
-    //
-    bool CanAttachSocket() {
-        return mActiveCount + mIdleCount < gMaxCount;
-    }
+    bool CanAttachSocket();
 
     // Called by the networking dashboard on the socket thread only
     // Fills the passed array with socket information
     void GetSocketConnections(nsTArray<mozilla::net::SocketInfo> *);
     uint64_t GetSentBytes() { return mSentBytesCount; }
     uint64_t GetReceivedBytes() { return mReceivedBytesCount; }
 
     // Returns true if keepalives are enabled in prefs.
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -2761,16 +2761,40 @@
     "kind": "boolean",
     "description": "The URL path contains !//"
   },
   "NETWORK_AUTODIAL": {
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "session used autodialer"
   },
+  "NETWORK_SESSION_AT_256FD": {
+    "expires_in_version": "49",
+    "kind": "boolean",
+    "description": "session exceeded old 256 limit",
+    "bug_numbers": [1260218],
+    "alert_emails": ["necko@mozilla.com"]
+  },
+  "NETWORK_SESSION_AT_900FD": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "description": "session reached 900 fd limit sockets",
+    "bug_numbers": [1260218],
+    "alert_emails": ["necko@mozilla.com"]
+  },
+  "NETWORK_PROBE_MAXCOUNT": {
+    "expires_in_version": "never",
+    "kind": "linear",
+    "low": 50,
+    "high": 1000,
+    "n_buckets": 10,
+    "description": "Result of nsSocketTransportService::ProbeMaxCount()",
+    "bug_numbers": [1260218],
+    "alert_emails": ["necko@mozilla.com"]
+  },
   "FIND_PLUGINS": {
     "alert_emails": ["perf-telemetry-alerts@mozilla.com"],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 3000,
     "n_buckets": 10,
     "description": "Time spent scanning filesystem for plugins (ms)"
   },