bug 965348 - control SocketTransportService::Poll tick during spdy idle r=hurley

This commit is contained in:
Patrick McManus 2014-01-30 02:29:20 -05:00
parent 7fd7d52cb1
commit f0d11b753b
12 changed files with 98 additions and 40 deletions

View File

@ -356,6 +356,11 @@ nsSocketTransportService::PollTimeout()
if (r < minR)
minR = r;
}
// nsASocketHandler defines UINT16_MAX as do not timeout
if (minR == UINT16_MAX) {
SOCKET_LOG(("poll timeout: none\n"));
return NS_SOCKET_POLL_TIMEOUT;
}
SOCKET_LOG(("poll timeout: %lu\n", minR));
return PR_SecondsToInterval(minR);
}

View File

@ -22,7 +22,7 @@ public:
virtual bool CanReuse() = 0;
virtual bool RoomForMoreStreams() = 0;
virtual PRIntervalTime IdleTime() = 0;
virtual void ReadTimeoutTick(PRIntervalTime now) = 0;
virtual uint32_t ReadTimeoutTick(PRIntervalTime now) = 0;
virtual void DontReuse() = 0;
static ASpdySession *NewSpdySession(uint32_t version,

View File

@ -246,7 +246,7 @@ Http2Session::IdleTime()
return PR_IntervalNow() - mLastDataReadEpoch;
}
void
uint32_t
Http2Session::ReadTimeoutTick(PRIntervalTime now)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@ -255,24 +255,26 @@ Http2Session::ReadTimeoutTick(PRIntervalTime now)
this, PR_IntervalToSeconds(now - mLastReadEpoch)));
if (!mPingThreshold)
return;
return UINT32_MAX;
if ((now - mLastReadEpoch) < mPingThreshold) {
// recent activity means ping is not an issue
if (mPingSentEpoch)
mPingSentEpoch = 0;
return;
return PR_IntervalToSeconds(mPingThreshold) -
PR_IntervalToSeconds(now - mLastReadEpoch);
}
if (mPingSentEpoch) {
LOG3(("Http2Session::ReadTimeoutTick %p handle outstanding ping\n"));
if ((now - mPingSentEpoch) >= gHttpHandler->SpdyPingTimeout()) {
LOG3(("Http2Session::ReadTimeoutTick %p Ping Timer Exhaustion\n",
this));
LOG3(("Http2Session::ReadTimeoutTick %p Ping Timer Exhaustion\n", this));
mPingSentEpoch = 0;
Close(NS_ERROR_NET_TIMEOUT);
return UINT32_MAX;
}
return;
return 1; // run the tick aggressively while ping is outstanding
}
LOG3(("Http2Session::ReadTimeoutTick %p generating ping\n", this));
@ -311,6 +313,8 @@ Http2Session::ReadTimeoutTick(PRIntervalTime now)
CleanupStream(deleteMe, NS_ERROR_ABORT, CANCEL_ERROR);
} while (deleteMe);
return 1; // run the tick aggressively while ping is outstanding
}
uint32_t

View File

@ -45,8 +45,11 @@ public:
bool CanReuse() { return !mShouldGoAway && !mClosed; }
bool RoomForMoreStreams();
// When the connection is active this is called every 1 second
void ReadTimeoutTick(PRIntervalTime now);
// When the connection is active this is called up to once every 1 second
// return the interval (in seconds) that the connection next wants to
// have this invoked. It might happen sooner depending on the needs of
// other connections.
uint32_t ReadTimeoutTick(PRIntervalTime now);
// Idle time represents time since "goodput".. e.g. a data or header frame
PRIntervalTime IdleTime();

View File

@ -207,7 +207,7 @@ SpdySession3::IdleTime()
return PR_IntervalNow() - mLastDataReadEpoch;
}
void
uint32_t
SpdySession3::ReadTimeoutTick(PRIntervalTime now)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@ -217,13 +217,15 @@ SpdySession3::ReadTimeoutTick(PRIntervalTime now)
this, PR_IntervalToSeconds(now - mLastReadEpoch)));
if (!mPingThreshold)
return;
return UINT32_MAX;
if ((now - mLastReadEpoch) < mPingThreshold) {
// recent activity means ping is not an issue
if (mPingSentEpoch)
mPingSentEpoch = 0;
return;
return PR_IntervalToSeconds(mPingThreshold) -
PR_IntervalToSeconds(now - mLastReadEpoch);
}
if (mPingSentEpoch) {
@ -233,8 +235,9 @@ SpdySession3::ReadTimeoutTick(PRIntervalTime now)
this));
mPingSentEpoch = 0;
Close(NS_ERROR_NET_TIMEOUT);
return UINT32_MAX;
}
return;
return 1; // run the tick aggressively while ping is outstanding
}
LOG(("SpdySession3::ReadTimeoutTick %p generating ping 0x%X\n",
@ -243,7 +246,7 @@ SpdySession3::ReadTimeoutTick(PRIntervalTime now)
if (mNextPingID == 0xffffffff) {
LOG(("SpdySession3::ReadTimeoutTick %p cannot form ping - ids exhausted\n",
this));
return;
return UINT32_MAX;
}
mPingSentEpoch = PR_IntervalNow();
@ -287,6 +290,7 @@ SpdySession3::ReadTimeoutTick(PRIntervalTime now)
"ping ids exhausted marking goaway\n", this));
mShouldGoAway = true;
}
return 1; // run the tick aggressively while ping is outstanding
}
uint32_t

