Home | History | Annotate | Download | only in socket
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/socket/client_socket_pool_base.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/format_macros.h"
      9 #include "base/logging.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/metrics/stats_counters.h"
     12 #include "base/stl_util.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/time/time.h"
     15 #include "base/values.h"
     16 #include "net/base/net_errors.h"
     17 #include "net/base/net_log.h"
     18 
     19 using base::TimeDelta;
     20 
     21 namespace net {
     22 
     23 namespace {
     24 
     25 // Indicate whether we should enable idle socket cleanup timer. When timer is
     26 // disabled, sockets are closed next time a socket request is made.
     27 bool g_cleanup_timer_enabled = true;
     28 
     29 // The timeout value, in seconds, used to clean up idle sockets that can't be
     30 // reused.
     31 //
     32 // Note: It's important to close idle sockets that have received data as soon
     33 // as possible because the received data may cause BSOD on Windows XP under
     34 // some conditions.  See http://crbug.com/4606.
     35 const int kCleanupInterval = 10;  // DO NOT INCREASE THIS TIMEOUT.
     36 
     37 // Indicate whether or not we should establish a new transport layer connection
     38 // after a certain timeout has passed without receiving an ACK.
     39 bool g_connect_backup_jobs_enabled = true;
     40 
     41 }  // namespace
     42 
     43 ConnectJob::ConnectJob(const std::string& group_name,
     44                        base::TimeDelta timeout_duration,
     45                        RequestPriority priority,
     46                        Delegate* delegate,
     47                        const BoundNetLog& net_log)
     48     : group_name_(group_name),
     49       timeout_duration_(timeout_duration),
     50       priority_(priority),
     51       delegate_(delegate),
     52       net_log_(net_log),
     53       idle_(true) {
     54   DCHECK(!group_name.empty());
     55   DCHECK(delegate);
     56   net_log.BeginEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB,
     57                      NetLog::StringCallback("group_name", &group_name_));
     58 }
     59 
     60 ConnectJob::~ConnectJob() {
     61   net_log().EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB);
     62 }
     63 
     64 scoped_ptr<StreamSocket> ConnectJob::PassSocket() {
     65   return socket_.Pass();
     66 }
     67 
     68 int ConnectJob::Connect() {
     69   if (timeout_duration_ != base::TimeDelta())
     70     timer_.Start(FROM_HERE, timeout_duration_, this, &ConnectJob::OnTimeout);
     71 
     72   idle_ = false;
     73 
     74   LogConnectStart();
     75 
     76   int rv = ConnectInternal();
     77 
     78   if (rv != ERR_IO_PENDING) {
     79     LogConnectCompletion(rv);
     80     delegate_ = NULL;
     81   }
     82 
     83   return rv;
     84 }
     85 
     86 void ConnectJob::SetSocket(scoped_ptr<StreamSocket> socket) {
     87   if (socket) {
     88     net_log().AddEvent(NetLog::TYPE_CONNECT_JOB_SET_SOCKET,
     89                        socket->NetLog().source().ToEventParametersCallback());
     90   }
     91   socket_ = socket.Pass();
     92 }
     93 
     94 void ConnectJob::NotifyDelegateOfCompletion(int rv) {
     95   // The delegate will own |this|.
     96   Delegate* delegate = delegate_;
     97   delegate_ = NULL;
     98 
     99   LogConnectCompletion(rv);
    100   delegate->OnConnectJobComplete(rv, this);
    101 }
    102 
    103 void ConnectJob::ResetTimer(base::TimeDelta remaining_time) {
    104   timer_.Stop();
    105   timer_.Start(FROM_HERE, remaining_time, this, &ConnectJob::OnTimeout);
    106 }
    107 
    108 void ConnectJob::LogConnectStart() {
    109   connect_timing_.connect_start = base::TimeTicks::Now();
    110   net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_CONNECT);
    111 }
    112 
    113 void ConnectJob::LogConnectCompletion(int net_error) {
    114   connect_timing_.connect_end = base::TimeTicks::Now();
    115   net_log().EndEventWithNetErrorCode(
    116       NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_CONNECT, net_error);
    117 }
    118 
    119 void ConnectJob::OnTimeout() {
    120   // Make sure the socket is NULL before calling into |delegate|.
    121   SetSocket(scoped_ptr<StreamSocket>());
    122 
    123   net_log_.AddEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_TIMED_OUT);
    124 
    125   NotifyDelegateOfCompletion(ERR_TIMED_OUT);
    126 }
    127 
    128 namespace internal {
    129 
    130 ClientSocketPoolBaseHelper::Request::Request(
    131     ClientSocketHandle* handle,
    132     const CompletionCallback& callback,
    133     RequestPriority priority,
    134     bool ignore_limits,
    135     Flags flags,
    136     const BoundNetLog& net_log)
    137     : handle_(handle),
    138       callback_(callback),
    139       priority_(priority),
    140       ignore_limits_(ignore_limits),
    141       flags_(flags),
    142       net_log_(net_log) {
    143   if (ignore_limits_)
    144     DCHECK_EQ(priority_, MAXIMUM_PRIORITY);
    145 }
    146 
    147 ClientSocketPoolBaseHelper::Request::~Request() {}
    148 
    149 ClientSocketPoolBaseHelper::ClientSocketPoolBaseHelper(
    150     HigherLayeredPool* pool,
    151     int max_sockets,
    152     int max_sockets_per_group,
    153     base::TimeDelta unused_idle_socket_timeout,
    154     base::TimeDelta used_idle_socket_timeout,
    155     ConnectJobFactory* connect_job_factory)
    156     : idle_socket_count_(0),
    157       connecting_socket_count_(0),
    158       handed_out_socket_count_(0),
    159       max_sockets_(max_sockets),
    160       max_sockets_per_group_(max_sockets_per_group),
    161       use_cleanup_timer_(g_cleanup_timer_enabled),
    162       unused_idle_socket_timeout_(unused_idle_socket_timeout),
    163       used_idle_socket_timeout_(used_idle_socket_timeout),
    164       connect_job_factory_(connect_job_factory),
    165       connect_backup_jobs_enabled_(false),
    166       pool_generation_number_(0),
    167       pool_(pool),
    168       weak_factory_(this) {
    169   DCHECK_LE(0, max_sockets_per_group);
    170   DCHECK_LE(max_sockets_per_group, max_sockets);
    171 
    172   NetworkChangeNotifier::AddIPAddressObserver(this);
    173 }
    174 
    175 ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() {
    176   // Clean up any idle sockets and pending connect jobs.  Assert that we have no
    177   // remaining active sockets or pending requests.  They should have all been
    178   // cleaned up prior to |this| being destroyed.
    179   FlushWithError(ERR_ABORTED);
    180   DCHECK(group_map_.empty());
    181   DCHECK(pending_callback_map_.empty());
    182   DCHECK_EQ(0, connecting_socket_count_);
    183   CHECK(higher_pools_.empty());
    184 
    185   NetworkChangeNotifier::RemoveIPAddressObserver(this);
    186 
    187   // Remove from lower layer pools.
    188   for (std::set<LowerLayeredPool*>::iterator it = lower_pools_.begin();
    189        it != lower_pools_.end();
    190        ++it) {
    191     (*it)->RemoveHigherLayeredPool(pool_);
    192   }
    193 }
    194 
    195 ClientSocketPoolBaseHelper::CallbackResultPair::CallbackResultPair()
    196     : result(OK) {
    197 }
    198 
    199 ClientSocketPoolBaseHelper::CallbackResultPair::CallbackResultPair(
    200     const CompletionCallback& callback_in, int result_in)
    201     : callback(callback_in),
    202       result(result_in) {
    203 }
    204 
    205 ClientSocketPoolBaseHelper::CallbackResultPair::~CallbackResultPair() {}
    206 
    207 bool ClientSocketPoolBaseHelper::IsStalled() const {
    208   // If a lower layer pool is stalled, consider |this| stalled as well.
    209   for (std::set<LowerLayeredPool*>::const_iterator it = lower_pools_.begin();
    210        it != lower_pools_.end();
    211        ++it) {
    212     if ((*it)->IsStalled())
    213       return true;
    214   }
    215 
    216   // If fewer than |max_sockets_| are in use, then clearly |this| is not
    217   // stalled.
    218   if ((handed_out_socket_count_ + connecting_socket_count_) < max_sockets_)
    219     return false;
    220   // So in order to be stalled, |this| must be using at least |max_sockets_| AND
    221   // |this| must have a request that is actually stalled on the global socket
    222   // limit.  To find such a request, look for a group that has more requests
    223   // than jobs AND where the number of sockets is less than
    224   // |max_sockets_per_group_|.  (If the number of sockets is equal to
    225   // |max_sockets_per_group_|, then the request is stalled on the group limit,
    226   // which does not count.)
    227   for (GroupMap::const_iterator it = group_map_.begin();
    228        it != group_map_.end(); ++it) {
    229     if (it->second->IsStalledOnPoolMaxSockets(max_sockets_per_group_))
    230       return true;
    231   }
    232   return false;
    233 }
    234 
    235 void ClientSocketPoolBaseHelper::AddLowerLayeredPool(
    236     LowerLayeredPool* lower_pool) {
    237   DCHECK(pool_);
    238   CHECK(!ContainsKey(lower_pools_, lower_pool));
    239   lower_pools_.insert(lower_pool);
    240   lower_pool->AddHigherLayeredPool(pool_);
    241 }
    242 
    243 void ClientSocketPoolBaseHelper::AddHigherLayeredPool(
    244     HigherLayeredPool* higher_pool) {
    245   CHECK(higher_pool);
    246   CHECK(!ContainsKey(higher_pools_, higher_pool));
    247   higher_pools_.insert(higher_pool);
    248 }
    249 
    250 void ClientSocketPoolBaseHelper::RemoveHigherLayeredPool(
    251     HigherLayeredPool* higher_pool) {
    252   CHECK(higher_pool);
    253   CHECK(ContainsKey(higher_pools_, higher_pool));
    254   higher_pools_.erase(higher_pool);
    255 }
    256 
    257 int ClientSocketPoolBaseHelper::RequestSocket(
    258     const std::string& group_name,
    259     scoped_ptr<const Request> request) {
    260   CHECK(!request->callback().is_null());
    261   CHECK(request->handle());
    262 
    263   // Cleanup any timed-out idle sockets if no timer is used.
    264   if (!use_cleanup_timer_)
    265     CleanupIdleSockets(false);
    266 
    267   request->net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL);
    268   Group* group = GetOrCreateGroup(group_name);
    269 
    270   int rv = RequestSocketInternal(group_name, *request);
    271   if (rv != ERR_IO_PENDING) {
    272     request->net_log().EndEventWithNetErrorCode(NetLog::TYPE_SOCKET_POOL, rv);
    273     CHECK(!request->handle()->is_initialized());
    274     request.reset();
    275   } else {
    276     group->InsertPendingRequest(request.Pass());
    277     // Have to do this asynchronously, as closing sockets in higher level pools
    278     // call back in to |this|, which will cause all sorts of fun and exciting
    279     // re-entrancy issues if the socket pool is doing something else at the
    280     // time.
    281     if (group->IsStalledOnPoolMaxSockets(max_sockets_per_group_)) {
    282       base::MessageLoop::current()->PostTask(
    283           FROM_HERE,
    284           base::Bind(
    285               &ClientSocketPoolBaseHelper::TryToCloseSocketsInLayeredPools,
    286               weak_factory_.GetWeakPtr()));
    287     }
    288   }
    289   return rv;
    290 }
    291 
    292 void ClientSocketPoolBaseHelper::RequestSockets(
    293     const std::string& group_name,
    294     const Request& request,
    295     int num_sockets) {
    296   DCHECK(request.callback().is_null());
    297   DCHECK(!request.handle());
    298 
    299   // Cleanup any timed out idle sockets if no timer is used.
    300   if (!use_cleanup_timer_)
    301     CleanupIdleSockets(false);
    302 
    303   if (num_sockets > max_sockets_per_group_) {
    304     num_sockets = max_sockets_per_group_;
    305   }
    306 
    307   request.net_log().BeginEvent(
    308       NetLog::TYPE_SOCKET_POOL_CONNECTING_N_SOCKETS,
    309       NetLog::IntegerCallback("num_sockets", num_sockets));
    310 
    311   Group* group = GetOrCreateGroup(group_name);
    312 
    313   // RequestSocketsInternal() may delete the group.
    314   bool deleted_group = false;
    315 
    316   int rv = OK;
    317   for (int num_iterations_left = num_sockets;
    318        group->NumActiveSocketSlots() < num_sockets &&
    319        num_iterations_left > 0 ; num_iterations_left--) {
    320     rv = RequestSocketInternal(group_name, request);
    321     if (rv < 0 && rv != ERR_IO_PENDING) {
    322       // We're encountering a synchronous error.  Give up.
    323       if (!ContainsKey(group_map_, group_name))
    324         deleted_group = true;
    325       break;
    326     }
    327     if (!ContainsKey(group_map_, group_name)) {
    328       // Unexpected.  The group should only be getting deleted on synchronous
    329       // error.
    330       NOTREACHED();
    331       deleted_group = true;
    332       break;
    333     }
    334   }
    335 
    336   if (!deleted_group && group->IsEmpty())
    337     RemoveGroup(group_name);
    338 
    339   if (rv == ERR_IO_PENDING)
    340     rv = OK;
    341   request.net_log().EndEventWithNetErrorCode(
    342       NetLog::TYPE_SOCKET_POOL_CONNECTING_N_SOCKETS, rv);
    343 }
    344 
    345 int ClientSocketPoolBaseHelper::RequestSocketInternal(
    346     const std::string& group_name,
    347     const Request& request) {
    348   ClientSocketHandle* const handle = request.handle();
    349   const bool preconnecting = !handle;
    350   Group* group = GetOrCreateGroup(group_name);
    351 
    352   if (!(request.flags() & NO_IDLE_SOCKETS)) {
    353     // Try to reuse a socket.
    354     if (AssignIdleSocketToRequest(request, group))
    355       return OK;
    356   }
    357 
    358   // If there are more ConnectJobs than pending requests, don't need to do
    359   // anything.  Can just wait for the extra job to connect, and then assign it
    360   // to the request.
    361   if (!preconnecting && group->TryToUseUnassignedConnectJob())
    362     return ERR_IO_PENDING;
    363 
    364   // Can we make another active socket now?
    365   if (!group->HasAvailableSocketSlot(max_sockets_per_group_) &&
    366       !request.ignore_limits()) {
    367     // TODO(willchan): Consider whether or not we need to close a socket in a
    368     // higher layered group. I don't think this makes sense since we would just
    369     // reuse that socket then if we needed one and wouldn't make it down to this
    370     // layer.
    371     request.net_log().AddEvent(
    372         NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP);
    373     return ERR_IO_PENDING;
    374   }
    375 
    376   if (ReachedMaxSocketsLimit() && !request.ignore_limits()) {
    377     // NOTE(mmenke):  Wonder if we really need different code for each case
    378     // here.  Only reason for them now seems to be preconnects.
    379     if (idle_socket_count() > 0) {
    380       // There's an idle socket in this pool. Either that's because there's
    381       // still one in this group, but we got here due to preconnecting bypassing
    382       // idle sockets, or because there's an idle socket in another group.
    383       bool closed = CloseOneIdleSocketExceptInGroup(group);
    384       if (preconnecting && !closed)
    385         return ERR_PRECONNECT_MAX_SOCKET_LIMIT;
    386     } else {
    387       // We could check if we really have a stalled group here, but it requires
    388       // a scan of all groups, so just flip a flag here, and do the check later.
    389       request.net_log().AddEvent(NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS);
    390       return ERR_IO_PENDING;
    391     }
    392   }
    393 
    394   // We couldn't find a socket to reuse, and there's space to allocate one,
    395   // so allocate and connect a new one.
    396   scoped_ptr<ConnectJob> connect_job(
    397       connect_job_factory_->NewConnectJob(group_name, request, this));
    398 
    399   int rv = connect_job->Connect();
    400   if (rv == OK) {
    401     LogBoundConnectJobToRequest(connect_job->net_log().source(), request);
    402     if (!preconnecting) {
    403       HandOutSocket(connect_job->PassSocket(), ClientSocketHandle::UNUSED,
    404                     connect_job->connect_timing(), handle, base::TimeDelta(),
    405                     group, request.net_log());
    406     } else {
    407       AddIdleSocket(connect_job->PassSocket(), group);
    408     }
    409   } else if (rv == ERR_IO_PENDING) {
    410     // If we don't have any sockets in this group, set a timer for potentially
    411     // creating a new one.  If the SYN is lost, this backup socket may complete
    412     // before the slow socket, improving end user latency.
    413     if (connect_backup_jobs_enabled_ && group->IsEmpty()) {
    414       group->StartBackupJobTimer(group_name, this);
    415     }
    416 
    417     connecting_socket_count_++;
    418 
    419     group->AddJob(connect_job.Pass(), preconnecting);
    420   } else {
    421     LogBoundConnectJobToRequest(connect_job->net_log().source(), request);
    422     scoped_ptr<StreamSocket> error_socket;
    423     if (!preconnecting) {
    424       DCHECK(handle);
    425       connect_job->GetAdditionalErrorState(handle);
    426       error_socket = connect_job->PassSocket();
    427     }
    428     if (error_socket) {
    429       HandOutSocket(error_socket.Pass(), ClientSocketHandle::UNUSED,
    430                     connect_job->connect_timing(), handle, base::TimeDelta(),
    431                     group, request.net_log());
    432     } else if (group->IsEmpty()) {
    433       RemoveGroup(group_name);
    434     }
    435   }
    436 
    437   return rv;
    438 }
    439 
    440 bool ClientSocketPoolBaseHelper::AssignIdleSocketToRequest(
    441     const Request& request, Group* group) {
    442   std::list<IdleSocket>* idle_sockets = group->mutable_idle_sockets();
    443   std::list<IdleSocket>::iterator idle_socket_it = idle_sockets->end();
    444 
    445   // Iterate through the idle sockets forwards (oldest to newest)
    446   //   * Delete any disconnected ones.
    447   //   * If we find a used idle socket, assign to |idle_socket|.  At the end,
    448   //   the |idle_socket_it| will be set to the newest used idle socket.
    449   for (std::list<IdleSocket>::iterator it = idle_sockets->begin();
    450        it != idle_sockets->end();) {
    451     if (!it->IsUsable()) {
    452       DecrementIdleCount();
    453       delete it->socket;
    454       it = idle_sockets->erase(it);
    455       continue;
    456     }
    457 
    458     if (it->socket->WasEverUsed()) {
    459       // We found one we can reuse!
    460       idle_socket_it = it;
    461     }
    462 
    463     ++it;
    464   }
    465 
    466   // If we haven't found an idle socket, that means there are no used idle
    467   // sockets.  Pick the oldest (first) idle socket (FIFO).
    468 
    469   if (idle_socket_it == idle_sockets->end() && !idle_sockets->empty())
    470     idle_socket_it = idle_sockets->begin();
    471 
    472   if (idle_socket_it != idle_sockets->end()) {
    473     DecrementIdleCount();
    474     base::TimeDelta idle_time =
    475         base::TimeTicks::Now() - idle_socket_it->start_time;
    476     IdleSocket idle_socket = *idle_socket_it;
    477     idle_sockets->erase(idle_socket_it);
    478     // TODO(davidben): If |idle_time| is under some low watermark, consider
    479     // treating as UNUSED rather than UNUSED_IDLE. This will avoid
    480     // HttpNetworkTransaction retrying on some errors.
    481     ClientSocketHandle::SocketReuseType reuse_type =
    482         idle_socket.socket->WasEverUsed() ?
    483             ClientSocketHandle::REUSED_IDLE :
    484             ClientSocketHandle::UNUSED_IDLE;
    485     HandOutSocket(
    486         scoped_ptr<StreamSocket>(idle_socket.socket),
    487         reuse_type,
    488         LoadTimingInfo::ConnectTiming(),
    489         request.handle(),
    490         idle_time,
    491         group,
    492         request.net_log());
    493     return true;
    494   }
    495 
    496   return false;
    497 }
    498 
    499 // static
    500 void ClientSocketPoolBaseHelper::LogBoundConnectJobToRequest(
    501     const NetLog::Source& connect_job_source, const Request& request) {
    502   request.net_log().AddEvent(NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB,
    503                              connect_job_source.ToEventParametersCallback());
    504 }
    505 
    506 void ClientSocketPoolBaseHelper::CancelRequest(
    507     const std::string& group_name, ClientSocketHandle* handle) {
    508   PendingCallbackMap::iterator callback_it = pending_callback_map_.find(handle);
    509   if (callback_it != pending_callback_map_.end()) {
    510     int result = callback_it->second.result;
    511     pending_callback_map_.erase(callback_it);
    512     scoped_ptr<StreamSocket> socket = handle->PassSocket();
    513     if (socket) {
    514       if (result != OK)
    515         socket->Disconnect();
    516       ReleaseSocket(handle->group_name(), socket.Pass(), handle->id());
    517     }
    518     return;
    519   }
    520 
    521   CHECK(ContainsKey(group_map_, group_name));
    522 
    523   Group* group = GetOrCreateGroup(group_name);
    524 
    525   // Search pending_requests for matching handle.
    526   scoped_ptr<const Request> request =
    527       group->FindAndRemovePendingRequest(handle);
    528   if (request) {
    529     request->net_log().AddEvent(NetLog::TYPE_CANCELLED);
    530     request->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
    531 
    532     // We let the job run, unless we're at the socket limit and there is
    533     // not another request waiting on the job.
    534     if (group->jobs().size() > group->pending_request_count() &&
    535         ReachedMaxSocketsLimit()) {
    536       RemoveConnectJob(*group->jobs().begin(), group);
    537       CheckForStalledSocketGroups();
    538     }
    539   }
    540 }
    541 
    542 bool ClientSocketPoolBaseHelper::HasGroup(const std::string& group_name) const {
    543   return ContainsKey(group_map_, group_name);
    544 }
    545 
    546 void ClientSocketPoolBaseHelper::CloseIdleSockets() {
    547   CleanupIdleSockets(true);
    548   DCHECK_EQ(0, idle_socket_count_);
    549 }
    550 
    551 int ClientSocketPoolBaseHelper::IdleSocketCountInGroup(
    552     const std::string& group_name) const {
    553   GroupMap::const_iterator i = group_map_.find(group_name);
    554   CHECK(i != group_map_.end());
    555 
    556   return i->second->idle_sockets().size();
    557 }
    558 
    559 LoadState ClientSocketPoolBaseHelper::GetLoadState(
    560     const std::string& group_name,
    561     const ClientSocketHandle* handle) const {
    562   if (ContainsKey(pending_callback_map_, handle))
    563     return LOAD_STATE_CONNECTING;
    564 
    565   if (!ContainsKey(group_map_, group_name)) {
    566     NOTREACHED() << "ClientSocketPool does not contain group: " << group_name
    567                  << " for handle: " << handle;
    568     return LOAD_STATE_IDLE;
    569   }
    570 
    571   // Can't use operator[] since it is non-const.
    572   const Group& group = *group_map_.find(group_name)->second;
    573 
    574   if (group.HasConnectJobForHandle(handle)) {
    575     // Just return the state  of the farthest along ConnectJob for the first
    576     // group.jobs().size() pending requests.
    577     LoadState max_state = LOAD_STATE_IDLE;
    578     for (ConnectJobSet::const_iterator job_it = group.jobs().begin();
    579          job_it != group.jobs().end(); ++job_it) {
    580       max_state = std::max(max_state, (*job_it)->GetLoadState());
    581     }
    582     return max_state;
    583   }
    584 
    585   if (group.IsStalledOnPoolMaxSockets(max_sockets_per_group_))
    586     return LOAD_STATE_WAITING_FOR_STALLED_SOCKET_POOL;
    587   return LOAD_STATE_WAITING_FOR_AVAILABLE_SOCKET;
    588 }
    589 
    590 base::DictionaryValue* ClientSocketPoolBaseHelper::GetInfoAsValue(
    591     const std::string& name, const std::string& type) const {
    592   base::DictionaryValue* dict = new base::DictionaryValue();
    593   dict->SetString("name", name);
    594   dict->SetString("type", type);
    595   dict->SetInteger("handed_out_socket_count", handed_out_socket_count_);
    596   dict->SetInteger("connecting_socket_count", connecting_socket_count_);
    597   dict->SetInteger("idle_socket_count", idle_socket_count_);
    598   dict->SetInteger("max_socket_count", max_sockets_);
    599   dict->SetInteger("max_sockets_per_group", max_sockets_per_group_);
    600   dict->SetInteger("pool_generation_number", pool_generation_number_);
    601 
    602   if (group_map_.empty())
    603     return dict;
    604 
    605   base::DictionaryValue* all_groups_dict = new base::DictionaryValue();
    606   for (GroupMap::const_iterator it = group_map_.begin();
    607        it != group_map_.end(); it++) {
    608     const Group* group = it->second;
    609     base::DictionaryValue* group_dict = new base::DictionaryValue();
    610 
    611     group_dict->SetInteger("pending_request_count",
    612                            group->pending_request_count());
    613     if (group->has_pending_requests()) {
    614       group_dict->SetString(
    615           "top_pending_priority",
    616           RequestPriorityToString(group->TopPendingPriority()));
    617     }
    618 
    619     group_dict->SetInteger("active_socket_count", group->active_socket_count());
    620 
    621     base::ListValue* idle_socket_list = new base::ListValue();
    622     std::list<IdleSocket>::const_iterator idle_socket;
    623     for (idle_socket = group->idle_sockets().begin();
    624          idle_socket != group->idle_sockets().end();
    625          idle_socket++) {
    626       int source_id = idle_socket->socket->NetLog().source().id;
    627       idle_socket_list->Append(new base::FundamentalValue(source_id));
    628     }
    629     group_dict->Set("idle_sockets", idle_socket_list);
    630 
    631     base::ListValue* connect_jobs_list = new base::ListValue();
    632     std::set<ConnectJob*>::const_iterator job = group->jobs().begin();
    633     for (job = group->jobs().begin(); job != group->jobs().end(); job++) {
    634       int source_id = (*job)->net_log().source().id;
    635       connect_jobs_list->Append(new base::FundamentalValue(source_id));
    636     }
    637     group_dict->Set("connect_jobs", connect_jobs_list);
    638 
    639     group_dict->SetBoolean("is_stalled",
    640                            group->IsStalledOnPoolMaxSockets(
    641                                max_sockets_per_group_));
    642     group_dict->SetBoolean("backup_job_timer_is_running",
    643                            group->BackupJobTimerIsRunning());
    644 
    645     all_groups_dict->SetWithoutPathExpansion(it->first, group_dict);
    646   }
    647   dict->Set("groups", all_groups_dict);
    648   return dict;
    649 }
    650 
    651 bool ClientSocketPoolBaseHelper::IdleSocket::IsUsable() const {
    652   if (socket->WasEverUsed())
    653     return socket->IsConnectedAndIdle();
    654   return socket->IsConnected();
    655 }
    656 
    657 bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup(
    658     base::TimeTicks now,
    659     base::TimeDelta timeout) const {
    660   bool timed_out = (now - start_time) >= timeout;
    661   if (timed_out)
    662     return true;
    663   return !IsUsable();
    664 }
    665 
    666 void ClientSocketPoolBaseHelper::CleanupIdleSockets(bool force) {
    667   if (idle_socket_count_ == 0)
    668     return;
    669 
    670   // Current time value. Retrieving it once at the function start rather than
    671   // inside the inner loop, since it shouldn't change by any meaningful amount.
    672   base::TimeTicks now = base::TimeTicks::Now();
    673 
    674   GroupMap::iterator i = group_map_.begin();
    675   while (i != group_map_.end()) {
    676     Group* group = i->second;
    677 
    678     std::list<IdleSocket>::iterator j = group->mutable_idle_sockets()->begin();
    679     while (j != group->idle_sockets().end()) {
    680       base::TimeDelta timeout =
    681           j->socket->WasEverUsed() ?
    682           used_idle_socket_timeout_ : unused_idle_socket_timeout_;
    683       if (force || j->ShouldCleanup(now, timeout)) {
    684         delete j->socket;
    685         j = group->mutable_idle_sockets()->erase(j);
    686         DecrementIdleCount();
    687       } else {
    688         ++j;
    689       }
    690     }
    691 
    692     // Delete group if no longer needed.
    693     if (group->IsEmpty()) {
    694       RemoveGroup(i++);
    695     } else {
    696       ++i;
    697     }
    698   }
    699 }
    700 
    701 ClientSocketPoolBaseHelper::Group* ClientSocketPoolBaseHelper::GetOrCreateGroup(
    702     const std::string& group_name) {
    703   GroupMap::iterator it = group_map_.find(group_name);
    704   if (it != group_map_.end())
    705     return it->second;
    706   Group* group = new Group;
    707   group_map_[group_name] = group;
    708   return group;
    709 }
    710 
    711 void ClientSocketPoolBaseHelper::RemoveGroup(const std::string& group_name) {
    712   GroupMap::iterator it = group_map_.find(group_name);
    713   CHECK(it != group_map_.end());
    714 
    715   RemoveGroup(it);
    716 }
    717 
    718 void ClientSocketPoolBaseHelper::RemoveGroup(GroupMap::iterator it) {
    719   delete it->second;
    720   group_map_.erase(it);
    721 }
    722 
    723 // static
    724 bool ClientSocketPoolBaseHelper::connect_backup_jobs_enabled() {
    725   return g_connect_backup_jobs_enabled;
    726 }
    727 
    728 // static
    729 bool ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(bool enabled) {
    730   bool old_value = g_connect_backup_jobs_enabled;
    731   g_connect_backup_jobs_enabled = enabled;
    732   return old_value;
    733 }
    734 
    735 void ClientSocketPoolBaseHelper::EnableConnectBackupJobs() {
    736   connect_backup_jobs_enabled_ = g_connect_backup_jobs_enabled;
    737 }
    738 
    739 void ClientSocketPoolBaseHelper::IncrementIdleCount() {
    740   if (++idle_socket_count_ == 1 && use_cleanup_timer_)
    741     StartIdleSocketTimer();
    742 }
    743 
    744 void ClientSocketPoolBaseHelper::DecrementIdleCount() {
    745   if (--idle_socket_count_ == 0)
    746     timer_.Stop();
    747 }
    748 
    749 // static
    750 bool ClientSocketPoolBaseHelper::cleanup_timer_enabled() {
    751   return g_cleanup_timer_enabled;
    752 }
    753 
    754 // static
    755 bool ClientSocketPoolBaseHelper::set_cleanup_timer_enabled(bool enabled) {
    756   bool old_value = g_cleanup_timer_enabled;
    757   g_cleanup_timer_enabled = enabled;
    758   return old_value;
    759 }
    760 
    761 void ClientSocketPoolBaseHelper::StartIdleSocketTimer() {
    762   timer_.Start(FROM_HERE, TimeDelta::FromSeconds(kCleanupInterval), this,
    763                &ClientSocketPoolBaseHelper::OnCleanupTimerFired);
    764 }
    765 
    766 void ClientSocketPoolBaseHelper::ReleaseSocket(const std::string& group_name,
    767                                                scoped_ptr<StreamSocket> socket,
    768                                                int id) {
    769   GroupMap::iterator i = group_map_.find(group_name);
    770   CHECK(i != group_map_.end());
    771 
    772   Group* group = i->second;
    773 
    774   CHECK_GT(handed_out_socket_count_, 0);
    775   handed_out_socket_count_--;
    776 
    777   CHECK_GT(group->active_socket_count(), 0);
    778   group->DecrementActiveSocketCount();
    779 
    780   const bool can_reuse = socket->IsConnectedAndIdle() &&
    781       id == pool_generation_number_;
    782   if (can_reuse) {
    783     // Add it to the idle list.
    784     AddIdleSocket(socket.Pass(), group);
    785     OnAvailableSocketSlot(group_name, group);
    786   } else {
    787     socket.reset();
    788   }
    789 
    790   CheckForStalledSocketGroups();
    791 }
    792 
    793 void ClientSocketPoolBaseHelper::CheckForStalledSocketGroups() {
    794   // If we have idle sockets, see if we can give one to the top-stalled group.
    795   std::string top_group_name;
    796   Group* top_group = NULL;
    797   if (!FindTopStalledGroup(&top_group, &top_group_name)) {
    798     // There may still be a stalled group in a lower level pool.
    799     for (std::set<LowerLayeredPool*>::iterator it = lower_pools_.begin();
    800          it != lower_pools_.end();
    801          ++it) {
    802        if ((*it)->IsStalled()) {
    803          CloseOneIdleSocket();
    804          break;
    805        }
    806     }
    807     return;
    808   }
    809 
    810   if (ReachedMaxSocketsLimit()) {
    811     if (idle_socket_count() > 0) {
    812       CloseOneIdleSocket();
    813     } else {
    814       // We can't activate more sockets since we're already at our global
    815       // limit.
    816       return;
    817     }
    818   }
    819 
    820   // Note:  we don't loop on waking stalled groups.  If the stalled group is at
    821   //        its limit, may be left with other stalled groups that could be
    822   //        woken.  This isn't optimal, but there is no starvation, so to avoid
    823   //        the looping we leave it at this.
    824   OnAvailableSocketSlot(top_group_name, top_group);
    825 }
    826 
    827 // Search for the highest priority pending request, amongst the groups that
    828 // are not at the |max_sockets_per_group_| limit. Note: for requests with
    829 // the same priority, the winner is based on group hash ordering (and not
    830 // insertion order).
    831 bool ClientSocketPoolBaseHelper::FindTopStalledGroup(
    832     Group** group,
    833     std::string* group_name) const {
    834   CHECK((group && group_name) || (!group && !group_name));
    835   Group* top_group = NULL;
    836   const std::string* top_group_name = NULL;
    837   bool has_stalled_group = false;
    838   for (GroupMap::const_iterator i = group_map_.begin();
    839        i != group_map_.end(); ++i) {
    840     Group* curr_group = i->second;
    841     if (!curr_group->has_pending_requests())
    842       continue;
    843     if (curr_group->IsStalledOnPoolMaxSockets(max_sockets_per_group_)) {
    844       if (!group)
    845         return true;
    846       has_stalled_group = true;
    847       bool has_higher_priority = !top_group ||
    848           curr_group->TopPendingPriority() > top_group->TopPendingPriority();
    849       if (has_higher_priority) {
    850         top_group = curr_group;
    851         top_group_name = &i->first;
    852       }
    853     }
    854   }
    855 
    856   if (top_group) {
    857     CHECK(group);
    858     *group = top_group;
    859     *group_name = *top_group_name;
    860   } else {
    861     CHECK(!has_stalled_group);
    862   }
    863   return has_stalled_group;
    864 }
    865 
    866 void ClientSocketPoolBaseHelper::OnConnectJobComplete(
    867     int result, ConnectJob* job) {
    868   DCHECK_NE(ERR_IO_PENDING, result);
    869   const std::string group_name = job->group_name();
    870   GroupMap::iterator group_it = group_map_.find(group_name);
    871   CHECK(group_it != group_map_.end());
    872   Group* group = group_it->second;
    873 
    874   scoped_ptr<StreamSocket> socket = job->PassSocket();
    875 
    876   // Copies of these are needed because |job| may be deleted before they are
    877   // accessed.
    878   BoundNetLog job_log = job->net_log();
    879   LoadTimingInfo::ConnectTiming connect_timing = job->connect_timing();
    880 
    881   // RemoveConnectJob(job, _) must be called by all branches below;
    882   // otherwise, |job| will be leaked.
    883 
    884   if (result == OK) {
    885     DCHECK(socket.get());
    886     RemoveConnectJob(job, group);
    887     scoped_ptr<const Request> request = group->PopNextPendingRequest();
    888     if (request) {
    889       LogBoundConnectJobToRequest(job_log.source(), *request);
    890       HandOutSocket(
    891           socket.Pass(), ClientSocketHandle::UNUSED, connect_timing,
    892           request->handle(), base::TimeDelta(), group, request->net_log());
    893       request->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
    894       InvokeUserCallbackLater(request->handle(), request->callback(), result);
    895     } else {
    896       AddIdleSocket(socket.Pass(), group);
    897       OnAvailableSocketSlot(group_name, group);
    898       CheckForStalledSocketGroups();
    899     }
    900   } else {
    901     // If we got a socket, it must contain error information so pass that
    902     // up so that the caller can retrieve it.
    903     bool handed_out_socket = false;
    904     scoped_ptr<const Request> request = group->PopNextPendingRequest();
    905     if (request) {
    906       LogBoundConnectJobToRequest(job_log.source(), *request);
    907       job->GetAdditionalErrorState(request->handle());
    908       RemoveConnectJob(job, group);
    909       if (socket.get()) {
    910         handed_out_socket = true;
    911         HandOutSocket(socket.Pass(), ClientSocketHandle::UNUSED,
    912                       connect_timing, request->handle(), base::TimeDelta(),
    913                       group, request->net_log());
    914       }
    915       request->net_log().EndEventWithNetErrorCode(
    916           NetLog::TYPE_SOCKET_POOL, result);
    917       InvokeUserCallbackLater(request->handle(), request->callback(), result);
    918     } else {
    919       RemoveConnectJob(job, group);
    920     }
    921     if (!handed_out_socket) {
    922       OnAvailableSocketSlot(group_name, group);
    923       CheckForStalledSocketGroups();
    924     }
    925   }
    926 }
    927 
    928 void ClientSocketPoolBaseHelper::OnIPAddressChanged() {
    929   FlushWithError(ERR_NETWORK_CHANGED);
    930 }
    931 
    932 void ClientSocketPoolBaseHelper::FlushWithError(int error) {
    933   pool_generation_number_++;
    934   CancelAllConnectJobs();
    935   CloseIdleSockets();
    936   CancelAllRequestsWithError(error);
    937 }
    938 
    939 void ClientSocketPoolBaseHelper::RemoveConnectJob(ConnectJob* job,
    940                                                   Group* group) {
    941   CHECK_GT(connecting_socket_count_, 0);
    942   connecting_socket_count_--;
    943 
    944   DCHECK(group);
    945   group->RemoveJob(job);
    946 }
    947 
    948 void ClientSocketPoolBaseHelper::OnAvailableSocketSlot(
    949     const std::string& group_name, Group* group) {
    950   DCHECK(ContainsKey(group_map_, group_name));
    951   if (group->IsEmpty()) {
    952     RemoveGroup(group_name);
    953   } else if (group->has_pending_requests()) {
    954     ProcessPendingRequest(group_name, group);
    955   }
    956 }
    957 
    958 void ClientSocketPoolBaseHelper::ProcessPendingRequest(
    959     const std::string& group_name, Group* group) {
    960   const Request* next_request = group->GetNextPendingRequest();
    961   DCHECK(next_request);
    962   int rv = RequestSocketInternal(group_name, *next_request);
    963   if (rv != ERR_IO_PENDING) {
    964     scoped_ptr<const Request> request = group->PopNextPendingRequest();
    965     DCHECK(request);
    966     if (group->IsEmpty())
    967       RemoveGroup(group_name);
    968 
    969     request->net_log().EndEventWithNetErrorCode(NetLog::TYPE_SOCKET_POOL, rv);
    970     InvokeUserCallbackLater(request->handle(), request->callback(), rv);
    971   }
    972 }
    973 
    974 void ClientSocketPoolBaseHelper::HandOutSocket(
    975     scoped_ptr<StreamSocket> socket,
    976     ClientSocketHandle::SocketReuseType reuse_type,
    977     const LoadTimingInfo::ConnectTiming& connect_timing,
    978     ClientSocketHandle* handle,
    979     base::TimeDelta idle_time,
    980     Group* group,
    981     const BoundNetLog& net_log) {
    982   DCHECK(socket);
    983   handle->SetSocket(socket.Pass());
    984   handle->set_reuse_type(reuse_type);
    985   handle->set_idle_time(idle_time);
    986   handle->set_pool_id(pool_generation_number_);
    987   handle->set_connect_timing(connect_timing);
    988 
    989   if (handle->is_reused()) {
    990     net_log.AddEvent(
    991         NetLog::TYPE_SOCKET_POOL_REUSED_AN_EXISTING_SOCKET,
    992         NetLog::IntegerCallback(
    993             "idle_ms", static_cast<int>(idle_time.InMilliseconds())));
    994   }
    995 
    996   net_log.AddEvent(
    997       NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET,
    998       handle->socket()->NetLog().source().ToEventParametersCallback());
    999 
   1000   handed_out_socket_count_++;
   1001   group->IncrementActiveSocketCount();
   1002 }
   1003 
   1004 void ClientSocketPoolBaseHelper::AddIdleSocket(
   1005     scoped_ptr<StreamSocket> socket,
   1006     Group* group) {
   1007   DCHECK(socket);
   1008   IdleSocket idle_socket;
   1009   idle_socket.socket = socket.release();
   1010   idle_socket.start_time = base::TimeTicks::Now();
   1011 
   1012   group->mutable_idle_sockets()->push_back(idle_socket);
   1013   IncrementIdleCount();
   1014 }
   1015 
   1016 void ClientSocketPoolBaseHelper::CancelAllConnectJobs() {
   1017   for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end();) {
   1018     Group* group = i->second;
   1019     connecting_socket_count_ -= group->jobs().size();
   1020     group->RemoveAllJobs();
   1021 
   1022     // Delete group if no longer needed.
   1023     if (group->IsEmpty()) {
   1024       // RemoveGroup() will call .erase() which will invalidate the iterator,
   1025       // but i will already have been incremented to a valid iterator before
   1026       // RemoveGroup() is called.
   1027       RemoveGroup(i++);
   1028     } else {
   1029       ++i;
   1030     }
   1031   }
   1032   DCHECK_EQ(0, connecting_socket_count_);
   1033 }
   1034 
   1035 void ClientSocketPoolBaseHelper::CancelAllRequestsWithError(int error) {
   1036   for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end();) {
   1037     Group* group = i->second;
   1038 
   1039     while (true) {
   1040       scoped_ptr<const Request> request = group->PopNextPendingRequest();
   1041       if (!request)
   1042         break;
   1043       InvokeUserCallbackLater(request->handle(), request->callback(), error);
   1044     }
   1045 
   1046     // Delete group if no longer needed.
   1047     if (group->IsEmpty()) {
   1048       // RemoveGroup() will call .erase() which will invalidate the iterator,
   1049       // but i will already have been incremented to a valid iterator before
   1050       // RemoveGroup() is called.
   1051       RemoveGroup(i++);
   1052     } else {
   1053       ++i;
   1054     }
   1055   }
   1056 }
   1057 
   1058 bool ClientSocketPoolBaseHelper::ReachedMaxSocketsLimit() const {
   1059   // Each connecting socket will eventually connect and be handed out.
   1060   int total = handed_out_socket_count_ + connecting_socket_count_ +
   1061       idle_socket_count();
   1062   // There can be more sockets than the limit since some requests can ignore
   1063   // the limit
   1064   if (total < max_sockets_)
   1065     return false;
   1066   return true;
   1067 }
   1068 
   1069 bool ClientSocketPoolBaseHelper::CloseOneIdleSocket() {
   1070   if (idle_socket_count() == 0)
   1071     return false;
   1072   return CloseOneIdleSocketExceptInGroup(NULL);
   1073 }
   1074 
   1075 bool ClientSocketPoolBaseHelper::CloseOneIdleSocketExceptInGroup(
   1076     const Group* exception_group) {
   1077   CHECK_GT(idle_socket_count(), 0);
   1078 
   1079   for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end(); ++i) {
   1080     Group* group = i->second;
   1081     if (exception_group == group)
   1082       continue;
   1083     std::list<IdleSocket>* idle_sockets = group->mutable_idle_sockets();
   1084 
   1085     if (!idle_sockets->empty()) {
   1086       delete idle_sockets->front().socket;
   1087       idle_sockets->pop_front();
   1088       DecrementIdleCount();
   1089       if (group->IsEmpty())
   1090         RemoveGroup(i);
   1091 
   1092       return true;
   1093     }
   1094   }
   1095 
   1096   return false;
   1097 }
   1098 
   1099 bool ClientSocketPoolBaseHelper::CloseOneIdleConnectionInHigherLayeredPool() {
   1100   // This pool doesn't have any idle sockets. It's possible that a pool at a
   1101   // higher layer is holding one of this sockets active, but it's actually idle.
   1102   // Query the higher layers.
   1103   for (std::set<HigherLayeredPool*>::const_iterator it = higher_pools_.begin();
   1104        it != higher_pools_.end(); ++it) {
   1105     if ((*it)->CloseOneIdleConnection())
   1106       return true;
   1107   }
   1108   return false;
   1109 }
   1110 
   1111 void ClientSocketPoolBaseHelper::InvokeUserCallbackLater(
   1112     ClientSocketHandle* handle, const CompletionCallback& callback, int rv) {
   1113   CHECK(!ContainsKey(pending_callback_map_, handle));
   1114   pending_callback_map_[handle] = CallbackResultPair(callback, rv);
   1115   base::MessageLoop::current()->PostTask(
   1116       FROM_HERE,
   1117       base::Bind(&ClientSocketPoolBaseHelper::InvokeUserCallback,
   1118                  weak_factory_.GetWeakPtr(), handle));
   1119 }
   1120 
   1121 void ClientSocketPoolBaseHelper::InvokeUserCallback(
   1122     ClientSocketHandle* handle) {
   1123   PendingCallbackMap::iterator it = pending_callback_map_.find(handle);
   1124 
   1125   // Exit if the request has already been cancelled.
   1126   if (it == pending_callback_map_.end())
   1127     return;
   1128 
   1129   CHECK(!handle->is_initialized());
   1130   CompletionCallback callback = it->second.callback;
   1131   int result = it->second.result;
   1132   pending_callback_map_.erase(it);
   1133   callback.Run(result);
   1134 }
   1135 
   1136 void ClientSocketPoolBaseHelper::TryToCloseSocketsInLayeredPools() {
   1137   while (IsStalled()) {
   1138     // Closing a socket will result in calling back into |this| to use the freed
   1139     // socket slot, so nothing else is needed.
   1140     if (!CloseOneIdleConnectionInHigherLayeredPool())
   1141       return;
   1142   }
   1143 }
   1144 
   1145 ClientSocketPoolBaseHelper::Group::Group()
   1146     : unassigned_job_count_(0),
   1147       pending_requests_(NUM_PRIORITIES),
   1148       active_socket_count_(0) {}
   1149 
   1150 ClientSocketPoolBaseHelper::Group::~Group() {
   1151   DCHECK_EQ(0u, unassigned_job_count_);
   1152 }
   1153 
   1154 void ClientSocketPoolBaseHelper::Group::StartBackupJobTimer(
   1155     const std::string& group_name,
   1156     ClientSocketPoolBaseHelper* pool) {
   1157   // Only allow one timer to run at a time.
   1158   if (BackupJobTimerIsRunning())
   1159     return;
   1160 
   1161   // Unretained here is okay because |backup_job_timer_| is
   1162   // automatically cancelled when it's destroyed.
   1163   backup_job_timer_.Start(
   1164       FROM_HERE, pool->ConnectRetryInterval(),
   1165       base::Bind(&Group::OnBackupJobTimerFired, base::Unretained(this),
   1166                  group_name, pool));
   1167 }
   1168 
   1169 bool ClientSocketPoolBaseHelper::Group::BackupJobTimerIsRunning() const {
   1170   return backup_job_timer_.IsRunning();
   1171 }
   1172 
   1173 bool ClientSocketPoolBaseHelper::Group::TryToUseUnassignedConnectJob() {
   1174   SanityCheck();
   1175 
   1176   if (unassigned_job_count_ == 0)
   1177     return false;
   1178   --unassigned_job_count_;
   1179   return true;
   1180 }
   1181 
   1182 void ClientSocketPoolBaseHelper::Group::AddJob(scoped_ptr<ConnectJob> job,
   1183                                                bool is_preconnect) {
   1184   SanityCheck();
   1185 
   1186   if (is_preconnect)
   1187     ++unassigned_job_count_;
   1188   jobs_.insert(job.release());
   1189 }
   1190 
   1191 void ClientSocketPoolBaseHelper::Group::RemoveJob(ConnectJob* job) {
   1192   scoped_ptr<ConnectJob> owned_job(job);
   1193   SanityCheck();
   1194 
   1195   std::set<ConnectJob*>::iterator it = jobs_.find(job);
   1196   if (it != jobs_.end()) {
   1197     jobs_.erase(it);
   1198   } else {
   1199     NOTREACHED();
   1200   }
   1201   size_t job_count = jobs_.size();
   1202   if (job_count < unassigned_job_count_)
   1203     unassigned_job_count_ = job_count;
   1204 
   1205   // If we've got no more jobs for this group, then we no longer need a
   1206   // backup job either.
   1207   if (jobs_.empty())
   1208     backup_job_timer_.Stop();
   1209 }
   1210 
   1211 void ClientSocketPoolBaseHelper::Group::OnBackupJobTimerFired(
   1212     std::string group_name,
   1213     ClientSocketPoolBaseHelper* pool) {
   1214   // If there are no more jobs pending, there is no work to do.
   1215   // If we've done our cleanups correctly, this should not happen.
   1216   if (jobs_.empty()) {
   1217     NOTREACHED();
   1218     return;
   1219   }
   1220 
   1221   // If our old job is waiting on DNS, or if we can't create any sockets
   1222   // right now due to limits, just reset the timer.
   1223   if (pool->ReachedMaxSocketsLimit() ||
   1224       !HasAvailableSocketSlot(pool->max_sockets_per_group_) ||
   1225       (*jobs_.begin())->GetLoadState() == LOAD_STATE_RESOLVING_HOST) {
   1226     StartBackupJobTimer(group_name, pool);
   1227     return;
   1228   }
   1229 
   1230   if (pending_requests_.empty())
   1231     return;
   1232 
   1233   scoped_ptr<ConnectJob> backup_job =
   1234       pool->connect_job_factory_->NewConnectJob(
   1235           group_name, *pending_requests_.FirstMax().value(), pool);
   1236   backup_job->net_log().AddEvent(NetLog::TYPE_BACKUP_CONNECT_JOB_CREATED);
   1237   SIMPLE_STATS_COUNTER("socket.backup_created");
   1238   int rv = backup_job->Connect();
   1239   pool->connecting_socket_count_++;
   1240   ConnectJob* raw_backup_job = backup_job.get();
   1241   AddJob(backup_job.Pass(), false);
   1242   if (rv != ERR_IO_PENDING)
   1243     pool->OnConnectJobComplete(rv, raw_backup_job);
   1244 }
   1245 
   1246 void ClientSocketPoolBaseHelper::Group::SanityCheck() {
   1247   DCHECK_LE(unassigned_job_count_, jobs_.size());
   1248 }
   1249 
   1250 void ClientSocketPoolBaseHelper::Group::RemoveAllJobs() {
   1251   SanityCheck();
   1252 
   1253   // Delete active jobs.
   1254   STLDeleteElements(&jobs_);
   1255   unassigned_job_count_ = 0;
   1256 
   1257   // Stop backup job timer.
   1258   backup_job_timer_.Stop();
   1259 }
   1260 
   1261 const ClientSocketPoolBaseHelper::Request*
   1262 ClientSocketPoolBaseHelper::Group::GetNextPendingRequest() const {
   1263   return
   1264       pending_requests_.empty() ? NULL : pending_requests_.FirstMax().value();
   1265 }
   1266 
   1267 bool ClientSocketPoolBaseHelper::Group::HasConnectJobForHandle(
   1268     const ClientSocketHandle* handle) const {
   1269   // Search the first |jobs_.size()| pending requests for |handle|.
   1270   // If it's farther back in the deque than that, it doesn't have a
   1271   // corresponding ConnectJob.
   1272   size_t i = 0;
   1273   for (RequestQueue::Pointer pointer = pending_requests_.FirstMax();
   1274        !pointer.is_null() && i < jobs_.size();
   1275        pointer = pending_requests_.GetNextTowardsLastMin(pointer), ++i) {
   1276     if (pointer.value()->handle() == handle)
   1277       return true;
   1278   }
   1279   return false;
   1280 }
   1281 
   1282 void ClientSocketPoolBaseHelper::Group::InsertPendingRequest(
   1283     scoped_ptr<const Request> request) {
   1284   // This value must be cached before we release |request|.
   1285   RequestPriority priority = request->priority();
   1286   if (request->ignore_limits()) {
   1287     // Put requests with ignore_limits == true (which should have
   1288     // priority == MAXIMUM_PRIORITY) ahead of other requests with
   1289     // MAXIMUM_PRIORITY.
   1290     DCHECK_EQ(priority, MAXIMUM_PRIORITY);
   1291     pending_requests_.InsertAtFront(request.release(), priority);
   1292   } else {
   1293     pending_requests_.Insert(request.release(), priority);
   1294   }
   1295 }
   1296 
   1297 scoped_ptr<const ClientSocketPoolBaseHelper::Request>
   1298 ClientSocketPoolBaseHelper::Group::PopNextPendingRequest() {
   1299   if (pending_requests_.empty())
   1300     return scoped_ptr<const ClientSocketPoolBaseHelper::Request>();
   1301   return RemovePendingRequest(pending_requests_.FirstMax());
   1302 }
   1303 
   1304 scoped_ptr<const ClientSocketPoolBaseHelper::Request>
   1305 ClientSocketPoolBaseHelper::Group::FindAndRemovePendingRequest(
   1306     ClientSocketHandle* handle) {
   1307   for (RequestQueue::Pointer pointer = pending_requests_.FirstMax();
   1308        !pointer.is_null();
   1309        pointer = pending_requests_.GetNextTowardsLastMin(pointer)) {
   1310     if (pointer.value()->handle() == handle) {
   1311       scoped_ptr<const Request> request = RemovePendingRequest(pointer);
   1312       return request.Pass();
   1313     }
   1314   }
   1315   return scoped_ptr<const ClientSocketPoolBaseHelper::Request>();
   1316 }
   1317 
   1318 scoped_ptr<const ClientSocketPoolBaseHelper::Request>
   1319 ClientSocketPoolBaseHelper::Group::RemovePendingRequest(
   1320     const RequestQueue::Pointer& pointer) {
   1321   scoped_ptr<const Request> request(pointer.value());
   1322   pending_requests_.Erase(pointer);
   1323   // If there are no more requests, kill the backup timer.
   1324   if (pending_requests_.empty())
   1325     backup_job_timer_.Stop();
   1326   return request.Pass();
   1327 }
   1328 
   1329 }  // namespace internal
   1330 
   1331 }  // namespace net
   1332