bug 728113 - spdy ping to keepalive nat timeouts 1/2 tick infrastructure r=honzab
authorPatrick McManus <mcmanus@ducksong.com>
Thu, 23 Feb 2012 20:56:57 -0500
changeset 87601 7dd51d8f86410def1d7bfdc21b687327c79a500f
parent 87600 19cdcc58546010c88e20ce704364b09bd1fbd229
child 87602 00ace68863af0f80eb42aba33d06fd7231b073d5
push id22133
push usermak77@bonardo.net
push dateFri, 24 Feb 2012 10:23:30 +0000
treeherdermozilla-central@fbcdc2c87df8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershonzab
bugs728113
milestone13.0a1
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 728113 - spdy ping to keepalive nat timeouts 1/2 tick infrastructure r=honzab
netwerk/protocol/http/SpdySession.cpp
netwerk/protocol/http/SpdySession.h
netwerk/protocol/http/nsHttpConnection.cpp
netwerk/protocol/http/nsHttpConnection.h
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/http/nsHttpConnectionMgr.h
--- a/netwerk/protocol/http/SpdySession.cpp
+++ b/netwerk/protocol/http/SpdySession.cpp
@@ -207,16 +207,22 @@ bool
 SpdySession::RoomForMoreStreams()
 {
   if (mNextStreamID + mStreamTransactionHash.Count() * 2 > kMaxStreamID)
     return false;
 
   return !mShouldGoAway;
 }
 
