bug 607741 - Raise max sockets open from 50 to up to 550. r=jduell
☠☠ backed out by 171d203d16b1 ☠ ☠
authorPatrick McManus <mcmanus@ducksong.com>
Mon, 11 Apr 2011 22:37:59 -0700
changeset 67964 e6d044d30abff340fb0bd4f8b929357d7464b9de
parent 67963 a174b86200d6183b8dd10d700e36b0f9e979beb0
child 67965 e84a9cf9fe6cdd30b2de099fe6d183d18f564ae4
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjduell
bugs607741
milestone2.2a1pre
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 607741 - Raise max sockets open from 50 to up to 550. r=jduell
netwerk/base/src/nsSocketTransportService2.cpp
netwerk/base/src/nsSocketTransportService2.h
netwerk/protocol/http/nsHttpHandler.cpp
--- a/netwerk/base/src/nsSocketTransportService2.cpp
+++ b/netwerk/base/src/nsSocketTransportService2.cpp
@@ -59,49 +59,64 @@ using namespace mozilla;
 #if defined(PR_LOGGING)
 PRLogModuleInfo *gSocketTransportLog = nsnull;
 #endif
 
 nsSocketTransportService *gSocketTransportService = nsnull;
 PRThread                 *gSocketThread           = nsnull;
 
 #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
+#define SOCKET_LIMIT_TARGET 550U
+#define SOCKET_LIMIT_MIN     50U
 
 //-----------------------------------------------------------------------------
 // ctor/dtor (called on the main/UI thread by the service manager)
 
 nsSocketTransportService::nsSocketTransportService()
     : mThread(nsnull)
     , mThreadEvent(nsnull)
     , mAutodialEnabled(PR_FALSE)
     , mLock("nsSocketTransportService::mLock")
     , mInitialized(PR_FALSE)
     , mShuttingDown(PR_FALSE)
+    , mActiveListSize(SOCKET_LIMIT_MIN)
+    , mIdleListSize(SOCKET_LIMIT_MIN)
     , mActiveCount(0)
     , mIdleCount(0)
     , mSendBufferSize(0)
 {
 #if defined(PR_LOGGING)
     gSocketTransportLog = PR_NewLogModule("nsSocketTransport");
 #endif
 
     NS_ASSERTION(NS_IsMainThread(), "wrong thread");
 
+    PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
+    mActiveList = (SocketContext *)
+        moz_xmalloc(sizeof(SocketContext) * mActiveListSize);
+    mIdleList = (SocketContext *)
+        moz_xmalloc(sizeof(SocketContext) * mIdleListSize);
+    mPollList = (PRPollDesc *)
+        moz_xmalloc(sizeof(PRPollDesc) * (mActiveListSize + 1));
+
     NS_ASSERTION(!gSocketTransportService, "must not instantiate twice");
     gSocketTransportService = this;
 }
 
 nsSocketTransportService::~nsSocketTransportService()
 {
     NS_ASSERTION(NS_IsMainThread(), "wrong thread");
     NS_ASSERTION(!mInitialized, "not shutdown properly");
     
     if (mThreadEvent)
         PR_DestroyPollableEvent(mThreadEvent);
 
+    moz_free(mActiveList);
+    moz_free(mIdleList);
+    moz_free(mPollList);
     gSocketTransportService = nsnull;
 }
 
 //-----------------------------------------------------------------------------
 // event queue (any thread)
 
 already_AddRefed<nsIThread>
 nsSocketTransportService::GetThreadSafely()
@@ -172,30 +187,30 @@ nsSocketTransportService::AttachSocket(P
 
     nsresult rv = AddToIdleList(&sock);
     if (NS_SUCCEEDED(rv))
         NS_ADDREF(handler);
     return rv;
 }
 
 nsresult
