Limit total number of sockets in the system.

Based on Eric Roman's patch at http://codereview.chromium.org/62181

http://crbug.com/15093

Review URL: http://codereview.chromium.org/149027

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21276 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/socket/client_socket_pool_base.h b/net/socket/client_socket_pool_base.h
index fc3582d..36281b4 100644
--- a/net/socket/client_socket_pool_base.h
+++ b/net/socket/client_socket_pool_base.h
@@ -127,7 +127,8 @@
     DISALLOW_COPY_AND_ASSIGN(ConnectJobFactory);
   };
 
-  ClientSocketPoolBase(int max_sockets_per_group,
+  ClientSocketPoolBase(int max_sockets,
+                       int max_sockets_per_group,
                        ConnectJobFactory* connect_job_factory);
 
   ~ClientSocketPoolBase();
@@ -150,10 +151,6 @@
     return idle_socket_count_;
   }
 
-  int max_sockets_per_group() const {
-    return max_sockets_per_group_;
-  }
-
   int IdleSocketCountInGroup(const std::string& group_name) const;
 
   LoadState GetLoadState(const std::string& group_name,
@@ -169,6 +166,9 @@
   // afterward and receive the socket from the job).
   static void EnableLateBindingOfSockets(bool enabled);
 
+  // For testing.
+  bool may_have_stalled_group() const { return may_have_stalled_group_; }
+
  private:
   // Entry for a persistent socket which became idle at time |start_time|.
   struct IdleSocket {
@@ -204,6 +204,10 @@
           max_sockets_per_group;
     }
 
+    int TopPendingPriority() const {
+      return pending_requests.front().priority;
+    }
+
     std::deque<IdleSocket> idle_sockets;
     std::set<const ConnectJob*> jobs;
     RequestQueue pending_requests;
@@ -230,6 +234,12 @@
   // Called via PostTask by ReleaseSocket.
   void DoReleaseSocket(const std::string& group_name, ClientSocket* socket);
 
+  // Scans the group map for groups which have an available socket slot and
+  // at least one pending request. Returns number of groups found, and if found
+  // at least one, fills |group| and |group_name| with data of the stalled group
+  // having highest priority.
+  int FindTopStalledGroup(Group** group, std::string* group_name);
+
   // Called when timer_ fires.  This method scans the idle sockets removing
   // sockets that timed out or can't be reused.
   void OnCleanupTimerFired() {
@@ -269,6 +279,10 @@
   // longer needed.
   void CancelAllConnectJobs();
 
+  // Returns true if we can't create any more sockets due to the total limit.
+  // TODO(phajdan.jr): Also take idle sockets into account.
+  bool ReachedMaxSocketsLimit() const;
+
   GroupMap group_map_;
 
   ConnectJobMap connect_job_map_;
@@ -280,9 +294,34 @@
   // The total number of idle sockets in the system.
   int idle_socket_count_;
 
+  // Number of connecting sockets across all groups.
+  int connecting_socket_count_;
+
+  // Number of connected sockets we handed out across all groups.
+  int handed_out_socket_count_;
+
+  // The maximum total number of sockets. See ReachedMaxSocketsLimit.
+  const int max_sockets_;
+
   // The maximum number of sockets kept per group.
   const int max_sockets_per_group_;
 
+  // Until the maximum number of sockets limit is reached, a group can only
+  // have pending requests if it exceeds the "max sockets per group" limit.
+  //
+  // This means when a socket is released, the only pending requests that can
+  // be started next belong to the same group.
+  //
+  // However once the |max_sockets_| limit is reached, this stops being true:
+  // groups can now have pending requests without having first reached the
+  // |max_sockets_per_group_| limit. So choosing the next request involves
+  // selecting the highest priority request across *all* groups.
+  //
+  // Since reaching the maximum number of sockets is an edge case, we make note
+  // of when it happens, and thus avoid doing the slower "scan all groups"
+  // in the common case.
+  bool may_have_stalled_group_;
+
   const scoped_ptr<ConnectJobFactory> connect_job_factory_;
 
   // Controls whether or not we use late binding of sockets.