Bug 591881 - Suspend pruning of idle connections when possible. r=dwitte a=blocking-fennec

This commit is contained in:
Jon Hemming 2010-10-14 17:28:43 -07:00
Родитель 3b1fd66438
Коммит 8c32c3ad25
6 изменённых файлов: 133 добавлений и 61 удалений

Просмотреть файл

@ -206,6 +206,15 @@ nsHttpConnection::CanReuse()
&& IsAlive(); && IsAlive();
} }
PRUint32 nsHttpConnection::TimeToLive()
{
PRInt32 tmp = mIdleTimeout - (NowInSeconds() - mLastReadTime);
if (0 > tmp)
tmp = 0;
return tmp;
}
PRBool PRBool
nsHttpConnection::IsAlive() nsHttpConnection::IsAlive()
{ {

Просмотреть файл

@ -99,6 +99,10 @@ public:
PRBool SupportsPipelining() { return mSupportsPipelining; } PRBool SupportsPipelining() { return mSupportsPipelining; }
PRBool IsKeepAlive() { return mKeepAliveMask && mKeepAlive; } PRBool IsKeepAlive() { return mKeepAliveMask && mKeepAlive; }
PRBool CanReuse(); // can this connection be reused? PRBool CanReuse(); // can this connection be reused?
// Returns time in seconds for how long connection can be reused.
PRUint32 TimeToLive();
void DontReuse() { mKeepAliveMask = PR_FALSE; void DontReuse() { mKeepAliveMask = PR_FALSE;
mKeepAlive = PR_FALSE; mKeepAlive = PR_FALSE;
mIdleTimeout = 0; } mIdleTimeout = 0; }

Просмотреть файл

@ -46,6 +46,8 @@
#include "nsIServiceManager.h" #include "nsIServiceManager.h"
#include "nsIObserverService.h"
// defined by the socket transport service while active // defined by the socket transport service while active
extern PRThread *gSocketThread; extern PRThread *gSocketThread;
@ -53,6 +55,9 @@ static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
NS_IMPL_THREADSAFE_ISUPPORTS1(nsHttpConnectionMgr, nsIObserver)
static void static void
InsertTransactionSorted(nsTArray<nsHttpTransaction*> &pendingQ, nsHttpTransaction *trans) InsertTransactionSorted(nsTArray<nsHttpTransaction*> &pendingQ, nsHttpTransaction *trans)
{ {
@ -82,6 +87,7 @@ nsHttpConnectionMgr::nsHttpConnectionMgr()
, mMaxPersistConnsPerProxy(0) , mMaxPersistConnsPerProxy(0)
, mNumActiveConns(0) , mNumActiveConns(0)
, mNumIdleConns(0) , mNumIdleConns(0)
, mTimeOfNextWakeUp(LL_MAXUINT)
{ {
LOG(("Creating nsHttpConnectionMgr @%x\n", this)); LOG(("Creating nsHttpConnectionMgr @%x\n", this));
} }
@ -177,6 +183,59 @@ nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler, PRInt32 iparam, void
return rv; return rv;
} }
void
nsHttpConnectionMgr::PruneDeadConnectionsAfter(PRUint32 timeInSeconds)
{
LOG(("nsHttpConnectionMgr::PruneDeadConnectionsAfter\n"));
if(!mTimer)
mTimer = do_CreateInstance("@mozilla.org/timer;1");
// failure to create a timer is not a fatal error, but idle connections
// will not be cleaned up until we try to use them.
if (mTimer) {
mTimeOfNextWakeUp = timeInSeconds + NowInSeconds();
mTimer->Init(this, timeInSeconds*1000, nsITimer::TYPE_ONE_SHOT);
} else {
NS_WARNING("failed to create: timer for pruning the dead connections!");
}
}
void
nsHttpConnectionMgr::StopPruneDeadConnectionsTimer()
{
LOG(("nsHttpConnectionMgr::StopPruneDeadConnectionsTimer\n"));
if (mTimer) {
mTimer->Cancel();
mTimer = NULL;
}
}
//-----------------------------------------------------------------------------
// nsHttpConnectionMgr::nsIObserver
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpConnectionMgr::Observe(nsISupports *subject,
const char *topic,
const PRUnichar *data)
{
LOG(("nsHttpConnectionMgr::Observe [topic=\"%s\"]\n", topic));
if (0 == strcmp(topic, "timer-callback")) {
// prune dead connections
PruneDeadConnections();
#ifdef DEBUG
nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
NS_ASSERTION(timer == mTimer, "unexpected timer-callback");
#endif
}
return NS_OK;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
nsresult nsresult
@ -317,6 +376,8 @@ nsHttpConnectionMgr::PurgeOneIdleConnectionCB(nsHashKey *key, void *data, void *
conn->Close(NS_ERROR_ABORT); conn->Close(NS_ERROR_ABORT);
NS_RELEASE(conn); NS_RELEASE(conn);
self->mNumIdleConns--; self->mNumIdleConns--;
if (0 == self->mNumIdleConns)
self->StopPruneDeadConnectionsTimer();
return kHashEnumerateStop; return kHashEnumerateStop;
} }
@ -331,6 +392,9 @@ nsHttpConnectionMgr::PruneDeadConnectionsCB(nsHashKey *key, void *data, void *cl
LOG((" pruning [ci=%s]\n", ent->mConnInfo->HashKey().get())); LOG((" pruning [ci=%s]\n", ent->mConnInfo->HashKey().get()));
// Find out how long it will take for next idle connection to not be reusable
// anymore.
PRUint32 timeToNextExpire = PR_UINT32_MAX;
PRInt32 count = ent->mIdleConns.Length(); PRInt32 count = ent->mIdleConns.Length();
if (count > 0) { if (count > 0) {
for (PRInt32 i=count-1; i>=0; --i) { for (PRInt32 i=count-1; i>=0; --i) {
@ -340,10 +404,27 @@ nsHttpConnectionMgr::PruneDeadConnectionsCB(nsHashKey *key, void *data, void *cl
conn->Close(NS_ERROR_ABORT); conn->Close(NS_ERROR_ABORT);
NS_RELEASE(conn); NS_RELEASE(conn);
self->mNumIdleConns--; self->mNumIdleConns--;
} else {
timeToNextExpire = PR_MIN(timeToNextExpire, conn->TimeToLive());
} }
} }
} }
// If time to next expire found is shorter than time to next wake-up, we need to
// change the time for next wake-up.
if (0 < ent->mIdleConns.Length()) {
PRUint32 now = NowInSeconds();
PRUint64 timeOfNextExpire = now + timeToNextExpire;
// If pruning of dead connections is not already scheduled to happen
// or time found for next connection to expire is is before
// mTimeOfNextWakeUp, we need to schedule the pruning to happen
// after timeToNextExpire.
if (!self->mTimer || timeOfNextExpire < self->mTimeOfNextWakeUp) {
self->PruneDeadConnectionsAfter(timeToNextExpire);
}
} else if (0 == self->mNumIdleConns) {
self->StopPruneDeadConnectionsTimer();
}
#ifdef DEBUG #ifdef DEBUG
count = ent->mActiveConns.Length(); count = ent->mActiveConns.Length();
if (count > 0) { if (count > 0) {
@ -401,6 +482,10 @@ nsHttpConnectionMgr::ShutdownPassCB(nsHashKey *key, void *data, void *closure)
conn->Close(NS_ERROR_ABORT); conn->Close(NS_ERROR_ABORT);
NS_RELEASE(conn); NS_RELEASE(conn);
} }
// If all idle connections are removed,
// we can stop pruning dead connections.
if (0 == self->mNumIdleConns)
self->StopPruneDeadConnectionsTimer();
// close all pending transactions // close all pending transactions
while (ent->mPendingQ.Length()) { while (ent->mPendingQ.Length()) {
@ -543,6 +628,12 @@ nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent, PRUint8 caps,
} }
if (!conn) { if (!conn) {
// No reusable idle connection found for this entry. If there are no
// idle connections left at all, we need to make sure that we are not
// pruning dead connections anymore.
if (0 == mNumIdleConns)
StopPruneDeadConnectionsTimer();
conn = new nsHttpConnection(); conn = new nsHttpConnection();
if (!conn) if (!conn)
return; return;
@ -556,7 +647,8 @@ nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent, PRUint8 caps,
// We created a new connection that will become active, purge the // We created a new connection that will become active, purge the
// oldest idle connection if we've reached the upper limit. // oldest idle connection if we've reached the upper limit.
if (mNumIdleConns + mNumActiveConns + 1 > mMaxConns) // This only needs to be done if there is a idle connection.
if (0 < mNumIdleConns && mNumIdleConns + mNumActiveConns + 1 > mMaxConns)
mCT.Enumerate(PurgeOneIdleConnectionCB, this); mCT.Enumerate(PurgeOneIdleConnectionCB, this);
// XXX this just purges a random idle connection. we should instead // XXX this just purges a random idle connection. we should instead
@ -874,6 +966,12 @@ nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
// connections first before getting to this one. // connections first before getting to this one.
ent->mIdleConns.AppendElement(conn); ent->mIdleConns.AppendElement(conn);
mNumIdleConns++; mNumIdleConns++;
// If the added connection was first idle connection or has shortest
// time to live among the idle connections, pruning dead
// connections needs to be done when it can't be reused anymore.
PRUint32 timeToLive = conn->TimeToLive();
if(!mTimer || NowInSeconds() + timeToLive < mTimeOfNextWakeUp)
PruneDeadConnectionsAfter(timeToLive);
} }
else { else {
LOG((" connection cannot be reused; closing connection\n")); LOG((" connection cannot be reused; closing connection\n"));
@ -990,3 +1088,4 @@ nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, PRUint32 bufL
{ {
return mConn->PushBack(buf, bufLen); return mConn->PushBack(buf, bufLen);
} }

Просмотреть файл

@ -48,13 +48,18 @@
#include "nsAutoPtr.h" #include "nsAutoPtr.h"
#include "prmon.h" #include "prmon.h"
#include "nsIObserver.h"
#include "nsITimer.h"
class nsHttpPipeline; class nsHttpPipeline;
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
class nsHttpConnectionMgr class nsHttpConnectionMgr : public nsIObserver
{ {
public: public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
// parameter names // parameter names
enum nsParamName { enum nsParamName {
@ -86,18 +91,12 @@ public:
// NOTE: functions below may be called on any thread. // NOTE: functions below may be called on any thread.
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
nsrefcnt AddRef() // Schedules next pruning of dead connection to happen after
{ // given time.
return PR_AtomicIncrement(&mRef); void PruneDeadConnectionsAfter(PRUint32 time);
}
nsrefcnt Release() // Stops timer scheduled for next pruning of dead connections.
{ void StopPruneDeadConnectionsTimer();
nsrefcnt n = PR_AtomicDecrement(&mRef);
if (n == 0)
delete this;
return n;
}
// adds a transaction to the list of managed transactions. // adds a transaction to the list of managed transactions.
nsresult AddTransaction(nsHttpTransaction *, PRInt32 priority); nsresult AddTransaction(nsHttpTransaction *, PRInt32 priority);
@ -274,10 +273,18 @@ private:
void OnMsgReclaimConnection (PRInt32, void *); void OnMsgReclaimConnection (PRInt32, void *);
void OnMsgUpdateParam (PRInt32, void *); void OnMsgUpdateParam (PRInt32, void *);
// counters // Total number of active connections in all of the ConnectionEntry objects
// that are accessed from mCT connection table.
PRUint16 mNumActiveConns; PRUint16 mNumActiveConns;
// Total number of idle connections in all of the ConnectionEntry objects
// that are accessed from mCT connection table.
PRUint16 mNumIdleConns; PRUint16 mNumIdleConns;
// Holds time in seconds for next wake-up to prune dead connections.
PRUint64 mTimeOfNextWakeUp;
// Timer for next pruning of dead connections.
nsCOMPtr<nsITimer> mTimer;
// //
// the connection table // the connection table
// //

Просмотреть файл

@ -210,9 +210,6 @@ nsHttpHandler::nsHttpHandler()
nsHttpHandler::~nsHttpHandler() nsHttpHandler::~nsHttpHandler()
{ {
// We do not deal with the timer cancellation in the destructor since
// it is taken care of in xpcom shutdown event in the Observe method.
LOG(("Deleting nsHttpHandler [this=%x]\n", this)); LOG(("Deleting nsHttpHandler [this=%x]\n", this));
// make sure the connection manager is shutdown // make sure the connection manager is shutdown
@ -335,7 +332,6 @@ nsHttpHandler::Init()
mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, PR_TRUE); mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, PR_TRUE);
} }
StartPruneDeadConnectionsTimer();
return NS_OK; return NS_OK;
} }
@ -363,31 +359,6 @@ nsHttpHandler::InitConnectionMgr()
return rv; return rv;
} }
void
nsHttpHandler::StartPruneDeadConnectionsTimer()
{
LOG(("nsHttpHandler::StartPruneDeadConnectionsTimer\n"));
mTimer = do_CreateInstance("@mozilla.org/timer;1");
NS_ASSERTION(mTimer, "no timer");
// failure to create a timer is not a fatal error, but idle connections
// will not be cleaned up until we try to use them.
if (mTimer)
mTimer->Init(this, 15*1000, // every 15 seconds
nsITimer::TYPE_REPEATING_SLACK);
}
void
nsHttpHandler::StopPruneDeadConnectionsTimer()
{
LOG(("nsHttpHandler::StopPruneDeadConnectionsTimer\n"));
if (mTimer) {
mTimer->Cancel();
mTimer = 0;
}
}
nsresult nsresult
nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request, nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request,
PRUint8 caps, PRUint8 caps,
@ -1649,9 +1620,6 @@ nsHttpHandler::Observe(nsISupports *subject,
else if (strcmp(topic, "profile-change-net-teardown") == 0 || else if (strcmp(topic, "profile-change-net-teardown") == 0 ||
strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
// kill off the "prune dead connections" timer
StopPruneDeadConnectionsTimer();
// clear cache of all authentication credentials. // clear cache of all authentication credentials.
mAuthCache.ClearAll(); mAuthCache.ClearAll();
@ -1666,18 +1634,6 @@ nsHttpHandler::Observe(nsISupports *subject,
else if (strcmp(topic, "profile-change-net-restore") == 0) { else if (strcmp(topic, "profile-change-net-restore") == 0) {
// initialize connection manager // initialize connection manager
InitConnectionMgr(); InitConnectionMgr();
// restart the "prune dead connections" timer
StartPruneDeadConnectionsTimer();
}
else if (strcmp(topic, "timer-callback") == 0) {
// prune dead connections
#ifdef DEBUG
nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
NS_ASSERTION(timer == mTimer, "unexpected timer-callback");
#endif
if (mConnMgr)
mConnMgr->PruneDeadConnections();
} }
else if (strcmp(topic, "net:clear-active-logins") == 0) { else if (strcmp(topic, "net:clear-active-logins") == 0) {
mAuthCache.ClearAll(); mAuthCache.ClearAll();

Просмотреть файл

@ -234,8 +234,6 @@ private:
nsresult SetAcceptCharsets(const char *); nsresult SetAcceptCharsets(const char *);
nsresult InitConnectionMgr(); nsresult InitConnectionMgr();
void StartPruneDeadConnectionsTimer();
void StopPruneDeadConnectionsTimer();
void NotifyObservers(nsIHttpChannel *chan, const char *event); void NotifyObservers(nsIHttpChannel *chan, const char *event);
@ -247,7 +245,6 @@ private:
nsCOMPtr<nsIObserverService> mObserverService; nsCOMPtr<nsIObserverService> mObserverService;
nsCOMPtr<nsICookieService> mCookieService; nsCOMPtr<nsICookieService> mCookieService;
nsCOMPtr<nsIIDNService> mIDNConverter; nsCOMPtr<nsIIDNService> mIDNConverter;
nsCOMPtr<nsITimer> mTimer;
nsCOMPtr<nsIStrictTransportSecurityService> mSTSService; nsCOMPtr<nsIStrictTransportSecurityService> mSTSService;
// the authentication credentials cache // the authentication credentials cache