+void
+SpdySession::ReadTimeoutTick(PRIntervalTime now)
+{
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+}
+
 PRUint32
 SpdySession::RegisterStreamID(SpdyStream *stream)
 {
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
   LOG3(("SpdySession::RegisterStreamID session=%p stream=%p id=0x%X "
         "concurrent=%d",this, stream, mNextStreamID, mConcurrent));
 
--- a/netwerk/protocol/http/SpdySession.h
+++ b/netwerk/protocol/http/SpdySession.h
@@ -72,16 +72,20 @@ public:
 
   SpdySession(nsAHttpTransaction *, nsISocketTransport *, PRInt32);
   ~SpdySession();
 
   bool AddStream(nsAHttpTransaction *, PRInt32);
   bool CanReuse() { return !mShouldGoAway && !mClosed; }
   void DontReuse();
   bool RoomForMoreStreams();
+
+  // When the connection is active this is called every 15 seconds
+  void  ReadTimeoutTick(PRIntervalTime now);
+
   PRUint32 RegisterStreamID(SpdyStream *);
 
   const static PRUint8 kFlag_Control   = 0x80;
 
   const static PRUint8 kFlag_Data_FIN  = 0x01;
   const static PRUint8 kFlag_Data_UNI  = 0x02;
   const static PRUint8 kFlag_Data_ZLIB = 0x02;
   
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -847,16 +847,36 @@ nsHttpConnection::TakeTransport(nsISocke
     mSocketTransport = nsnull;
     mSocketIn = nsnull;
     mSocketOut = nsnull;
     
     return NS_OK;
 }
 
 void
+nsHttpConnection::ReadTimeoutTick(PRIntervalTime now)
+{
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
+    // make sure timer didn't tick before Activate()
+    if (!mTransaction)
+        return;
+
+    // Spdy in the future actually should implement some timeout handling
+    // using the SPDY ping frame.
+    if (mSpdySession) {
+        mSpdySession->ReadTimeoutTick(now);
+        return;
+    }
+    
+    // Pending patches places pipeline rescheduling code will go here
+
+}
+
+void
 nsHttpConnection::GetSecurityInfo(nsISupports **secinfo)
 {
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     if (mSocketTransport) {
         if (NS_FAILED(mSocketTransport->GetSecurityInfo(secinfo)))
             *secinfo = nsnull;
     }
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -153,16 +153,19 @@ public:
     // connection pool, the nsHttpConnection still reads errors and hangups
     // on the socket so that it can be proactively released if the server
     // initiates a termination. Only call on socket thread.
     void BeginIdleMonitoring();
     void EndIdleMonitoring();
 
     bool UsingSpdy() { return mUsingSpdy; }
 
+    // When the connection is active this is called every 15 seconds
+    void  ReadTimeoutTick(PRIntervalTime now);
+
 private:
     // called to cause the underlying socket to start speaking SSL
     nsresult ProxyStartSSL();
 
     nsresult OnTransactionDone(nsresult reason);
     nsresult OnSocketWritable();
     nsresult OnSocketReadable();
 
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -90,26 +90,29 @@ nsHttpConnectionMgr::nsHttpConnectionMgr
     , mMaxConnsPerHost(0)
     , mMaxConnsPerProxy(0)
     , mMaxPersistConnsPerHost(0)
     , mMaxPersistConnsPerProxy(0)
     , mIsShuttingDown(false)
     , mNumActiveConns(0)
     , mNumIdleConns(0)
     , mTimeOfNextWakeUp(LL_MAXUINT)
+    , mReadTimeoutTickArmed(false)
 {
     LOG(("Creating nsHttpConnectionMgr @%x\n", this));
     mCT.Init();
     mAlternateProtocolHash.Init(16);
     mSpdyPreferredHash.Init();
 }
 
 nsHttpConnectionMgr::~nsHttpConnectionMgr()
 {
     LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
+    if (mReadTimeoutTick)
+        mReadTimeoutTick->Cancel();
 }
 
 nsresult
 nsHttpConnectionMgr::EnsureSocketThreadTargetIfOnline()
 {
     nsresult rv;
     nsCOMPtr<nsIEventTarget> sts;
     nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
@@ -258,23 +261,29 @@ nsHttpConnectionMgr::ConditionallyStopPr
 
 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
+    if (0 == strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
         nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
-        NS_ASSERTION(timer == mTimer, "unexpected timer-callback");
-#endif
+        if (timer == mTimer) {
+            PruneDeadConnections();
+        }
+        else if (timer == mReadTimeoutTick) {
+            ReadTimeoutTick();
+        }
+        else {
+            NS_ABORT_IF_FALSE(false, "unexpected timer-callback");
+            LOG(("Unexpected timer object\n"));
+            return NS_ERROR_UNEXPECTED;
+        }
     }
 
     return NS_OK;
 }
 
 
 //-----------------------------------------------------------------------------
 
@@ -1162,16 +1171,17 @@ nsHttpConnectionMgr::GetConnection(nsCon
 
 void
 nsHttpConnectionMgr::AddActiveConn(nsHttpConnection *conn,
                                    nsConnectionEntry *ent)
 {
     NS_ADDREF(conn);
     ent->mActiveConns.AppendElement(conn);
     mNumActiveConns++;
+    ActivateTimeoutTick();
 }
 
 void
 nsHttpConnectionMgr::StartedConnect()
 {
     mNumActiveConns++;
 }
 
@@ -1467,16 +1477,22 @@ nsHttpConnectionMgr::GetSpdyPreferredCon
 
 void
 nsHttpConnectionMgr::OnMsgShutdown(PRInt32, void *)
 {
     LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
 
     mCT.Enumerate(ShutdownPassCB, this);
 
+    if (mReadTimeoutTick) {
+        mReadTimeoutTick->Cancel();
+        mReadTimeoutTick = nsnull;
+        mReadTimeoutTickArmed = false;
+    }
+    
     // signal shutdown complete
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     mon.Notify();
 }
 
 void
 nsHttpConnectionMgr::OnMsgNewTransaction(PRInt32 priority, void *param)
 {
@@ -1706,16 +1722,87 @@ nsHttpConnectionMgr::OnMsgUpdateParam(PR
 nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
 {
     if (mSpdyPreferred)
         gHttpHandler->ConnMgr()->RemoveSpdyPreferredEnt(mCoalescingKey);
 
     NS_RELEASE(mConnInfo);
 }
 
+// Read Timeout Tick handlers
+
+void
+nsHttpConnectionMgr::ActivateTimeoutTick()
+{
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+    LOG(("nsHttpConnectionMgr::ActivateTimeoutTick() "
+         "this=%p mReadTimeoutTick=%p\n"));
+
+    // right now the spdy timeout code is the only thing hooked to the timeout
+    // tick, so disable it if spdy is not being used. However pipelining code
+    // will also want this functionality soon.
+    if (!gHttpHandler->IsSpdyEnabled())
+        return;
+
+    // The timer tick should be enabled if it is not already pending.
+    // Upon running the tick will rearm itself if there are active
+    // connections available.
+
+    if (mReadTimeoutTick && mReadTimeoutTickArmed)
+        return;
+
+    if (!mReadTimeoutTick) {
+        mReadTimeoutTick = do_CreateInstance(NS_TIMER_CONTRACTID);
+        if (!mReadTimeoutTick) {
+            NS_WARNING("failed to create timer for http timeout management");
+            return;
+        }
+    }
+
+    NS_ABORT_IF_FALSE(!mReadTimeoutTickArmed, "timer tick armed");
+    mReadTimeoutTickArmed = true;
+    // pipeline will expect a 1000ms granuality
+    mReadTimeoutTick->Init(this, 15000, nsITimer::TYPE_REPEATING_SLACK);
+}
+
+void
+nsHttpConnectionMgr::ReadTimeoutTick()
+{
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+    NS_ABORT_IF_FALSE(mReadTimeoutTick, "no readtimeout tick");
+
+    LOG(("nsHttpConnectionMgr::ReadTimeoutTick active=%d\n",
+         mNumActiveConns));
+
+    if (!mNumActiveConns && mReadTimeoutTickArmed) {
+        mReadTimeoutTick->Cancel();
+        mReadTimeoutTickArmed = false;
+        return;
+    }
+
+    mCT.Enumerate(ReadTimeoutTickCB, this);
+}
+
+PLDHashOperator
+nsHttpConnectionMgr::ReadTimeoutTickCB(const nsACString &key,
+                                       nsAutoPtr<nsConnectionEntry> &ent,
+                                       void *closure)
+{
+    nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
+
+    LOG(("nsHttpConnectionMgr::ReadTimeoutTickCB() this=%p host=%s\n",
+         self, ent->mConnInfo->Host()));
+
+    PRIntervalTime now = PR_IntervalNow();
+    for (PRUint32 index = 0; index < ent->mActiveConns.Length(); ++index)
+        ent->mActiveConns[index]->ReadTimeoutTick(now);
+
+    return PL_DHASH_NEXT;
+}
+
 //-----------------------------------------------------------------------------
 // nsHttpConnectionMgr::nsConnectionHandle
 
 nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
 {
     if (mConn) {
         gHttpHandler->ReclaimConnection(mConn);
         NS_RELEASE(mConn);
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -405,25 +405,37 @@ private:
     // that are accessed from mCT connection table.
     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;
 
+    // A 1s tick to call nsHttpConnection::ReadTimeoutTick on
+    // active http/1 connections. Disabled when there are no
+    // active connections.
+    nsCOMPtr<nsITimer> mReadTimeoutTick;
+    bool mReadTimeoutTickArmed;
+
     //
     // the connection table
     //
     // this table is indexed by connection key.  each entry is a
     // nsConnectionEntry object.
     //
     nsClassHashtable<nsCStringHashKey, nsConnectionEntry> mCT;
 
     // mAlternateProtocolHash is used only for spdy/2 upgrades for now
     nsCStringHashSet mAlternateProtocolHash; // protected by the monitor
     static PLDHashOperator TrimAlternateProtocolHash(PLDHashTable *table,
                                                      PLDHashEntryHdr *hdr,
                                                      PRUint32 number,
                                                      void *closure);
+    // Read Timeout Tick handlers
+    void ActivateTimeoutTick();
+    void ReadTimeoutTick();
+    static PLDHashOperator ReadTimeoutTickCB(const nsACString &key,
+                                             nsAutoPtr<nsConnectionEntry> &ent,
+                                             void *closure);
 };
 
 #endif // !nsHttpConnectionMgr_h__