From 7bd44a185883b87673c4a2ee8d8a025ba8fcafa8 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Tue, 25 Oct 2011 11:36:49 -0400 Subject: [PATCH] bug 692260 - pr_poll limit autodiscovery for windows r=jduell --HG-- extra : rebase_source : dc9b024b4004e0a4025e843e94e046b4dd69dca9 --- .../base/src/nsSocketTransportService2.cpp | 76 +++++++++++++++++++ netwerk/base/src/nsSocketTransportService2.h | 6 ++ netwerk/protocol/http/nsHttpConnectionMgr.cpp | 10 +++ netwerk/protocol/http/nsHttpHandler.cpp | 27 +++++-- netwerk/protocol/http/nsHttpHandler.h | 1 + 5 files changed, 115 insertions(+), 5 deletions(-) diff --git a/netwerk/base/src/nsSocketTransportService2.cpp b/netwerk/base/src/nsSocketTransportService2.cpp index 3bf0affb1edb..1f1f516f6573 100644 --- a/netwerk/base/src/nsSocketTransportService2.cpp +++ b/netwerk/base/src/nsSocketTransportService2.cpp @@ -85,6 +85,7 @@ nsSocketTransportService::nsSocketTransportService() , mActiveCount(0) , mIdleCount(0) , mSendBufferSize(0) + , mProbedMaxCount(false) { #if defined(PR_LOGGING) gSocketTransportLog = PR_NewLogModule("nsSocketTransport"); @@ -719,6 +720,14 @@ nsSocketTransportService::DoPollIteration(bool wait) SOCKET_LOG((" calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount)); +#if defined(XP_WIN) + // 30 active connections is the historic limit before firefox 7's 256. A few + // windows systems have troubles with the higher limit, so actively probe a + // limit the first time we exceed 30. + if ((mActiveCount > 30) && !mProbedMaxCount) + ProbeMaxCount(); +#endif + // Measures seconds spent while blocked on PR_Poll PRUint32 pollInterval; @@ -835,6 +844,73 @@ nsSocketTransportService::GetSendBufferSize(PRInt32 *value) #include #endif +// Right now the only need to do this is on windows. +#if defined(XP_WIN) +void +nsSocketTransportService::ProbeMaxCount() +{ + NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); + + if (mProbedMaxCount) + return; + mProbedMaxCount = true; + + PRInt32 startedMaxCount = gMaxCount; + + // Allocate and test a PR_Poll up to the gMaxCount number of unconnected + // sockets. See bug 692260 - windows should be able to handle 1000 sockets + // in select() without a problem, but LSPs have been known to balk at lower + // numbers. (64 in the bug). + + // Allocate + struct PRPollDesc pfd[SOCKET_LIMIT_TARGET]; + PRUint32 numAllocated = 0; + + for (PRUint32 index = 0 ; index < gMaxCount; ++index) { + pfd[index].in_flags = PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT; + pfd[index].out_flags = 0; + pfd[index].fd = PR_OpenTCPSocket(PR_AF_INET); + if (!pfd[index].fd) { + SOCKET_LOG(("Socket Limit Test index %d failed\n", index)); + if (index < SOCKET_LIMIT_MIN) + gMaxCount = SOCKET_LIMIT_MIN; + else + gMaxCount = index; + break; + } + ++numAllocated; + } + + // Test + PR_STATIC_ASSERT(SOCKET_LIMIT_MIN >= 32U); + while (gMaxCount <= numAllocated) { + PRInt32 rv = PR_Poll(pfd, gMaxCount, PR_MillisecondsToInterval(0)); + + SOCKET_LOG(("Socket Limit Test poll() size=%d rv=%d\n", + gMaxCount, rv)); + + if (rv >= 0) + break; + + SOCKET_LOG(("Socket Limit Test poll confirmationSize=%d rv=%d error=%d\n", + gMaxCount, rv, PR_GetError())); + + gMaxCount -= 32; + if (gMaxCount <= SOCKET_LIMIT_MIN) { + gMaxCount = SOCKET_LIMIT_MIN; + break; + } + } + + // Free + for (PRUint32 index = 0 ; index < numAllocated; ++index) + if (pfd[index].fd) + PR_Close(pfd[index].fd); + + SOCKET_LOG(("Socket Limit Test max was confirmed at %d\n", gMaxCount)); +} +#endif // windows + PRStatus nsSocketTransportService::DiscoverMaxCount() { diff --git a/netwerk/base/src/nsSocketTransportService2.h b/netwerk/base/src/nsSocketTransportService2.h index c1b3dd5f46e9..4734229828dd 100644 --- a/netwerk/base/src/nsSocketTransportService2.h +++ b/netwerk/base/src/nsSocketTransportService2.h @@ -205,6 +205,12 @@ private: // Preference Monitor for SendBufferSize nsresult UpdatePrefs(); PRInt32 mSendBufferSize; + + // Socket thread only for dynamically adjusting max socket size +#if defined(XP_WIN) + void ProbeMaxCount(); +#endif + bool mProbedMaxCount; }; extern nsSocketTransportService *gSocketTransportService; diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index f861035b091e..d24b82b6e7df 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -646,6 +646,16 @@ nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 cap LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x]\n", ci->HashKey().get(), caps)); + // update maxconns if potentially limited by the max socket count + // this requires a dynamic reduction in the max socket count to a point + // lower than the max-connections pref. + PRUint32 maxSocketCount = gHttpHandler->MaxSocketCount(); + if (mMaxConns > maxSocketCount) { + mMaxConns = maxSocketCount; + LOG(("nsHttpConnectionMgr %p mMaxConns dynamically reduced to %u", + this, mMaxConns)); + } + // If there are more active connections than the global limit, then we're // done. Purging idle connections won't get us below it. if (mNumActiveConns >= mMaxConns) { diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 70d7bea16416..2516e31fe629 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -773,6 +773,24 @@ nsHttpHandler::InitUserAgentComponents() mUserAgentIsDirty = true; } +PRUint32 +nsHttpHandler::MaxSocketCount() +{ + PR_CallOnce(&nsSocketTransportService::gMaxCountInitOnce, + nsSocketTransportService::DiscoverMaxCount); + // Don't use the full max count because sockets can be held in + // the persistent connection pool for a long time and that could + // starve other users. + + PRUint32 maxCount = nsSocketTransportService::gMaxCount; + if (maxCount <= 8) + maxCount = 1; + else + maxCount -= 8; + + return maxCount; +} + void nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref) { @@ -837,11 +855,10 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref) if (PREF_CHANGED(HTTP_PREF("max-connections"))) { rv = prefs->GetIntPref(HTTP_PREF("max-connections"), &val); if (NS_SUCCEEDED(rv)) { - PR_CallOnce(&nsSocketTransportService::gMaxCountInitOnce, - nsSocketTransportService::DiscoverMaxCount); - mMaxConnections = - (PRUint16) NS_CLAMP((PRUint32)val, 1, - nsSocketTransportService::gMaxCount); + + mMaxConnections = (PRUint16) NS_CLAMP((PRUint32)val, + 1, MaxSocketCount()); + if (mConnMgr) mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_CONNECTIONS, mMaxConnections); diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index 5dc0805049bc..4eb0abae246a 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -108,6 +108,7 @@ public: PRUint8 GetQoSBits() { return mQoSBits; } PRUint16 GetIdleSynTimeout() { return mIdleSynTimeout; } bool FastFallbackToIPv4() { return mFastFallbackToIPv4; } + PRUint32 MaxSocketCount(); bool IsPersistentHttpsCachingEnabled() { return mEnablePersistentHttpsCaching; }