Bug 653530 - HTTP Transaction multiply dispatched r=honzab
authorPatrick McManus <mcmanus@ducksong.com>
Fri, 29 Apr 2011 15:32:40 -0400
changeset 68789 9b12c34434f4ce6011725864fe0eb04110afbcda
parent 68788 3bee3fefdec880516561e969da238a1b86be623f
child 68790 b8d612c740498ea6ceffff2f7ec2d243139109b1
push id19746
push usermcmanus@ducksong.com
push dateFri, 29 Apr 2011 21:11:24 +0000
treeherdermozilla-central@9b12c34434f4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershonzab
bugs653530
milestone6.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 653530 - HTTP Transaction multiply dispatched r=honzab
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/http/nsHttpConnectionMgr.h
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -556,17 +556,31 @@ nsHttpConnectionMgr::ProcessPendingQForE
 
     PRInt32 i, count = ent->mPendingQ.Length();
     if (count > 0) {
         LOG(("  pending-count=%u\n", count));
         nsHttpTransaction *trans = nsnull;
         nsHttpConnection *conn = nsnull;
         for (i=0; i<count; ++i) {
             trans = ent->mPendingQ[i];
-            GetConnection(ent, trans, &conn);
+
+            // When this transaction has already established a half-open
+            // connection, we want to prevent any duplicate half-open
+            // connections from being established and bound to this
+            // transaction. Allow only use of an idle persistent connection
+            // (if found) for transactions referred by a half-open connection.
+            PRBool alreadyHalfOpen = PR_FALSE;
+            for (PRInt32 j = 0; j < ((PRInt32) ent->mHalfOpens.Length()); j++) {
+                if (ent->mHalfOpens[j]->Transaction() == trans) {
+                    alreadyHalfOpen = PR_TRUE;
+                    break;
+                }
+            }
+
+            GetConnection(ent, trans, alreadyHalfOpen, &conn);
             if (conn)
                 break;
         }
         if (conn) {
             LOG(("  dispatching pending transaction...\n"));
 
             // remove pending transaction
             ent->mPendingQ.RemoveElementAt(i);
@@ -642,16 +656,17 @@ nsHttpConnectionMgr::AtActiveConnectionL
     // use >= just to be safe
     return (totalCount >= maxConns) || ( (caps & NS_HTTP_ALLOW_KEEPALIVE) &&
                                          (persistCount >= maxPersistConns) );
 }
 
 void
 nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent,
                                    nsHttpTransaction *trans,
+                                   PRBool onlyReusedConnection,
                                    nsHttpConnection **result)
 {
     LOG(("nsHttpConnectionMgr::GetConnection [ci=%s caps=%x]\n",
         ent->mConnInfo->HashKey().get(), PRUint32(trans->Caps())));
 
     // First, see if an idle persistent connection may be reused instead of
     // establishing a new socket. We do not need to check the connection limits
     // yet as they govern the maximum number of open connections and reusing
@@ -682,16 +697,22 @@ nsHttpConnectionMgr::GetConnection(nsCon
             // 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();
         }
     }
 
     if (!conn) {
+
+        // If the onlyReusedConnection parameter is TRUE, then GetConnection()
+        // does not create new transports under any circumstances.
+        if (onlyReusedConnection)
+            return;
+        
         // Check if we need to purge an idle connection. Note that we may have
         // removed one above; if so, this will be a no-op. We do this before
         // checking the active connection limit to catch the case where we do
         // have an idle connection, but the purge timer hasn't fired yet.
         // XXX this just purges a random idle connection.  we should instead
         // enumerate the entire hash table to find the eldest idle connection.
         if (mNumIdleConns && mNumIdleConns + mNumActiveConns + 1 >= mMaxConns)
             mCT.Enumerate(PurgeExcessIdleConnectionsCB, this);
@@ -886,17 +907,17 @@ nsHttpConnectionMgr::ProcessNewTransacti
     conn = wrappedConnection ? wrappedConnection->TakeHttpConnection() : nsnull;
 
     if (conn) {
         NS_ASSERTION(caps & NS_HTTP_STICKY_CONNECTION, "unexpected caps");
 
         trans->SetConnection(nsnull);
     }
     else
-        GetConnection(ent, trans, &conn);
+        GetConnection(ent, trans, PR_FALSE, &conn);
 
     nsresult rv;
     if (!conn) {
         LOG(("  adding transaction to pending queue [trans=%x pending-count=%u]\n",
             trans, ent->mPendingQ.Length()+1));
         // put this transaction on the pending queue...
         InsertTransactionSorted(ent->mPendingQ, trans);
         NS_ADDREF(trans);
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -207,16 +207,18 @@ private:
         nsresult SetupStreams(nsISocketTransport **,
                               nsIAsyncInputStream **,
                               nsIAsyncOutputStream **);
         nsresult SetupPrimaryStreams();
         nsresult SetupBackupStreams();
         void     SetupBackupTimer();
         void     Abandon();
         
+        nsHttpTransaction *Transaction() { return mTransaction; }
+
     private:
         nsConnectionEntry              *mEnt;
         nsRefPtr<nsHttpTransaction>    mTransaction;
         nsCOMPtr<nsISocketTransport>   mSocketTransport;
         nsCOMPtr<nsIAsyncOutputStream> mStreamOut;
         nsCOMPtr<nsIAsyncInputStream>  mStreamIn;
 
         // for syn retry
@@ -253,17 +255,17 @@ private:
     static PRIntn ProcessOneTransactionCB(nsHashKey *, void *, void *);
 
     static PRIntn PruneDeadConnectionsCB(nsHashKey *, void *, void *);
     static PRIntn ShutdownPassCB(nsHashKey *, void *, void *);
     static PRIntn PurgeExcessIdleConnectionsCB(nsHashKey *, void *, void *);
     PRBool   ProcessPendingQForEntry(nsConnectionEntry *);
     PRBool   AtActiveConnectionLimit(nsConnectionEntry *, PRUint8 caps);
     void     GetConnection(nsConnectionEntry *, nsHttpTransaction *,
-                           nsHttpConnection **);
+                           PRBool, nsHttpConnection **);
     nsresult DispatchTransaction(nsConnectionEntry *, nsAHttpTransaction *,
                                  PRUint8 caps, nsHttpConnection *);
     PRBool   BuildPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **);
     nsresult ProcessNewTransaction(nsHttpTransaction *);
     nsresult EnsureSocketThreadTargetIfOnline();
     nsresult CreateTransport(nsConnectionEntry *, nsHttpTransaction *);
     void     AddActiveConn(nsHttpConnection *, nsConnectionEntry *);
     void     StartedConnect();