View File

@ -44,8 +44,11 @@ public:
bool CanReuse() { return !mShouldGoAway && !mClosed; }
bool RoomForMoreStreams();
// When the connection is active this is called every 1 second
void ReadTimeoutTick(PRIntervalTime now);
// When the connection is active this is called up to once every 1 second
// return the interval (in seconds) that the connection next wants to
// have this invoked. It might happen sooner depending on the needs of
// other connections.
uint32_t ReadTimeoutTick(PRIntervalTime now);
// Idle time represents time since "goodput".. e.g. a data or header frame
PRIntervalTime IdleTime();

View File

@ -211,7 +211,7 @@ SpdySession31::IdleTime()
return PR_IntervalNow() - mLastDataReadEpoch;
}
void
uint32_t
SpdySession31::ReadTimeoutTick(PRIntervalTime now)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@ -221,13 +221,15 @@ SpdySession31::ReadTimeoutTick(PRIntervalTime now)
this, PR_IntervalToSeconds(now - mLastReadEpoch)));
if (!mPingThreshold)
return;
return UINT32_MAX;
if ((now - mLastReadEpoch) < mPingThreshold) {
// recent activity means ping is not an issue
if (mPingSentEpoch)
mPingSentEpoch = 0;
return;
return PR_IntervalToSeconds(mPingThreshold) -
PR_IntervalToSeconds(now - mLastReadEpoch);
}
if (mPingSentEpoch) {
@ -237,8 +239,9 @@ SpdySession31::ReadTimeoutTick(PRIntervalTime now)
this));
mPingSentEpoch = 0;
Close(NS_ERROR_NET_TIMEOUT);
return UINT32_MAX;
}
return;
return 1; // run the tick aggressively while ping is outstanding
}
LOG(("SpdySession31::ReadTimeoutTick %p generating ping 0x%X\n",
@ -247,7 +250,7 @@ SpdySession31::ReadTimeoutTick(PRIntervalTime now)
if (mNextPingID == 0xffffffff) {
LOG(("SpdySession31::ReadTimeoutTick %p cannot form ping - ids exhausted\n",
this));
return;
return UINT32_MAX;
}
mPingSentEpoch = PR_IntervalNow();
@ -291,6 +294,7 @@ SpdySession31::ReadTimeoutTick(PRIntervalTime now)
"ping ids exhausted marking goaway\n", this));
mShouldGoAway = true;
}
return 1; // run the tick aggressively while ping is outstanding
}
uint32_t

View File

@ -43,8 +43,11 @@ public:
bool CanReuse() { return !mShouldGoAway && !mClosed; }
bool RoomForMoreStreams();
// When the connection is active this is called every 1 second
void ReadTimeoutTick(PRIntervalTime now);
// When the connection is active this is called up to once every 1 second
// return the interval (in seconds) that the connection next wants to
// have this invoked. It might happen sooner depending on the needs of
// other connections.
uint32_t ReadTimeoutTick(PRIntervalTime now);
// Idle time represents time since "goodput".. e.g. a data or header frame
PRIntervalTime IdleTime();

View File

@ -22,6 +22,7 @@
#include "mozilla/Telemetry.h"
#include "nsISupportsPriority.h"
#include "nsHttpPipeline.h"
#include <algorithm>
#ifdef DEBUG
// defined by the socket transport service while active
@ -941,21 +942,22 @@ nsHttpConnection::TakeTransport(nsISocketTransport **aTransport,
return NS_OK;
}
void
uint32_t
nsHttpConnection::ReadTimeoutTick(PRIntervalTime now)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
// make sure timer didn't tick before Activate()
if (!mTransaction)
return;
return UINT32_MAX;
// Spdy implements some timeout handling using the SPDY ping frame.
if (mSpdySession) {
mSpdySession->ReadTimeoutTick(now);
return;
return mSpdySession->ReadTimeoutTick(now);
}
uint32_t nextTickAfter = UINT32_MAX;
// Timeout if the response is taking too long to arrive.
if (mResponseTimeoutEnabled) {
PRIntervalTime initialResponseDelta = now - mLastWriteTime;
@ -968,12 +970,15 @@ nsHttpConnection::ReadTimeoutTick(PRIntervalTime now)
// This will also close the connection
CloseTransaction(mTransaction, NS_ERROR_NET_TIMEOUT);
return;
return UINT32_MAX;
}
nextTickAfter = PR_IntervalToSeconds(gHttpHandler->ResponseTimeout()) -
PR_IntervalToSeconds(initialResponseDelta);
nextTickAfter = std::max(nextTickAfter, 1U);
}
if (!gHttpHandler->GetPipelineRescheduleOnTimeout())
return;
return nextTickAfter;
PRIntervalTime delta = now - mLastReadTime;
@ -987,6 +992,11 @@ nsHttpConnection::ReadTimeoutTick(PRIntervalTime now)
// be the place to add general read timeout handling if it is desired.
uint32_t pipelineDepth = mTransaction->PipelineDepth();
if (pipelineDepth > 1) {
// if we have pipelines outstanding (not just an idle connection)
// then get a fairly quick tick
nextTickAfter = 1;
}
if (delta >= gHttpHandler->GetPipelineRescheduleTimeout() &&
pipelineDepth > 1) {
@ -1009,10 +1019,10 @@ nsHttpConnection::ReadTimeoutTick(PRIntervalTime now)
}
if (delta < gHttpHandler->GetPipelineTimeout())
return;
return nextTickAfter;
if (pipelineDepth <= 1 && !mTransaction->PipelinePosition())
return;
return nextTickAfter;
// nothing has transpired on this pipelined socket for many
// seconds. Call that a total stall and close the transaction.
@ -1027,6 +1037,7 @@ nsHttpConnection::ReadTimeoutTick(PRIntervalTime now)
// This will also close the connection
CloseTransaction(mTransaction, NS_ERROR_NET_TIMEOUT);
return UINT32_MAX;
}
void