-nsSocketTransportService::DetachSocket(SocketContext *sock)
+nsSocketTransportService::DetachSocket(SocketContext *listHead, SocketContext *sock)
 {
     SOCKET_LOG(("nsSocketTransportService::DetachSocket [handler=%x]\n", sock->mHandler));
+    NS_ABORT_IF_FALSE((listHead == mActiveList) || (listHead == mIdleList),
+                      "DetachSocket invalid head");
 
     // inform the handler that this socket is going away
     sock->mHandler->OnSocketDetached(sock->mFD);
 
     // cleanup
     sock->mFD = nsnull;
     NS_RELEASE(sock->mHandler);
 
-    // find out what list this is on.
-    PRUint32 index = sock - mActiveList;
-    if (index < NS_SOCKET_MAX_COUNT)
+    if (listHead == mActiveList)
         RemoveFromPollList(sock);
     else
         RemoveFromIdleList(sock);
 
     // NOTE: sock is now an invalid pointer
     
     //
     // notify the first element on the pending socket queue...
@@ -206,23 +221,28 @@ nsSocketTransportService::DetachSocket(S
         return Dispatch(event, NS_DISPATCH_NORMAL);
     }
     return NS_OK;
 }
 
 nsresult
 nsSocketTransportService::AddToPollList(SocketContext *sock)
 {
-    SOCKET_LOG(("nsSocketTransportService::AddToPollList [handler=%x]\n", sock->mHandler));
+    NS_ABORT_IF_FALSE(!(((PRUint32)(sock - mActiveList)) < mActiveListSize),
+                      "AddToPollList Socket Already Active");
 
-    if (mActiveCount == NS_SOCKET_MAX_COUNT) {
-        NS_ERROR("too many active sockets");
-        return NS_ERROR_UNEXPECTED;
+    SOCKET_LOG(("nsSocketTransportService::AddToPollList [handler=%x]\n", sock->mHandler));
+    if (mActiveCount == mActiveListSize) {
+        SOCKET_LOG(("  Active List size of %d met\n", mActiveCount));
+        if (!GrowActiveList()) {
+            NS_ERROR("too many active sockets");
+            return NS_ERROR_OUT_OF_MEMORY;
+        }
     }
-
+    
     mActiveList[mActiveCount] = *sock;
     mActiveCount++;
 
     mPollList[mActiveCount].fd = sock->mFD;
     mPollList[mActiveCount].in_flags = sock->mHandler->mPollFlags;
     mPollList[mActiveCount].out_flags = 0;
 
     SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
@@ -230,81 +250,118 @@ nsSocketTransportService::AddToPollList(
 }
 
 void
 nsSocketTransportService::RemoveFromPollList(SocketContext *sock)
 {
     SOCKET_LOG(("nsSocketTransportService::RemoveFromPollList [handler=%x]\n", sock->mHandler));
 
     PRUint32 index = sock - mActiveList;
-    NS_ASSERTION(index < NS_SOCKET_MAX_COUNT, "invalid index");
+    NS_ABORT_IF_FALSE(index < mActiveListSize, "invalid index");
 
     SOCKET_LOG(("  index=%u mActiveCount=%u\n", index, mActiveCount));
 
     if (index != mActiveCount-1) {
         mActiveList[index] = mActiveList[mActiveCount-1];
         mPollList[index+1] = mPollList[mActiveCount];
     }
     mActiveCount--;
 
     SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
 }
 
 nsresult
 nsSocketTransportService::AddToIdleList(SocketContext *sock)
 {
-    SOCKET_LOG(("nsSocketTransportService::AddToIdleList [handler=%x]\n", sock->mHandler));
+    NS_ABORT_IF_FALSE(!(((PRUint32)(sock - mIdleList)) < mIdleListSize),
+                      "AddToIdlelList Socket Already Idle");
 
-    if (mIdleCount == NS_SOCKET_MAX_COUNT) {
-        NS_ERROR("too many idle sockets");
-        return NS_ERROR_UNEXPECTED;
+    SOCKET_LOG(("nsSocketTransportService::AddToIdleList [handler=%x]\n", sock->mHandler));
+    if (mIdleCount == mIdleListSize) {
+        SOCKET_LOG(("  Idle List size of %d met\n", mIdleCount));
+        if (!GrowIdleList()) {
+            NS_ERROR("too many idle sockets");
+            return NS_ERROR_OUT_OF_MEMORY;
+        }
     }
 
     mIdleList[mIdleCount] = *sock;
     mIdleCount++;
 
     SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
     return NS_OK;
 }
 
 void
 nsSocketTransportService::RemoveFromIdleList(SocketContext *sock)
 {
     SOCKET_LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%x]\n", sock->mHandler));
 
-    PRUint32 index = sock - &mIdleList[0];
-    NS_ASSERTION(index < NS_SOCKET_MAX_COUNT, "invalid index");
+    PRUint32 index = sock - mIdleList;
+    NS_ASSERTION(index < mIdleListSize, "invalid index in idle list");
 
     if (index != mIdleCount-1)
         mIdleList[index] = mIdleList[mIdleCount-1];
     mIdleCount--;
 
     SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
 }
 
 void
 nsSocketTransportService::MoveToIdleList(SocketContext *sock)
 {
     nsresult rv = AddToIdleList(sock);
     if (NS_FAILED(rv))
-        DetachSocket(sock);
+        DetachSocket(mActiveList, sock);
     else
         RemoveFromPollList(sock);
 }
 
 void
 nsSocketTransportService::MoveToPollList(SocketContext *sock)
 {
     nsresult rv = AddToPollList(sock);
     if (NS_FAILED(rv))
-        DetachSocket(sock);
+        DetachSocket(mIdleList, sock);
     else
         RemoveFromIdleList(sock);
 }
 
+PRBool
+nsSocketTransportService::GrowActiveList()
+{
+    PRInt32 toAdd = gMaxCount - mActiveListSize;
+    if (toAdd > 100)
+        toAdd = 100;
+    if (toAdd < 1)
+        return PR_FALSE;
+    
+    mActiveListSize += toAdd;
+    mActiveList = (SocketContext *)
+        moz_xrealloc(mActiveList, sizeof(SocketContext) * mActiveListSize);
+    mPollList = (PRPollDesc *)
+        moz_xrealloc(mPollList, sizeof(PRPollDesc) * (mActiveListSize + 1));
+    return PR_TRUE;
+}
+
+PRBool
+nsSocketTransportService::GrowIdleList()
+{
+    PRInt32 toAdd = gMaxCount - mIdleListSize;
+    if (toAdd > 100)
+        toAdd = 100;
+    if (toAdd < 1)
+        return PR_FALSE;
+
+    mIdleListSize += toAdd;
+    mIdleList = (SocketContext *)
+        moz_xrealloc(mIdleList, sizeof(SocketContext) * mIdleListSize);
+    return PR_TRUE;
+}
+
 PRIntervalTime
 nsSocketTransportService::PollTimeout()
 {
     if (mActiveCount == 0)
         return NS_SOCKET_POLL_TIMEOUT;
 
     // compute minimum time before any socket timeout expires.
     PRUint32 minR = PR_UINT16_MAX;
@@ -589,19 +646,19 @@ nsSocketTransportService::Run()
         NS_ProcessNextEvent(thread);
     }
 
     SOCKET_LOG(("STS shutting down thread\n"));
 
     // detach any sockets
     PRInt32 i;
     for (i=mActiveCount-1; i>=0; --i)
-        DetachSocket(&mActiveList[i]);
+        DetachSocket(mActiveList, &mActiveList[i]);
     for (i=mIdleCount-1; i>=0; --i)
-        DetachSocket(&mIdleList[i]);
+        DetachSocket(mIdleList, &mIdleList[i]);
 
     // Final pass over the event queue. This makes sure that events posted by
     // socket detach handlers get processed.
     NS_ProcessPendingEvents(thread);
 
     gSocketThread = nsnull;
 
     SOCKET_LOG(("STS thread exit\n"));
@@ -630,17 +687,17 @@ nsSocketTransportService::DoPollIteratio
     for (i=mActiveCount-1; i>=0; --i) {
         //---
         SOCKET_LOG(("  active [%u] { handler=%x condition=%x pollflags=%hu }\n", i,
             mActiveList[i].mHandler,
             mActiveList[i].mHandler->mCondition,
             mActiveList[i].mHandler->mPollFlags));
         //---
         if (NS_FAILED(mActiveList[i].mHandler->mCondition))
-            DetachSocket(&mActiveList[i]);
+            DetachSocket(mActiveList, &mActiveList[i]);
         else {
             PRUint16 in_flags = mActiveList[i].mHandler->mPollFlags;
             if (in_flags == 0)
                 MoveToIdleList(&mActiveList[i]);
             else {
                 // update poll flags
                 mPollList[i+1].in_flags = in_flags;
                 mPollList[i+1].out_flags = 0;
@@ -650,17 +707,17 @@ nsSocketTransportService::DoPollIteratio
     for (i=count-1; i>=0; --i) {
         //---
         SOCKET_LOG(("  idle [%u] { handler=%x condition=%x pollflags=%hu }\n", i,
             mIdleList[i].mHandler,
             mIdleList[i].mHandler->mCondition,
             mIdleList[i].mHandler->mPollFlags));
         //---
         if (NS_FAILED(mIdleList[i].mHandler->mCondition))
-            DetachSocket(&mIdleList[i]);
+            DetachSocket(mIdleList, &mIdleList[i]);
         else if (mIdleList[i].mHandler->mPollFlags != 0)
             MoveToPollList(&mIdleList[i]);
     }
 
     SOCKET_LOG(("  calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount));
 
     // Measures seconds spent while blocked on PR_Poll
     PRUint32 pollInterval;
@@ -697,17 +754,17 @@ nsSocketTransportService::DoPollIteratio
         }
 
         //
         // check for "dead" sockets and remove them (need to do this in
         // reverse order obviously).
         //
         for (i=mActiveCount-1; i>=0; --i) {
             if (NS_FAILED(mActiveList[i].mHandler->mCondition))
-                DetachSocket(&mActiveList[i]);
+                DetachSocket(mActiveList, &mActiveList[i]);
         }
 
         if (n != 0 && mPollList[0].out_flags == PR_POLL_READ) {
             // acknowledge pollable event (wait should not block)
             if (PR_WaitForPollableEvent(mThreadEvent) != PR_SUCCESS) {
                 // On Windows, the TCP loopback connection in the
                 // pollable event may become broken when a laptop
                 // switches between wired and wireless networks or
@@ -766,8 +823,71 @@ nsSocketTransportService::Observe(nsISup
 NS_IMETHODIMP
 nsSocketTransportService::GetSendBufferSize(PRInt32 *value)
 {
     *value = mSendBufferSize;
     return NS_OK;
 }
 
 
+/// ugly OS specific includes are placed at the bottom of the src for clarity
+
+#if defined(XP_WIN)
+#include <windows.h>
+#elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
+#include <sys/resource.h>
+#endif
+
+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.
+
+    struct rlimit rlimitData;
+    if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1)
+        return PR_SUCCESS;
+    if (rlimitData.rlim_cur >=  SOCKET_LIMIT_TARGET + 250) {
+        gMaxCount = SOCKET_LIMIT_TARGET;
+        return PR_SUCCESS;
+    }
+
+    PRInt32 maxallowed = rlimitData.rlim_max;
+    if (maxallowed == -1) {                       /* no limit */
+        maxallowed = SOCKET_LIMIT_TARGET + 250;
+    } else if ((PRUint32)maxallowed < SOCKET_LIMIT_MIN + 250) {
+        return PR_SUCCESS;
+    } else if ((PRUint32)maxallowed > SOCKET_LIMIT_TARGET + 250) {
+        maxallowed = SOCKET_LIMIT_TARGET + 250;
+    }
+
+    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;
+
+#elif defined(XP_WIN) && !defined(WIN_CE)
+    // win 95, 98, etc had a limit of 100 - so we will just
+    // use the historical 50 in every case older than XP (0x501).
+    // >= XP is confirmed to have at least 1000
+
+    OSVERSIONINFO osInfo = { sizeof(OSVERSIONINFO) };
+    if (GetVersionEx(&osInfo)) {
+        PRInt32 version = 
+            (osInfo.dwMajorVersion & 0xff) << 8 | 
+            (osInfo.dwMinorVersion & 0xff);
+        if (version >= 0x501)                    /* xp or later */
+            gMaxCount = SOCKET_LIMIT_TARGET;
+    }
+#else
+    // other platforms are harder to test - so leave at safe legacy value
+#endif
+
+    return PR_SUCCESS;
+}
--- a/netwerk/base/src/nsSocketTransportService2.h
+++ b/netwerk/base/src/nsSocketTransportService2.h
@@ -60,17 +60,16 @@
 //
 extern PRLogModuleInfo *gSocketTransportLog;
 #endif
 #define SOCKET_LOG(args)     PR_LOG(gSocketTransportLog, PR_LOG_DEBUG, args)
 #define SOCKET_LOG_ENABLED() PR_LOG_TEST(gSocketTransportLog, PR_LOG_DEBUG)
 
 //-----------------------------------------------------------------------------
 
-#define NS_SOCKET_MAX_COUNT    50
 #define NS_SOCKET_POLL_TIMEOUT PR_INTERVAL_NO_TIMEOUT
 
 //-----------------------------------------------------------------------------
 
 class nsSocketTransportService : public nsPISocketTransportService
                                , public nsIEventTarget
                                , public nsIThreadObserver
                                , public nsIRunnable
@@ -84,25 +83,31 @@ public:
     NS_DECL_NSISOCKETTRANSPORTSERVICE
     NS_DECL_NSIEVENTTARGET
     NS_DECL_NSITHREADOBSERVER
     NS_DECL_NSIRUNNABLE
     NS_DECL_NSIOBSERVER 
 
     nsSocketTransportService();
 
+    // Max Socket count may need to get initialized/used by nsHttpHandler
+    // before this class is initialized.
+    static PRUint32 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.
     //
     PRBool CanAttachSocket() {
-        return mActiveCount + mIdleCount < NS_SOCKET_MAX_COUNT;
+        return mActiveCount + mIdleCount < gMaxCount;
     }
 
 protected:
 
     virtual ~nsSocketTransportService();
 
 private:
 
@@ -148,38 +153,44 @@ private:
 
     struct SocketContext
     {
         PRFileDesc       *mFD;
         nsASocketHandler *mHandler;
         PRUint16          mElapsedTime;  // time elapsed w/o activity
     };
 
-    SocketContext mActiveList [ NS_SOCKET_MAX_COUNT ];
-    SocketContext mIdleList   [ NS_SOCKET_MAX_COUNT ];
+    SocketContext *mActiveList;                   /* mListSize entries */
+    SocketContext *mIdleList;                     /* mListSize entries */
 
+    PRUint32 mActiveListSize;
+    PRUint32 mIdleListSize;
     PRUint32 mActiveCount;
     PRUint32 mIdleCount;
 
-    nsresult DetachSocket(SocketContext *);
+    nsresult DetachSocket(SocketContext *, SocketContext *);
     nsresult AddToIdleList(SocketContext *);
     nsresult AddToPollList(SocketContext *);
     void RemoveFromIdleList(SocketContext *);
     void RemoveFromPollList(SocketContext *);
     void MoveToIdleList(SocketContext *sock);
     void MoveToPollList(SocketContext *sock);
+
+    PRBool GrowActiveList();
+    PRBool GrowIdleList();
+    void   InitMaxCount();
     
     //-------------------------------------------------------------------------
     // poll list (socket thread only)
     //
     // first element of the poll list is mThreadEvent (or null if the pollable
     // event cannot be created).
     //-------------------------------------------------------------------------
 
-    PRPollDesc mPollList[ NS_SOCKET_MAX_COUNT + 1 ];
+    PRPollDesc *mPollList;                        /* mListSize + 1 entries */
 
     PRIntervalTime PollTimeout();            // computes ideal poll timeout
     nsresult       DoPollIteration(PRBool wait);
                                              // perfoms a single poll iteration
     PRInt32        Poll(PRBool wait, PRUint32 *interval);
                                              // calls PR_Poll.  the out param
                                              // interval indicates the poll
                                              // duration in seconds.
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -839,17 +839,21 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
                 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_REQUEST_DELAY,
                                       mMaxRequestDelay);
         }
     }
 
     if (PREF_CHANGED(HTTP_PREF("max-connections"))) {
         rv = prefs->GetIntPref(HTTP_PREF("max-connections"), &val);
         if (NS_SUCCEEDED(rv)) {
-            mMaxConnections = (PRUint16) NS_CLAMP(val, 1, NS_SOCKET_MAX_COUNT);
+            PR_CallOnce(&nsSocketTransportService::gMaxCountInitOnce,
+                        nsSocketTransportService::DiscoverMaxCount);
+            mMaxConnections =
+                (PRUint16) NS_CLAMP((PRUint32)val, 1,
+                                    nsSocketTransportService::gMaxCount);
             if (mConnMgr)
                 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_CONNECTIONS,
                                       mMaxConnections);
         }
     }
 
     if (PREF_CHANGED(HTTP_PREF("max-connections-per-server"))) {
         rv = prefs->GetIntPref(HTTP_PREF("max-connections-per-server"), &val);