blob: eb1cd5134c2ca190cf65e3bf2a4388f8e9e0c262 [file] [log] [blame]
[email protected]a796bcec2010-03-22 17:17:261// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]ff579d42009-06-24 15:47:022// 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"
[email protected]fd4fe0b2010-02-08 23:02:158#include "base/format_macros.h"
[email protected]ff579d42009-06-24 15:47:029#include "base/message_loop.h"
[email protected]6b624c62010-03-14 08:37:3210#include "base/stats_counters.h"
[email protected]ff579d42009-06-24 15:47:0211#include "base/stl_util-inl.h"
[email protected]fd4fe0b2010-02-08 23:02:1512#include "base/string_util.h"
[email protected]ff579d42009-06-24 15:47:0213#include "base/time.h"
[email protected]9e743cd2010-03-16 07:03:5314#include "net/base/net_log.h"
[email protected]ff579d42009-06-24 15:47:0215#include "net/base/net_errors.h"
16#include "net/socket/client_socket_handle.h"
17
18using base::TimeDelta;
19
20namespace {
21
22// The timeout value, in seconds, used to clean up idle sockets that can't be
23// reused.
24//
25// Note: It's important to close idle sockets that have received data as soon
26// as possible because the received data may cause BSOD on Windows XP under
27// some conditions. See http://crbug.com/4606.
28const int kCleanupInterval = 10; // DO NOT INCREASE THIS TIMEOUT.
29
[email protected]ff579d42009-06-24 15:47:0230} // namespace
31
32namespace net {
33
[email protected]2ab05b52009-07-01 23:57:5834ConnectJob::ConnectJob(const std::string& group_name,
[email protected]974ebd62009-08-03 23:14:3435 base::TimeDelta timeout_duration,
[email protected]fd7b7c92009-08-20 19:38:3036 Delegate* delegate,
[email protected]9e743cd2010-03-16 07:03:5337 const BoundNetLog& net_log)
[email protected]2ab05b52009-07-01 23:57:5838 : group_name_(group_name),
[email protected]974ebd62009-08-03 23:14:3439 timeout_duration_(timeout_duration),
[email protected]2ab05b52009-07-01 23:57:5840 delegate_(delegate),
[email protected]9e743cd2010-03-16 07:03:5341 net_log_(net_log) {
[email protected]2ab05b52009-07-01 23:57:5842 DCHECK(!group_name.empty());
[email protected]2ab05b52009-07-01 23:57:5843 DCHECK(delegate);
44}
45
[email protected]fd7b7c92009-08-20 19:38:3046ConnectJob::~ConnectJob() {
47 if (delegate_) {
48 // If the delegate was not NULLed, then NotifyDelegateOfCompletion has
49 // not been called yet (hence we are cancelling).
[email protected]9e743cd2010-03-16 07:03:5350 net_log_.AddEvent(NetLog::TYPE_CANCELLED);
51 net_log_.EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB);
[email protected]fd7b7c92009-08-20 19:38:3052 }
53}
[email protected]2ab05b52009-07-01 23:57:5854
[email protected]974ebd62009-08-03 23:14:3455int ConnectJob::Connect() {
56 if (timeout_duration_ != base::TimeDelta())
57 timer_.Start(timeout_duration_, this, &ConnectJob::OnTimeout);
[email protected]fd7b7c92009-08-20 19:38:3058
[email protected]fd9c0d92010-03-23 17:47:4959 net_log_.BeginEventWithString(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB,
60 group_name_);
[email protected]fd7b7c92009-08-20 19:38:3061
62 int rv = ConnectInternal();
63
64 if (rv != ERR_IO_PENDING) {
65 delegate_ = NULL;
[email protected]9e743cd2010-03-16 07:03:5366 net_log_.EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB);
[email protected]fd7b7c92009-08-20 19:38:3067 }
68
69 return rv;
70}
71
72void ConnectJob::NotifyDelegateOfCompletion(int rv) {
73 // The delegate will delete |this|.
74 Delegate *delegate = delegate_;
75 delegate_ = NULL;
76
[email protected]9e743cd2010-03-16 07:03:5377 net_log_.EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB);
[email protected]fd7b7c92009-08-20 19:38:3078
79 delegate->OnConnectJobComplete(rv, this);
[email protected]974ebd62009-08-03 23:14:3480}
81
[email protected]a796bcec2010-03-22 17:17:2682void ConnectJob::ResetTimer(base::TimeDelta remaining_time) {
83 timer_.Stop();
84 timer_.Start(remaining_time, this, &ConnectJob::OnTimeout);
85}
86
[email protected]974ebd62009-08-03 23:14:3487void ConnectJob::OnTimeout() {
[email protected]6e713f02009-08-06 02:56:4088 // Make sure the socket is NULL before calling into |delegate|.
89 set_socket(NULL);
[email protected]fd7b7c92009-08-20 19:38:3090
[email protected]9e743cd2010-03-16 07:03:5391 net_log_.AddEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_TIMED_OUT);
[email protected]fd7b7c92009-08-20 19:38:3092
93 NotifyDelegateOfCompletion(ERR_TIMED_OUT);
[email protected]974ebd62009-08-03 23:14:3494}
95
[email protected]d80a4322009-08-14 07:07:4996namespace internal {
97
[email protected]fd4fe0b2010-02-08 23:02:1598ClientSocketPoolBaseHelper::Request::Request(
99 ClientSocketHandle* handle,
100 CompletionCallback* callback,
101 RequestPriority priority,
[email protected]9e743cd2010-03-16 07:03:53102 const BoundNetLog& net_log)
[email protected]fd4fe0b2010-02-08 23:02:15103 : handle_(handle), callback_(callback), priority_(priority),
[email protected]9e743cd2010-03-16 07:03:53104 net_log_(net_log) {}
[email protected]fd4fe0b2010-02-08 23:02:15105
106ClientSocketPoolBaseHelper::Request::~Request() {}
107
[email protected]d80a4322009-08-14 07:07:49108ClientSocketPoolBaseHelper::ClientSocketPoolBaseHelper(
[email protected]211d2172009-07-22 15:48:53109 int max_sockets,
[email protected]ff579d42009-06-24 15:47:02110 int max_sockets_per_group,
[email protected]9bf28db2009-08-29 01:35:16111 base::TimeDelta unused_idle_socket_timeout,
112 base::TimeDelta used_idle_socket_timeout,
[email protected]9b3ef462010-04-08 01:48:31113 ConnectJobFactory* connect_job_factory)
[email protected]ff579d42009-06-24 15:47:02114 : idle_socket_count_(0),
[email protected]211d2172009-07-22 15:48:53115 connecting_socket_count_(0),
116 handed_out_socket_count_(0),
117 max_sockets_(max_sockets),
[email protected]ff579d42009-06-24 15:47:02118 max_sockets_per_group_(max_sockets_per_group),
[email protected]9bf28db2009-08-29 01:35:16119 unused_idle_socket_timeout_(unused_idle_socket_timeout),
120 used_idle_socket_timeout_(used_idle_socket_timeout),
[email protected]211d2172009-07-22 15:48:53121 may_have_stalled_group_(false),
[email protected]100d5fb92009-12-21 21:08:35122 connect_job_factory_(connect_job_factory),
[email protected]7c28e9a2010-03-20 01:16:13123 backup_jobs_enabled_(false),
[email protected]6b624c62010-03-14 08:37:32124 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
[email protected]211d2172009-07-22 15:48:53125 DCHECK_LE(0, max_sockets_per_group);
126 DCHECK_LE(max_sockets_per_group, max_sockets);
[email protected]211d2172009-07-22 15:48:53127}
[email protected]ff579d42009-06-24 15:47:02128
[email protected]d80a4322009-08-14 07:07:49129ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() {
[email protected]4d3b05d2010-01-27 21:27:29130 CancelAllConnectJobs();
131
[email protected]ff579d42009-06-24 15:47:02132 // Clean up any idle sockets. Assert that we have no remaining active
133 // sockets or pending requests. They should have all been cleaned up prior
134 // to the manager being destroyed.
135 CloseIdleSockets();
[email protected]6b624c62010-03-14 08:37:32136 CHECK(group_map_.empty());
[email protected]4d3b05d2010-01-27 21:27:29137 DCHECK_EQ(0, connecting_socket_count_);
[email protected]ff579d42009-06-24 15:47:02138}
139
140// InsertRequestIntoQueue inserts the request into the queue based on
141// priority. Highest priorities are closest to the front. Older requests are
142// prioritized over requests of equal priority.
143//
144// static
[email protected]d80a4322009-08-14 07:07:49145void ClientSocketPoolBaseHelper::InsertRequestIntoQueue(
146 const Request* r, RequestQueue* pending_requests) {
[email protected]ff579d42009-06-24 15:47:02147 RequestQueue::iterator it = pending_requests->begin();
[email protected]ac790b42009-12-02 04:31:31148 while (it != pending_requests->end() && r->priority() >= (*it)->priority())
[email protected]ff579d42009-06-24 15:47:02149 ++it;
150 pending_requests->insert(it, r);
151}
152
[email protected]fd7b7c92009-08-20 19:38:30153// static
154const ClientSocketPoolBaseHelper::Request*
155ClientSocketPoolBaseHelper::RemoveRequestFromQueue(
156 RequestQueue::iterator it, RequestQueue* pending_requests) {
157 const Request* req = *it;
[email protected]fd7b7c92009-08-20 19:38:30158 pending_requests->erase(it);
159 return req;
160}
161
[email protected]d80a4322009-08-14 07:07:49162int ClientSocketPoolBaseHelper::RequestSocket(
[email protected]ff579d42009-06-24 15:47:02163 const std::string& group_name,
[email protected]d80a4322009-08-14 07:07:49164 const Request* request) {
[email protected]9e743cd2010-03-16 07:03:53165 request->net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]fd4fe0b2010-02-08 23:02:15166 Group& group = group_map_[group_name];
167 int rv = RequestSocketInternal(group_name, request);
168 if (rv != ERR_IO_PENDING)
[email protected]9e743cd2010-03-16 07:03:53169 request->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]fd4fe0b2010-02-08 23:02:15170 else
171 InsertRequestIntoQueue(request, &group.pending_requests);
172 return rv;
173}
174
175int ClientSocketPoolBaseHelper::RequestSocketInternal(
176 const std::string& group_name,
177 const Request* request) {
[email protected]d80a4322009-08-14 07:07:49178 DCHECK_GE(request->priority(), 0);
179 CompletionCallback* const callback = request->callback();
180 CHECK(callback);
181 ClientSocketHandle* const handle = request->handle();
182 CHECK(handle);
[email protected]ff579d42009-06-24 15:47:02183 Group& group = group_map_[group_name];
184
[email protected]42df4e8e2010-04-13 22:02:56185 // Can we make another active socket now?
186 if (ReachedMaxSocketsLimit() ||
187 !group.HasAvailableSocketSlot(max_sockets_per_group_)) {
188 if (ReachedMaxSocketsLimit()) {
189 // We could check if we really have a stalled group here, but it requires
190 // a scan of all groups, so just flip a flag here, and do the check later.
191 may_have_stalled_group_ = true;
192
193 request->net_log().AddEvent(NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS);
194 } else {
195 request->net_log().AddEvent(NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP);
196 }
197 return ERR_IO_PENDING;
198 }
199
[email protected]65552102010-04-09 22:58:10200 // Try to reuse a socket.
201 while (!group.idle_sockets.empty()) {
202 IdleSocket idle_socket = group.idle_sockets.back();
203 group.idle_sockets.pop_back();
204 DecrementIdleCount();
205 if (idle_socket.socket->IsConnectedAndIdle()) {
206 // We found one we can reuse!
207 base::TimeDelta idle_time =
208 base::TimeTicks::Now() - idle_socket.start_time;
209 HandOutSocket(
210 idle_socket.socket, idle_socket.used, handle, idle_time, &group,
211 request->net_log());
212 return OK;
213 }
214 delete idle_socket.socket;
215 }
216
[email protected]5edbf8d2010-01-13 18:44:11217 // See if we already have enough connect jobs or sockets that will be released
218 // soon.
[email protected]4d3b05d2010-01-27 21:27:29219 if (group.HasReleasingSockets()) {
[email protected]5edbf8d2010-01-13 18:44:11220 return ERR_IO_PENDING;
221 }
222
[email protected]ff579d42009-06-24 15:47:02223 // We couldn't find a socket to reuse, so allocate and connect a new one.
[email protected]9e743cd2010-03-16 07:03:53224 BoundNetLog job_net_log = BoundNetLog::Make(
225 request->net_log().net_log(), NetLog::SOURCE_CONNECT_JOB);
[email protected]fd7b7c92009-08-20 19:38:30226
[email protected]2ab05b52009-07-01 23:57:58227 scoped_ptr<ConnectJob> connect_job(
[email protected]fd7b7c92009-08-20 19:38:30228 connect_job_factory_->NewConnectJob(group_name, *request, this,
[email protected]9e743cd2010-03-16 07:03:53229 job_net_log));
[email protected]ff579d42009-06-24 15:47:02230
[email protected]2ab05b52009-07-01 23:57:58231 int rv = connect_job->Connect();
[email protected]fd7b7c92009-08-20 19:38:30232
[email protected]9e743cd2010-03-16 07:03:53233 if (rv != ERR_IO_PENDING) {
[email protected]6427fe22010-04-16 22:27:41234 request->net_log().AddEventWithInteger(
235 NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID,
236 job_net_log.source().id);
[email protected]9e743cd2010-03-16 07:03:53237 }
[email protected]fd7b7c92009-08-20 19:38:30238
[email protected]2ab05b52009-07-01 23:57:58239 if (rv == OK) {
240 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */,
[email protected]9e743cd2010-03-16 07:03:53241 handle, base::TimeDelta(), &group, request->net_log());
[email protected]2ab05b52009-07-01 23:57:58242 } else if (rv == ERR_IO_PENDING) {
[email protected]6b624c62010-03-14 08:37:32243 // If we don't have any sockets in this group, set a timer for potentially
244 // creating a new one. If the SYN is lost, this backup socket may complete
245 // before the slow socket, improving end user latency.
[email protected]7c28e9a2010-03-20 01:16:13246 if (group.IsEmpty() && !group.backup_job && backup_jobs_enabled_) {
[email protected]6b624c62010-03-14 08:37:32247 group.backup_job = connect_job_factory_->NewConnectJob(group_name,
248 *request,
249 this,
[email protected]9e743cd2010-03-16 07:03:53250 job_net_log);
[email protected]6b624c62010-03-14 08:37:32251 StartBackupSocketTimer(group_name);
252 }
253
[email protected]211d2172009-07-22 15:48:53254 connecting_socket_count_++;
255
[email protected]5fc08e32009-07-15 17:09:57256 ConnectJob* job = connect_job.release();
[email protected]5fc08e32009-07-15 17:09:57257 group.jobs.insert(job);
[email protected]2b7523d2009-07-29 20:29:23258 } else if (group.IsEmpty()) {
259 group_map_.erase(group_name);
[email protected]2ab05b52009-07-01 23:57:58260 }
[email protected]ff579d42009-06-24 15:47:02261
[email protected]2ab05b52009-07-01 23:57:58262 return rv;
[email protected]ff579d42009-06-24 15:47:02263}
264
[email protected]6b624c62010-03-14 08:37:32265void ClientSocketPoolBaseHelper::StartBackupSocketTimer(
266 const std::string& group_name) {
267 CHECK(ContainsKey(group_map_, group_name));
268 Group& group = group_map_[group_name];
269
270 // Only allow one timer pending to create a backup socket.
271 if (group.backup_task)
272 return;
273
274 group.backup_task = method_factory_.NewRunnableMethod(
275 &ClientSocketPoolBaseHelper::OnBackupSocketTimerFired, group_name);
276 MessageLoop::current()->PostDelayedTask(FROM_HERE, group.backup_task,
277 ConnectRetryIntervalMs());
278}
279
280void ClientSocketPoolBaseHelper::OnBackupSocketTimerFired(
281 const std::string& group_name) {
282 CHECK(ContainsKey(group_map_, group_name));
283
284 Group& group = group_map_[group_name];
285
286 CHECK(group.backup_task);
287 group.backup_task = NULL;
288
289 CHECK(group.backup_job);
290
291 // If our backup job is waiting on DNS, just reset the timer.
292 CHECK(group.jobs.size());
293 if ((*group.jobs.begin())->GetLoadState() == LOAD_STATE_RESOLVING_HOST) {
[email protected]9e743cd2010-03-16 07:03:53294 group.backup_job->net_log().EndEvent(
295 NetLog::TYPE_SOCKET_BACKUP_TIMER_EXTENDED);
[email protected]6b624c62010-03-14 08:37:32296 StartBackupSocketTimer(group_name);
297 return;
298 }
299
[email protected]9e743cd2010-03-16 07:03:53300 group.backup_job->net_log().AddEvent(NetLog::TYPE_SOCKET_BACKUP_CREATED);
[email protected]6b624c62010-03-14 08:37:32301 SIMPLE_STATS_COUNTER("socket.backup_created");
302 int rv = group.backup_job->Connect();
[email protected]c83658c2010-03-24 08:19:34303 connecting_socket_count_++;
304 group.jobs.insert(group.backup_job);
305 ConnectJob* job = group.backup_job;
306 group.backup_job = NULL;
307 if (rv != ERR_IO_PENDING)
308 OnConnectJobComplete(rv, job);
[email protected]6b624c62010-03-14 08:37:32309}
310
[email protected]d80a4322009-08-14 07:07:49311void ClientSocketPoolBaseHelper::CancelRequest(
312 const std::string& group_name, const ClientSocketHandle* handle) {
[email protected]ff579d42009-06-24 15:47:02313 CHECK(ContainsKey(group_map_, group_name));
314
315 Group& group = group_map_[group_name];
316
[email protected]ff579d42009-06-24 15:47:02317 // Search pending_requests for matching handle.
318 RequestQueue::iterator it = group.pending_requests.begin();
319 for (; it != group.pending_requests.end(); ++it) {
[email protected]d80a4322009-08-14 07:07:49320 if ((*it)->handle() == handle) {
[email protected]fd7b7c92009-08-20 19:38:30321 const Request* req = RemoveRequestFromQueue(it, &group.pending_requests);
[email protected]9e743cd2010-03-16 07:03:53322 req->net_log().AddEvent(NetLog::TYPE_CANCELLED);
323 req->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]fd7b7c92009-08-20 19:38:30324 delete req;
[email protected]a796bcec2010-03-22 17:17:26325 // Let one connect job connect and become idle for potential future use.
[email protected]4d3b05d2010-01-27 21:27:29326 if (group.jobs.size() > group.pending_requests.size() + 1) {
[email protected]974ebd62009-08-03 23:14:34327 // TODO(willchan): Cancel the job in the earliest LoadState.
[email protected]4d3b05d2010-01-27 21:27:29328 RemoveConnectJob(*group.jobs.begin(), &group);
[email protected]974ebd62009-08-03 23:14:34329 OnAvailableSocketSlot(group_name, &group);
330 }
[email protected]ff579d42009-06-24 15:47:02331 return;
332 }
333 }
[email protected]ff579d42009-06-24 15:47:02334}
335
[email protected]d80a4322009-08-14 07:07:49336void ClientSocketPoolBaseHelper::ReleaseSocket(const std::string& group_name,
337 ClientSocket* socket) {
[email protected]5edbf8d2010-01-13 18:44:11338 Group& group = group_map_[group_name];
339 group.num_releasing_sockets++;
340 DCHECK_LE(group.num_releasing_sockets, group.active_socket_count);
[email protected]ff579d42009-06-24 15:47:02341 // Run this asynchronously to allow the caller to finish before we let
342 // another to begin doing work. This also avoids nasty recursion issues.
343 // NOTE: We cannot refer to the handle argument after this method returns.
344 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]d80a4322009-08-14 07:07:49345 this, &ClientSocketPoolBaseHelper::DoReleaseSocket, group_name, socket));
[email protected]ff579d42009-06-24 15:47:02346}
347
[email protected]d80a4322009-08-14 07:07:49348void ClientSocketPoolBaseHelper::CloseIdleSockets() {
[email protected]ff579d42009-06-24 15:47:02349 CleanupIdleSockets(true);
350}
351
[email protected]d80a4322009-08-14 07:07:49352int ClientSocketPoolBaseHelper::IdleSocketCountInGroup(
[email protected]ff579d42009-06-24 15:47:02353 const std::string& group_name) const {
354 GroupMap::const_iterator i = group_map_.find(group_name);
355 CHECK(i != group_map_.end());
356
357 return i->second.idle_sockets.size();
358}
359
[email protected]d80a4322009-08-14 07:07:49360LoadState ClientSocketPoolBaseHelper::GetLoadState(
[email protected]ff579d42009-06-24 15:47:02361 const std::string& group_name,
362 const ClientSocketHandle* handle) const {
363 if (!ContainsKey(group_map_, group_name)) {
364 NOTREACHED() << "ClientSocketPool does not contain group: " << group_name
365 << " for handle: " << handle;
366 return LOAD_STATE_IDLE;
367 }
368
369 // Can't use operator[] since it is non-const.
370 const Group& group = group_map_.find(group_name)->second;
371
[email protected]ff579d42009-06-24 15:47:02372 // Search pending_requests for matching handle.
373 RequestQueue::const_iterator it = group.pending_requests.begin();
[email protected]5fc08e32009-07-15 17:09:57374 for (size_t i = 0; it != group.pending_requests.end(); ++it, ++i) {
[email protected]d80a4322009-08-14 07:07:49375 if ((*it)->handle() == handle) {
[email protected]4d3b05d2010-01-27 21:27:29376 if (i < group.jobs.size()) {
[email protected]5fc08e32009-07-15 17:09:57377 LoadState max_state = LOAD_STATE_IDLE;
378 for (ConnectJobSet::const_iterator job_it = group.jobs.begin();
379 job_it != group.jobs.end(); ++job_it) {
[email protected]46451352009-09-01 14:54:21380 max_state = std::max(max_state, (*job_it)->GetLoadState());
[email protected]5fc08e32009-07-15 17:09:57381 }
382 return max_state;
383 } else {
384 // TODO(wtc): Add a state for being on the wait list.
385 // See http://www.crbug.com/5077.
386 return LOAD_STATE_IDLE;
387 }
[email protected]ff579d42009-06-24 15:47:02388 }
389 }
390
391 NOTREACHED();
392 return LOAD_STATE_IDLE;
393}
394
[email protected]d80a4322009-08-14 07:07:49395bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup(
[email protected]9bf28db2009-08-29 01:35:16396 base::TimeTicks now,
397 base::TimeDelta timeout) const {
398 bool timed_out = (now - start_time) >= timeout;
[email protected]5fc08e32009-07-15 17:09:57399 return timed_out ||
400 !(used ? socket->IsConnectedAndIdle() : socket->IsConnected());
[email protected]ff579d42009-06-24 15:47:02401}
402
[email protected]d80a4322009-08-14 07:07:49403void ClientSocketPoolBaseHelper::CleanupIdleSockets(bool force) {
[email protected]ff579d42009-06-24 15:47:02404 if (idle_socket_count_ == 0)
405 return;
406
407 // Current time value. Retrieving it once at the function start rather than
408 // inside the inner loop, since it shouldn't change by any meaningful amount.
409 base::TimeTicks now = base::TimeTicks::Now();
410
411 GroupMap::iterator i = group_map_.begin();
412 while (i != group_map_.end()) {
413 Group& group = i->second;
414
415 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
416 while (j != group.idle_sockets.end()) {
[email protected]9bf28db2009-08-29 01:35:16417 base::TimeDelta timeout =
418 j->used ? used_idle_socket_timeout_ : unused_idle_socket_timeout_;
419 if (force || j->ShouldCleanup(now, timeout)) {
[email protected]ff579d42009-06-24 15:47:02420 delete j->socket;
421 j = group.idle_sockets.erase(j);
422 DecrementIdleCount();
423 } else {
424 ++j;
425 }
426 }
427
428 // Delete group if no longer needed.
[email protected]2ab05b52009-07-01 23:57:58429 if (group.IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02430 group_map_.erase(i++);
431 } else {
432 ++i;
433 }
434 }
435}
436
[email protected]d80a4322009-08-14 07:07:49437void ClientSocketPoolBaseHelper::IncrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02438 if (++idle_socket_count_ == 1)
439 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this,
[email protected]d80a4322009-08-14 07:07:49440 &ClientSocketPoolBaseHelper::OnCleanupTimerFired);
[email protected]ff579d42009-06-24 15:47:02441}
442
[email protected]d80a4322009-08-14 07:07:49443void ClientSocketPoolBaseHelper::DecrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02444 if (--idle_socket_count_ == 0)
445 timer_.Stop();
446}
447
[email protected]d80a4322009-08-14 07:07:49448void ClientSocketPoolBaseHelper::DoReleaseSocket(const std::string& group_name,
449 ClientSocket* socket) {
[email protected]ff579d42009-06-24 15:47:02450 GroupMap::iterator i = group_map_.find(group_name);
451 CHECK(i != group_map_.end());
452
453 Group& group = i->second;
454
[email protected]5edbf8d2010-01-13 18:44:11455 group.num_releasing_sockets--;
456 DCHECK_GE(group.num_releasing_sockets, 0);
457
[email protected]b1f031dd2010-03-02 23:19:33458 CHECK_GT(handed_out_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53459 handed_out_socket_count_--;
460
[email protected]b1f031dd2010-03-02 23:19:33461 CHECK_GT(group.active_socket_count, 0);
[email protected]2ab05b52009-07-01 23:57:58462 group.active_socket_count--;
[email protected]ff579d42009-06-24 15:47:02463
464 const bool can_reuse = socket->IsConnectedAndIdle();
465 if (can_reuse) {
[email protected]5fc08e32009-07-15 17:09:57466 AddIdleSocket(socket, true /* used socket */, &group);
[email protected]ff579d42009-06-24 15:47:02467 } else {
468 delete socket;
469 }
470
[email protected]2ab05b52009-07-01 23:57:58471 OnAvailableSocketSlot(group_name, &group);
[email protected]4f2abec2010-02-03 18:10:16472
473 // If there are no more releasing sockets, then we might have to process
474 // multiple available socket slots, since we stalled their processing until
475 // all sockets have been released.
[email protected]4f1e4982010-03-02 18:31:04476 i = group_map_.find(group_name);
477 if (i == group_map_.end() || i->second.num_releasing_sockets > 0)
[email protected]4f2abec2010-02-03 18:10:16478 return;
479
480 while (true) {
481 // We can't activate more sockets since we're already at our global limit.
482 if (ReachedMaxSocketsLimit())
483 return;
[email protected]616925a2010-03-02 19:02:38484
[email protected]4f2abec2010-02-03 18:10:16485 // |group| might now be deleted.
486 i = group_map_.find(group_name);
487 if (i == group_map_.end())
488 return;
489
490 group = i->second;
[email protected]616925a2010-03-02 19:02:38491
[email protected]4f2abec2010-02-03 18:10:16492 // If we already have enough ConnectJobs to satisfy the pending requests,
493 // don't bother starting up more.
494 if (group.pending_requests.size() <= group.jobs.size())
495 return;
496
497 if (!group.HasAvailableSocketSlot(max_sockets_per_group_))
498 return;
499
500 OnAvailableSocketSlot(group_name, &group);
501 }
[email protected]ff579d42009-06-24 15:47:02502}
503
[email protected]211d2172009-07-22 15:48:53504// Search for the highest priority pending request, amongst the groups that
505// are not at the |max_sockets_per_group_| limit. Note: for requests with
506// the same priority, the winner is based on group hash ordering (and not
507// insertion order).
[email protected]d80a4322009-08-14 07:07:49508int ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group,
509 std::string* group_name) {
[email protected]211d2172009-07-22 15:48:53510 Group* top_group = NULL;
511 const std::string* top_group_name = NULL;
512 int stalled_group_count = 0;
513 for (GroupMap::iterator i = group_map_.begin();
514 i != group_map_.end(); ++i) {
515 Group& group = i->second;
516 const RequestQueue& queue = group.pending_requests;
517 if (queue.empty())
518 continue;
[email protected]6427fe22010-04-16 22:27:41519 bool has_unused_slot =
520 group.HasAvailableSocketSlot(max_sockets_per_group_) &&
521 group.pending_requests.size() > group.jobs.size();
522 if (has_unused_slot) {
[email protected]211d2172009-07-22 15:48:53523 stalled_group_count++;
[email protected]6427fe22010-04-16 22:27:41524 bool has_higher_priority = !top_group ||
525 group.TopPendingPriority() < top_group->TopPendingPriority();
526 if (has_higher_priority) {
527 top_group = &group;
528 top_group_name = &i->first;
529 }
[email protected]211d2172009-07-22 15:48:53530 }
531 }
532 if (top_group) {
533 *group = top_group;
534 *group_name = *top_group_name;
535 }
536 return stalled_group_count;
537}
538
[email protected]d80a4322009-08-14 07:07:49539void ClientSocketPoolBaseHelper::OnConnectJobComplete(
540 int result, ConnectJob* job) {
[email protected]2ab05b52009-07-01 23:57:58541 DCHECK_NE(ERR_IO_PENDING, result);
542 const std::string group_name = job->group_name();
[email protected]ff579d42009-06-24 15:47:02543 GroupMap::iterator group_it = group_map_.find(group_name);
544 CHECK(group_it != group_map_.end());
545 Group& group = group_it->second;
546
[email protected]6b624c62010-03-14 08:37:32547 // We've had a connect on the socket; discard any pending backup job
548 // for this group and kill the pending task.
549 group.CleanupBackupJob();
550
[email protected]5fc08e32009-07-15 17:09:57551 scoped_ptr<ClientSocket> socket(job->ReleaseSocket());
[email protected]ff579d42009-06-24 15:47:02552
[email protected]9e743cd2010-03-16 07:03:53553 BoundNetLog job_log = job->net_log();
[email protected]4d3b05d2010-01-27 21:27:29554 RemoveConnectJob(job, &group);
[email protected]5fc08e32009-07-15 17:09:57555
[email protected]4d3b05d2010-01-27 21:27:29556 if (result == OK) {
557 DCHECK(socket.get());
[email protected]fd7b7c92009-08-20 19:38:30558 if (!group.pending_requests.empty()) {
[email protected]4d3b05d2010-01-27 21:27:29559 scoped_ptr<const Request> r(RemoveRequestFromQueue(
[email protected]fd7b7c92009-08-20 19:38:30560 group.pending_requests.begin(), &group.pending_requests));
[email protected]9e743cd2010-03-16 07:03:53561 r->net_log().AddEventWithInteger(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID,
562 job_log.source().id);
563 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]4d3b05d2010-01-27 21:27:29564 HandOutSocket(
565 socket.release(), false /* unused socket */, r->handle(),
[email protected]9e743cd2010-03-16 07:03:53566 base::TimeDelta(), &group, r->net_log());
[email protected]4d3b05d2010-01-27 21:27:29567 r->callback()->Run(result);
[email protected]5fc08e32009-07-15 17:09:57568 } else {
[email protected]4d3b05d2010-01-27 21:27:29569 AddIdleSocket(socket.release(), false /* unused socket */, &group);
570 OnAvailableSocketSlot(group_name, &group);
[email protected]5fc08e32009-07-15 17:09:57571 }
[email protected]94c20472010-01-14 08:14:36572 } else {
[email protected]4d3b05d2010-01-27 21:27:29573 DCHECK(!socket.get());
574 if (!group.pending_requests.empty()) {
575 scoped_ptr<const Request> r(RemoveRequestFromQueue(
576 group.pending_requests.begin(), &group.pending_requests));
[email protected]9e743cd2010-03-16 07:03:53577 r->net_log().AddEventWithInteger(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID,
578 job_log.source().id);
579 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]4d3b05d2010-01-27 21:27:29580 r->callback()->Run(result);
581 }
582 MaybeOnAvailableSocketSlot(group_name);
[email protected]ff579d42009-06-24 15:47:02583 }
[email protected]ff579d42009-06-24 15:47:02584}
585
[email protected]4d3b05d2010-01-27 21:27:29586void ClientSocketPoolBaseHelper::RemoveConnectJob(const ConnectJob *job,
587 Group* group) {
[email protected]b1f031dd2010-03-02 23:19:33588 CHECK_GT(connecting_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53589 connecting_socket_count_--;
590
[email protected]4d3b05d2010-01-27 21:27:29591 DCHECK(job);
592 delete job;
[email protected]5fc08e32009-07-15 17:09:57593
594 if (group) {
595 DCHECK(ContainsKey(group->jobs, job));
596 group->jobs.erase(job);
597 }
[email protected]ff579d42009-06-24 15:47:02598}
599
[email protected]d80a4322009-08-14 07:07:49600void ClientSocketPoolBaseHelper::MaybeOnAvailableSocketSlot(
[email protected]2ab05b52009-07-01 23:57:58601 const std::string& group_name) {
602 GroupMap::iterator it = group_map_.find(group_name);
603 if (it != group_map_.end()) {
604 Group& group = it->second;
605 if (group.HasAvailableSocketSlot(max_sockets_per_group_))
606 OnAvailableSocketSlot(group_name, &group);
607 }
608}
[email protected]ff579d42009-06-24 15:47:02609
[email protected]d80a4322009-08-14 07:07:49610void ClientSocketPoolBaseHelper::OnAvailableSocketSlot(
611 const std::string& group_name, Group* group) {
[email protected]211d2172009-07-22 15:48:53612 if (may_have_stalled_group_) {
613 std::string top_group_name;
[email protected]bed37d442009-08-20 19:58:20614 Group* top_group = NULL;
[email protected]211d2172009-07-22 15:48:53615 int stalled_group_count = FindTopStalledGroup(&top_group, &top_group_name);
616 if (stalled_group_count <= 1)
617 may_have_stalled_group_ = false;
[email protected]42df4e8e2010-04-13 22:02:56618 if (stalled_group_count >= 1)
[email protected]211d2172009-07-22 15:48:53619 ProcessPendingRequest(top_group_name, top_group);
620 } else if (!group->pending_requests.empty()) {
[email protected]ff579d42009-06-24 15:47:02621 ProcessPendingRequest(group_name, group);
622 // |group| may no longer be valid after this point. Be careful not to
623 // access it again.
[email protected]2ab05b52009-07-01 23:57:58624 } else if (group->IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02625 // Delete |group| if no longer needed. |group| will no longer be valid.
[email protected]ff579d42009-06-24 15:47:02626 group_map_.erase(group_name);
[email protected]ff579d42009-06-24 15:47:02627 }
628}
629
[email protected]d80a4322009-08-14 07:07:49630void ClientSocketPoolBaseHelper::ProcessPendingRequest(
631 const std::string& group_name, Group* group) {
[email protected]fd4fe0b2010-02-08 23:02:15632 scoped_ptr<const Request> r(*group->pending_requests.begin());
633 int rv = RequestSocketInternal(group_name, r.get());
[email protected]ff579d42009-06-24 15:47:02634
[email protected]2ab05b52009-07-01 23:57:58635 if (rv != ERR_IO_PENDING) {
[email protected]9e743cd2010-03-16 07:03:53636 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]fd4fe0b2010-02-08 23:02:15637 RemoveRequestFromQueue(group->pending_requests.begin(),
638 &group->pending_requests);
[email protected]d80a4322009-08-14 07:07:49639 r->callback()->Run(rv);
[email protected]2ab05b52009-07-01 23:57:58640 if (rv != OK) {
641 // |group| may be invalid after the callback, we need to search
642 // |group_map_| again.
643 MaybeOnAvailableSocketSlot(group_name);
644 }
[email protected]d80a4322009-08-14 07:07:49645 } else {
646 r.release();
[email protected]2ab05b52009-07-01 23:57:58647 }
648}
649
[email protected]d80a4322009-08-14 07:07:49650void ClientSocketPoolBaseHelper::HandOutSocket(
[email protected]2ab05b52009-07-01 23:57:58651 ClientSocket* socket,
652 bool reused,
653 ClientSocketHandle* handle,
[email protected]f9d285c2009-08-17 19:54:29654 base::TimeDelta idle_time,
[email protected]fd4fe0b2010-02-08 23:02:15655 Group* group,
[email protected]9e743cd2010-03-16 07:03:53656 const BoundNetLog& net_log) {
[email protected]2ab05b52009-07-01 23:57:58657 DCHECK(socket);
658 handle->set_socket(socket);
659 handle->set_is_reused(reused);
[email protected]f9d285c2009-08-17 19:54:29660 handle->set_idle_time(idle_time);
[email protected]211d2172009-07-22 15:48:53661
[email protected]fd4fe0b2010-02-08 23:02:15662 if (reused)
[email protected]9e743cd2010-03-16 07:03:53663 net_log.AddStringLiteral("Reusing socket.");
[email protected]fd4fe0b2010-02-08 23:02:15664 if (idle_time != base::TimeDelta()) {
[email protected]9e743cd2010-03-16 07:03:53665 net_log.AddString(
[email protected]fd4fe0b2010-02-08 23:02:15666 StringPrintf("Socket sat idle for %" PRId64 " milliseconds",
667 idle_time.InMilliseconds()));
668 }
669
[email protected]211d2172009-07-22 15:48:53670 handed_out_socket_count_++;
[email protected]2ab05b52009-07-01 23:57:58671 group->active_socket_count++;
[email protected]ff579d42009-06-24 15:47:02672}
673
[email protected]d80a4322009-08-14 07:07:49674void ClientSocketPoolBaseHelper::AddIdleSocket(
[email protected]5fc08e32009-07-15 17:09:57675 ClientSocket* socket, bool used, Group* group) {
676 DCHECK(socket);
677 IdleSocket idle_socket;
678 idle_socket.socket = socket;
679 idle_socket.start_time = base::TimeTicks::Now();
680 idle_socket.used = used;
681
682 group->idle_sockets.push_back(idle_socket);
683 IncrementIdleCount();
684}
685
[email protected]d80a4322009-08-14 07:07:49686void ClientSocketPoolBaseHelper::CancelAllConnectJobs() {
[email protected]5fc08e32009-07-15 17:09:57687 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end();) {
688 Group& group = i->second;
[email protected]4d3b05d2010-01-27 21:27:29689 connecting_socket_count_ -= group.jobs.size();
[email protected]5fc08e32009-07-15 17:09:57690 STLDeleteElements(&group.jobs);
691
[email protected]6b624c62010-03-14 08:37:32692 if (group.backup_task) {
693 group.backup_task->Cancel();
694 group.backup_task = NULL;
695 }
696
[email protected]5fc08e32009-07-15 17:09:57697 // Delete group if no longer needed.
698 if (group.IsEmpty()) {
[email protected]5fc08e32009-07-15 17:09:57699 group_map_.erase(i++);
700 } else {
701 ++i;
702 }
703 }
704}
705
[email protected]d80a4322009-08-14 07:07:49706bool ClientSocketPoolBaseHelper::ReachedMaxSocketsLimit() const {
[email protected]211d2172009-07-22 15:48:53707 // Each connecting socket will eventually connect and be handed out.
[email protected]42df4e8e2010-04-13 22:02:56708 int total = handed_out_socket_count_ + connecting_socket_count_;
[email protected]211d2172009-07-22 15:48:53709 DCHECK_LE(total, max_sockets_);
710 return total == max_sockets_;
711}
712
[email protected]d80a4322009-08-14 07:07:49713} // namespace internal
714
[email protected]ff579d42009-06-24 15:47:02715} // namespace net