View File

@ -142,8 +142,11 @@ public:
// authoritatively whether UsingSpdy() or not.
bool ReportedNPN() { return mReportedSpdy; }
// When the connection is active this is called every 1 second
void ReadTimeoutTick(PRIntervalTime now);
// When the connection is active this is called up to once every 1 second
// return the interval (in seconds) that the connection next wants to
// have this invoked. It might happen sooner depending on the needs of
// other connections.
uint32_t ReadTimeoutTick(PRIntervalTime now);
nsAHttpTransaction::Classifier Classification() { return mClassification; }
void Classify(nsAHttpTransaction::Classifier newclass)

View File

@ -66,6 +66,7 @@ nsHttpConnectionMgr::nsHttpConnectionMgr()
, mNumHalfOpenConns(0)
, mTimeOfNextWakeUp(UINT64_MAX)
, mTimeoutTickArmed(false)
, mTimeoutTickNext(1)
{
LOG(("Creating nsHttpConnectionMgr @%x\n", this));
}
@ -2439,8 +2440,14 @@ nsHttpConnectionMgr::ActivateTimeoutTick()
// Upon running the tick will rearm itself if there are active
// connections available.
if (mTimeoutTick && mTimeoutTickArmed)
if (mTimeoutTick && mTimeoutTickArmed) {
// make sure we get one iteration on a quick tick
if (mTimeoutTickNext > 1) {
mTimeoutTickNext = 1;
mTimeoutTick->SetDelay(1000);
}
return;
}
if (!mTimeoutTick) {
mTimeoutTick = do_CreateInstance(NS_TIMER_CONTRACTID);
@ -2462,10 +2469,16 @@ nsHttpConnectionMgr::TimeoutTick()
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
MOZ_ASSERT(mTimeoutTick, "no readtimeout tick");
LOG(("nsHttpConnectionMgr::TimeoutTick active=%d\n",
mNumActiveConns));
LOG(("nsHttpConnectionMgr::TimeoutTick active=%d\n", mNumActiveConns));
// The next tick will be between 1 second and 1 hr
// Set it to the max value here, and the TimeoutTickCB()s can
// reduce it to their local needs.
mTimeoutTickNext = 3600; // 1hr
mCT.Enumerate(TimeoutTickCB, this);
if (mTimeoutTick) {
mTimeoutTickNext = std::max(mTimeoutTickNext, 1U);
mTimeoutTick->SetDelay(mTimeoutTickNext * 1000);
}
}
PLDHashOperator
@ -2480,8 +2493,10 @@ nsHttpConnectionMgr::TimeoutTickCB(const nsACString &key,
// first call the tick handler for each active connection
PRIntervalTime now = PR_IntervalNow();
for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index)
ent->mActiveConns[index]->ReadTimeoutTick(now);
for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
uint32_t connNextTimeout = ent->mActiveConns[index]->ReadTimeoutTick(now);
self->mTimeoutTickNext = std::min(self->mTimeoutTickNext, connNextTimeout);
}
// now check for any stalled half open sockets
if (ent->mHalfOpens.Length()) {
@ -2513,7 +2528,9 @@ nsHttpConnectionMgr::TimeoutTickCB(const nsACString &key,
}
}
}
if (ent->mHalfOpens.Length()) {
self->mTimeoutTickNext = 1;
}
return PL_DHASH_NEXT;
}

View File

@ -629,6 +629,7 @@ private:
// Disabled when there are no active or half open connections.
nsCOMPtr<nsITimer> mTimeoutTick;
bool mTimeoutTickArmed;
uint32_t mTimeoutTickNext;
//
// the connection table