blob: 9bc663f3527106b66eff1349a0859b5c0b7030b0 [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]a2006ece2010-04-23 16:44:0241 net_log_(net_log),
42 idle_(true) {
[email protected]2ab05b52009-07-01 23:57:5843 DCHECK(!group_name.empty());
[email protected]2ab05b52009-07-01 23:57:5844 DCHECK(delegate);
[email protected]06650c52010-06-03 00:49:1745 net_log.BeginEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB, NULL);
[email protected]2ab05b52009-07-01 23:57:5846}
47
[email protected]fd7b7c92009-08-20 19:38:3048ConnectJob::~ConnectJob() {
[email protected]06650c52010-06-03 00:49:1749 net_log().EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB, NULL);
[email protected]fd7b7c92009-08-20 19:38:3050}
[email protected]2ab05b52009-07-01 23:57:5851
[email protected]974ebd62009-08-03 23:14:3452int ConnectJob::Connect() {
53 if (timeout_duration_ != base::TimeDelta())
54 timer_.Start(timeout_duration_, this, &ConnectJob::OnTimeout);
[email protected]fd7b7c92009-08-20 19:38:3055
[email protected]a2006ece2010-04-23 16:44:0256 idle_ = false;
[email protected]fd7b7c92009-08-20 19:38:3057
[email protected]06650c52010-06-03 00:49:1758 LogConnectStart();
59
[email protected]fd7b7c92009-08-20 19:38:3060 int rv = ConnectInternal();
61
62 if (rv != ERR_IO_PENDING) {
[email protected]06650c52010-06-03 00:49:1763 LogConnectCompletion(rv);
[email protected]fd7b7c92009-08-20 19:38:3064 delegate_ = NULL;
[email protected]fd7b7c92009-08-20 19:38:3065 }
66
67 return rv;
68}
69
[email protected]06650c52010-06-03 00:49:1770void ConnectJob::set_socket(ClientSocket* socket) {
71 if (socket) {
72 net_log().AddEvent(NetLog::TYPE_CONNECT_JOB_SET_SOCKET,
73 new NetLogSourceParameter("source_dependency",
74 socket->NetLog().source()));
75 }
76 socket_.reset(socket);
77}
78
[email protected]fd7b7c92009-08-20 19:38:3079void ConnectJob::NotifyDelegateOfCompletion(int rv) {
80 // The delegate will delete |this|.
81 Delegate *delegate = delegate_;
82 delegate_ = NULL;
83
[email protected]06650c52010-06-03 00:49:1784 LogConnectCompletion(rv);
[email protected]fd7b7c92009-08-20 19:38:3085 delegate->OnConnectJobComplete(rv, this);
[email protected]974ebd62009-08-03 23:14:3486}
87
[email protected]a796bcec2010-03-22 17:17:2688void ConnectJob::ResetTimer(base::TimeDelta remaining_time) {
89 timer_.Stop();
90 timer_.Start(remaining_time, this, &ConnectJob::OnTimeout);
91}
92
[email protected]06650c52010-06-03 00:49:1793void ConnectJob::LogConnectStart() {
94 net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_CONNECT,
95 new NetLogStringParameter("group_name", group_name_));
96}
97
98void ConnectJob::LogConnectCompletion(int net_error) {
99 scoped_refptr<NetLog::EventParameters> params;
100 if (net_error != OK)
101 params = new NetLogIntegerParameter("net_error", net_error);
102 net_log().EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_CONNECT, params);
103}
104
[email protected]974ebd62009-08-03 23:14:34105void ConnectJob::OnTimeout() {
[email protected]6e713f02009-08-06 02:56:40106 // Make sure the socket is NULL before calling into |delegate|.
107 set_socket(NULL);
[email protected]fd7b7c92009-08-20 19:38:30108
[email protected]ec11be62010-04-28 19:28:09109 net_log_.AddEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_TIMED_OUT, NULL);
[email protected]fd7b7c92009-08-20 19:38:30110
111 NotifyDelegateOfCompletion(ERR_TIMED_OUT);
[email protected]974ebd62009-08-03 23:14:34112}
113
[email protected]d80a4322009-08-14 07:07:49114namespace internal {
115
[email protected]fd4fe0b2010-02-08 23:02:15116ClientSocketPoolBaseHelper::Request::Request(
117 ClientSocketHandle* handle,
118 CompletionCallback* callback,
119 RequestPriority priority,
[email protected]9e743cd2010-03-16 07:03:53120 const BoundNetLog& net_log)
[email protected]fd4fe0b2010-02-08 23:02:15121 : handle_(handle), callback_(callback), priority_(priority),
[email protected]9e743cd2010-03-16 07:03:53122 net_log_(net_log) {}
[email protected]fd4fe0b2010-02-08 23:02:15123
124ClientSocketPoolBaseHelper::Request::~Request() {}
125
[email protected]d80a4322009-08-14 07:07:49126ClientSocketPoolBaseHelper::ClientSocketPoolBaseHelper(
[email protected]211d2172009-07-22 15:48:53127 int max_sockets,
[email protected]ff579d42009-06-24 15:47:02128 int max_sockets_per_group,
[email protected]9bf28db2009-08-29 01:35:16129 base::TimeDelta unused_idle_socket_timeout,
130 base::TimeDelta used_idle_socket_timeout,
[email protected]66761b952010-06-25 21:30:38131 ConnectJobFactory* connect_job_factory)
[email protected]ff579d42009-06-24 15:47:02132 : idle_socket_count_(0),
[email protected]211d2172009-07-22 15:48:53133 connecting_socket_count_(0),
134 handed_out_socket_count_(0),
[email protected]8ae03f42010-07-07 19:08:10135 num_releasing_sockets_(0),
[email protected]211d2172009-07-22 15:48:53136 max_sockets_(max_sockets),
[email protected]ff579d42009-06-24 15:47:02137 max_sockets_per_group_(max_sockets_per_group),
[email protected]9bf28db2009-08-29 01:35:16138 unused_idle_socket_timeout_(unused_idle_socket_timeout),
139 used_idle_socket_timeout_(used_idle_socket_timeout),
[email protected]8ae03f42010-07-07 19:08:10140 may_have_stalled_group_(false),
[email protected]100d5fb92009-12-21 21:08:35141 connect_job_factory_(connect_job_factory),
[email protected]7c28e9a2010-03-20 01:16:13142 backup_jobs_enabled_(false),
[email protected]a7e38572010-06-07 18:22:24143 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
[email protected]8ae03f42010-07-07 19:08:10144 pool_generation_number_(0) {
[email protected]211d2172009-07-22 15:48:53145 DCHECK_LE(0, max_sockets_per_group);
146 DCHECK_LE(max_sockets_per_group, max_sockets);
[email protected]a554a8262010-05-20 00:13:52147
[email protected]66761b952010-06-25 21:30:38148 NetworkChangeNotifier::AddObserver(this);
[email protected]211d2172009-07-22 15:48:53149}
[email protected]ff579d42009-06-24 15:47:02150
[email protected]d80a4322009-08-14 07:07:49151ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() {
[email protected]4d3b05d2010-01-27 21:27:29152 CancelAllConnectJobs();
153
[email protected]ff579d42009-06-24 15:47:02154 // Clean up any idle sockets. Assert that we have no remaining active
155 // sockets or pending requests. They should have all been cleaned up prior
156 // to the manager being destroyed.
157 CloseIdleSockets();
[email protected]6b624c62010-03-14 08:37:32158 CHECK(group_map_.empty());
[email protected]4d3b05d2010-01-27 21:27:29159 DCHECK_EQ(0, connecting_socket_count_);
[email protected]a554a8262010-05-20 00:13:52160
[email protected]66761b952010-06-25 21:30:38161 NetworkChangeNotifier::RemoveObserver(this);
[email protected]ff579d42009-06-24 15:47:02162}
163
164// InsertRequestIntoQueue inserts the request into the queue based on
165// priority. Highest priorities are closest to the front. Older requests are
166// prioritized over requests of equal priority.
167//
168// static
[email protected]d80a4322009-08-14 07:07:49169void ClientSocketPoolBaseHelper::InsertRequestIntoQueue(
170 const Request* r, RequestQueue* pending_requests) {
[email protected]ff579d42009-06-24 15:47:02171 RequestQueue::iterator it = pending_requests->begin();
[email protected]ac790b42009-12-02 04:31:31172 while (it != pending_requests->end() && r->priority() >= (*it)->priority())
[email protected]ff579d42009-06-24 15:47:02173 ++it;
174 pending_requests->insert(it, r);
175}
176
[email protected]fd7b7c92009-08-20 19:38:30177// static
178const ClientSocketPoolBaseHelper::Request*
179ClientSocketPoolBaseHelper::RemoveRequestFromQueue(
180 RequestQueue::iterator it, RequestQueue* pending_requests) {
181 const Request* req = *it;
[email protected]fd7b7c92009-08-20 19:38:30182 pending_requests->erase(it);
183 return req;
184}
185
[email protected]d80a4322009-08-14 07:07:49186int ClientSocketPoolBaseHelper::RequestSocket(
[email protected]ff579d42009-06-24 15:47:02187 const std::string& group_name,
[email protected]d80a4322009-08-14 07:07:49188 const Request* request) {
[email protected]ec11be62010-04-28 19:28:09189 request->net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL, NULL);
[email protected]fd4fe0b2010-02-08 23:02:15190 Group& group = group_map_[group_name];
191 int rv = RequestSocketInternal(group_name, request);
[email protected]e7e99322010-05-04 23:30:17192 if (rv != ERR_IO_PENDING) {
[email protected]ec11be62010-04-28 19:28:09193 request->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL);
[email protected]e7e99322010-05-04 23:30:17194 delete request;
195 } else {
[email protected]fd4fe0b2010-02-08 23:02:15196 InsertRequestIntoQueue(request, &group.pending_requests);
[email protected]e7e99322010-05-04 23:30:17197 }
[email protected]fd4fe0b2010-02-08 23:02:15198 return rv;
199}
200
201int ClientSocketPoolBaseHelper::RequestSocketInternal(
202 const std::string& group_name,
203 const Request* request) {
[email protected]d80a4322009-08-14 07:07:49204 DCHECK_GE(request->priority(), 0);
205 CompletionCallback* const callback = request->callback();
206 CHECK(callback);
207 ClientSocketHandle* const handle = request->handle();
208 CHECK(handle);
[email protected]ff579d42009-06-24 15:47:02209 Group& group = group_map_[group_name];
210
[email protected]65552102010-04-09 22:58:10211 // Try to reuse a socket.
[email protected]8ae03f42010-07-07 19:08:10212 while (!group.idle_sockets.empty()) {
213 IdleSocket idle_socket = group.idle_sockets.back();
214 group.idle_sockets.pop_back();
215 DecrementIdleCount();
216 if (idle_socket.socket->IsConnectedAndIdle()) {
217 // We found one we can reuse!
218 base::TimeDelta idle_time =
219 base::TimeTicks::Now() - idle_socket.start_time;
220 HandOutSocket(
221 idle_socket.socket, idle_socket.used, handle, idle_time, &group,
222 request->net_log());
223 return OK;
224 }
225 delete idle_socket.socket;
226 }
[email protected]65552102010-04-09 22:58:10227
[email protected]43a21b82010-06-10 21:30:54228 // Can we make another active socket now?
229 if (!group.HasAvailableSocketSlot(max_sockets_per_group_)) {
230 request->net_log().AddEvent(
231 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP, NULL);
232 return ERR_IO_PENDING;
233 }
234
235 if (ReachedMaxSocketsLimit()) {
236 if (idle_socket_count() > 0) {
237 CloseOneIdleSocket();
238 } else {
239 // We could check if we really have a stalled group here, but it requires
240 // a scan of all groups, so just flip a flag here, and do the check later.
[email protected]8ae03f42010-07-07 19:08:10241 may_have_stalled_group_ = true;
[email protected]43a21b82010-06-10 21:30:54242 request->net_log().AddEvent(
243 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL);
244 return ERR_IO_PENDING;
245 }
246 }
247
[email protected]8ae03f42010-07-07 19:08:10248 // See if we already have enough connect jobs or sockets that will be released
249 // soon.
250 if (group.HasReleasingSockets()) {
251 return ERR_IO_PENDING;
252 }
253
[email protected]ff579d42009-06-24 15:47:02254 // We couldn't find a socket to reuse, so allocate and connect a new one.
[email protected]2ab05b52009-07-01 23:57:58255 scoped_ptr<ConnectJob> connect_job(
[email protected]06650c52010-06-03 00:49:17256 connect_job_factory_->NewConnectJob(group_name, *request, this));
[email protected]ff579d42009-06-24 15:47:02257
[email protected]2ab05b52009-07-01 23:57:58258 int rv = connect_job->Connect();
259 if (rv == OK) {
[email protected]06650c52010-06-03 00:49:17260 LogBoundConnectJobToRequest(connect_job->net_log().source(), request);
[email protected]2ab05b52009-07-01 23:57:58261 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */,
[email protected]9e743cd2010-03-16 07:03:53262 handle, base::TimeDelta(), &group, request->net_log());
[email protected]2ab05b52009-07-01 23:57:58263 } else if (rv == ERR_IO_PENDING) {
[email protected]6b624c62010-03-14 08:37:32264 // If we don't have any sockets in this group, set a timer for potentially
265 // creating a new one. If the SYN is lost, this backup socket may complete
266 // before the slow socket, improving end user latency.
[email protected]7c28e9a2010-03-20 01:16:13267 if (group.IsEmpty() && !group.backup_job && backup_jobs_enabled_) {
[email protected]6b624c62010-03-14 08:37:32268 group.backup_job = connect_job_factory_->NewConnectJob(group_name,
269 *request,
[email protected]06650c52010-06-03 00:49:17270 this);
[email protected]6b624c62010-03-14 08:37:32271 StartBackupSocketTimer(group_name);
272 }
273
[email protected]211d2172009-07-22 15:48:53274 connecting_socket_count_++;
275
[email protected]5fc08e32009-07-15 17:09:57276 ConnectJob* job = connect_job.release();
[email protected]5fc08e32009-07-15 17:09:57277 group.jobs.insert(job);
[email protected]a2006ece2010-04-23 16:44:02278 } else {
[email protected]06650c52010-06-03 00:49:17279 LogBoundConnectJobToRequest(connect_job->net_log().source(), request);
[email protected]a2006ece2010-04-23 16:44:02280 if (group.IsEmpty())
281 group_map_.erase(group_name);
[email protected]2ab05b52009-07-01 23:57:58282 }
[email protected]ff579d42009-06-24 15:47:02283
[email protected]2ab05b52009-07-01 23:57:58284 return rv;
[email protected]ff579d42009-06-24 15:47:02285}
286
[email protected]06650c52010-06-03 00:49:17287// static
288void ClientSocketPoolBaseHelper::LogBoundConnectJobToRequest(
289 const NetLog::Source& connect_job_source, const Request* request) {
290 request->net_log().AddEvent(
291 NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB,
292 new NetLogSourceParameter("source_dependency", connect_job_source));
293}
294
[email protected]6b624c62010-03-14 08:37:32295void ClientSocketPoolBaseHelper::StartBackupSocketTimer(
296 const std::string& group_name) {
297 CHECK(ContainsKey(group_map_, group_name));
298 Group& group = group_map_[group_name];
299
300 // Only allow one timer pending to create a backup socket.
301 if (group.backup_task)
302 return;
303
304 group.backup_task = method_factory_.NewRunnableMethod(
305 &ClientSocketPoolBaseHelper::OnBackupSocketTimerFired, group_name);
306 MessageLoop::current()->PostDelayedTask(FROM_HERE, group.backup_task,
307 ConnectRetryIntervalMs());
308}
309
310void ClientSocketPoolBaseHelper::OnBackupSocketTimerFired(
311 const std::string& group_name) {
312 CHECK(ContainsKey(group_map_, group_name));
313
314 Group& group = group_map_[group_name];
315
316 CHECK(group.backup_task);
317 group.backup_task = NULL;
318
319 CHECK(group.backup_job);
320
[email protected]25eea382010-07-10 23:55:26321 // If there are no more jobs pending, there is no work to do.
322 // If we've done our cleanups correctly, this should not happen.
323 if (group.jobs.empty()) {
324 NOTREACHED();
325 return;
326 }
327
[email protected]c901f6d2010-04-27 17:48:28328 // If our backup job is waiting on DNS, or if we can't create any sockets
329 // right now due to limits, just reset the timer.
[email protected]c901f6d2010-04-27 17:48:28330 if (ReachedMaxSocketsLimit() ||
331 !group.HasAvailableSocketSlot(max_sockets_per_group_) ||
332 (*group.jobs.begin())->GetLoadState() == LOAD_STATE_RESOLVING_HOST) {
[email protected]6b624c62010-03-14 08:37:32333 StartBackupSocketTimer(group_name);
334 return;
335 }
336
[email protected]ec11be62010-04-28 19:28:09337 group.backup_job->net_log().AddEvent(NetLog::TYPE_SOCKET_BACKUP_CREATED,
338 NULL);
[email protected]6b624c62010-03-14 08:37:32339 SIMPLE_STATS_COUNTER("socket.backup_created");
340 int rv = group.backup_job->Connect();
[email protected]c83658c2010-03-24 08:19:34341 connecting_socket_count_++;
342 group.jobs.insert(group.backup_job);
343 ConnectJob* job = group.backup_job;
344 group.backup_job = NULL;
345 if (rv != ERR_IO_PENDING)
346 OnConnectJobComplete(rv, job);
[email protected]6b624c62010-03-14 08:37:32347}
348
[email protected]d80a4322009-08-14 07:07:49349void ClientSocketPoolBaseHelper::CancelRequest(
350 const std::string& group_name, const ClientSocketHandle* handle) {
[email protected]b6501d3d2010-06-03 23:53:34351 // Running callbacks can cause the last outside reference to be released.
352 // Hold onto a reference.
353 scoped_refptr<ClientSocketPoolBaseHelper> ref_holder(this);
354
[email protected]ff579d42009-06-24 15:47:02355 CHECK(ContainsKey(group_map_, group_name));
356
357 Group& group = group_map_[group_name];
358
[email protected]ff579d42009-06-24 15:47:02359 // Search pending_requests for matching handle.
360 RequestQueue::iterator it = group.pending_requests.begin();
361 for (; it != group.pending_requests.end(); ++it) {
[email protected]d80a4322009-08-14 07:07:49362 if ((*it)->handle() == handle) {
[email protected]fd7b7c92009-08-20 19:38:30363 const Request* req = RemoveRequestFromQueue(it, &group.pending_requests);
[email protected]ec11be62010-04-28 19:28:09364 req->net_log().AddEvent(NetLog::TYPE_CANCELLED, NULL);
365 req->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL);
[email protected]fd7b7c92009-08-20 19:38:30366 delete req;
[email protected]8ae03f42010-07-07 19:08:10367 // Let one connect job connect and become idle for potential future use.
368 if (group.jobs.size() > group.pending_requests.size() + 1) {
369 // TODO(willchan): Cancel the job in the earliest LoadState.
[email protected]4d3b05d2010-01-27 21:27:29370 RemoveConnectJob(*group.jobs.begin(), &group);
[email protected]8ae03f42010-07-07 19:08:10371 OnAvailableSocketSlot(group_name, &group);
[email protected]974ebd62009-08-03 23:14:34372 }
[email protected]8ae03f42010-07-07 19:08:10373 return;
[email protected]ff579d42009-06-24 15:47:02374 }
375 }
[email protected]ff579d42009-06-24 15:47:02376}
377
[email protected]8ae03f42010-07-07 19:08:10378void ClientSocketPoolBaseHelper::ReleaseSocket(const std::string& group_name,
379 ClientSocket* socket,
380 int id) {
381 Group& group = group_map_[group_name];
382 group.num_releasing_sockets++;
383 num_releasing_sockets_++;
384 DCHECK_LE(group.num_releasing_sockets, group.active_socket_count);
385 // Run this asynchronously to allow the caller to finish before we let
386 // another to begin doing work. This also avoids nasty recursion issues.
387 // NOTE: We cannot refer to the handle argument after this method returns.
388 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(this,
389 &ClientSocketPoolBaseHelper::DoReleaseSocket, group_name, socket, id));
390}
391
[email protected]d80a4322009-08-14 07:07:49392void ClientSocketPoolBaseHelper::CloseIdleSockets() {
[email protected]ff579d42009-06-24 15:47:02393 CleanupIdleSockets(true);
394}
395
[email protected]d80a4322009-08-14 07:07:49396int ClientSocketPoolBaseHelper::IdleSocketCountInGroup(
[email protected]ff579d42009-06-24 15:47:02397 const std::string& group_name) const {
398 GroupMap::const_iterator i = group_map_.find(group_name);
399 CHECK(i != group_map_.end());
400
401 return i->second.idle_sockets.size();
402}
403
[email protected]d80a4322009-08-14 07:07:49404LoadState ClientSocketPoolBaseHelper::GetLoadState(
[email protected]ff579d42009-06-24 15:47:02405 const std::string& group_name,
406 const ClientSocketHandle* handle) const {
407 if (!ContainsKey(group_map_, group_name)) {
408 NOTREACHED() << "ClientSocketPool does not contain group: " << group_name
409 << " for handle: " << handle;
410 return LOAD_STATE_IDLE;
411 }
412
413 // Can't use operator[] since it is non-const.
414 const Group& group = group_map_.find(group_name)->second;
415
[email protected]ff579d42009-06-24 15:47:02416 // Search pending_requests for matching handle.
417 RequestQueue::const_iterator it = group.pending_requests.begin();
[email protected]5fc08e32009-07-15 17:09:57418 for (size_t i = 0; it != group.pending_requests.end(); ++it, ++i) {
[email protected]d80a4322009-08-14 07:07:49419 if ((*it)->handle() == handle) {
[email protected]4d3b05d2010-01-27 21:27:29420 if (i < group.jobs.size()) {
[email protected]5fc08e32009-07-15 17:09:57421 LoadState max_state = LOAD_STATE_IDLE;
422 for (ConnectJobSet::const_iterator job_it = group.jobs.begin();
423 job_it != group.jobs.end(); ++job_it) {
[email protected]46451352009-09-01 14:54:21424 max_state = std::max(max_state, (*job_it)->GetLoadState());
[email protected]5fc08e32009-07-15 17:09:57425 }
426 return max_state;
427 } else {
428 // TODO(wtc): Add a state for being on the wait list.
429 // See http://www.crbug.com/5077.
430 return LOAD_STATE_IDLE;
431 }
[email protected]ff579d42009-06-24 15:47:02432 }
433 }
434
435 NOTREACHED();
436 return LOAD_STATE_IDLE;
437}
438
[email protected]d80a4322009-08-14 07:07:49439bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup(
[email protected]9bf28db2009-08-29 01:35:16440 base::TimeTicks now,
441 base::TimeDelta timeout) const {
442 bool timed_out = (now - start_time) >= timeout;
[email protected]5fc08e32009-07-15 17:09:57443 return timed_out ||
444 !(used ? socket->IsConnectedAndIdle() : socket->IsConnected());
[email protected]ff579d42009-06-24 15:47:02445}
446
[email protected]d80a4322009-08-14 07:07:49447void ClientSocketPoolBaseHelper::CleanupIdleSockets(bool force) {
[email protected]ff579d42009-06-24 15:47:02448 if (idle_socket_count_ == 0)
449 return;
450
451 // Current time value. Retrieving it once at the function start rather than
452 // inside the inner loop, since it shouldn't change by any meaningful amount.
453 base::TimeTicks now = base::TimeTicks::Now();
454
455 GroupMap::iterator i = group_map_.begin();
456 while (i != group_map_.end()) {
457 Group& group = i->second;
458
459 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
460 while (j != group.idle_sockets.end()) {
[email protected]9bf28db2009-08-29 01:35:16461 base::TimeDelta timeout =
462 j->used ? used_idle_socket_timeout_ : unused_idle_socket_timeout_;
463 if (force || j->ShouldCleanup(now, timeout)) {
[email protected]ff579d42009-06-24 15:47:02464 delete j->socket;
465 j = group.idle_sockets.erase(j);
466 DecrementIdleCount();
467 } else {
468 ++j;
469 }
470 }
471
472 // Delete group if no longer needed.
[email protected]2ab05b52009-07-01 23:57:58473 if (group.IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02474 group_map_.erase(i++);
475 } else {
476 ++i;
477 }
478 }
479}
480
[email protected]d80a4322009-08-14 07:07:49481void ClientSocketPoolBaseHelper::IncrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02482 if (++idle_socket_count_ == 1)
483 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this,
[email protected]d80a4322009-08-14 07:07:49484 &ClientSocketPoolBaseHelper::OnCleanupTimerFired);
[email protected]ff579d42009-06-24 15:47:02485}
486
[email protected]d80a4322009-08-14 07:07:49487void ClientSocketPoolBaseHelper::DecrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02488 if (--idle_socket_count_ == 0)
489 timer_.Stop();
490}
491
[email protected]8ae03f42010-07-07 19:08:10492void ClientSocketPoolBaseHelper::DoReleaseSocket(const std::string& group_name,
493 ClientSocket* socket,
494 int id) {
[email protected]b6501d3d2010-06-03 23:53:34495 // Running callbacks can cause the last outside reference to be released.
496 // Hold onto a reference.
497 scoped_refptr<ClientSocketPoolBaseHelper> ref_holder(this);
498
[email protected]ff579d42009-06-24 15:47:02499 GroupMap::iterator i = group_map_.find(group_name);
500 CHECK(i != group_map_.end());
501
502 Group& group = i->second;
503
[email protected]8ae03f42010-07-07 19:08:10504 group.num_releasing_sockets--;
505 DCHECK_GE(group.num_releasing_sockets, 0);
506
[email protected]b1f031dd2010-03-02 23:19:33507 CHECK_GT(handed_out_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53508 handed_out_socket_count_--;
509
[email protected]b1f031dd2010-03-02 23:19:33510 CHECK_GT(group.active_socket_count, 0);
[email protected]2ab05b52009-07-01 23:57:58511 group.active_socket_count--;
[email protected]ff579d42009-06-24 15:47:02512
[email protected]8ae03f42010-07-07 19:08:10513 CHECK_GT(num_releasing_sockets_, 0);
514 num_releasing_sockets_--;
515
[email protected]a7e38572010-06-07 18:22:24516 const bool can_reuse = socket->IsConnectedAndIdle() &&
517 id == pool_generation_number_;
[email protected]ff579d42009-06-24 15:47:02518 if (can_reuse) {
[email protected]5fc08e32009-07-15 17:09:57519 AddIdleSocket(socket, true /* used socket */, &group);
[email protected]ff579d42009-06-24 15:47:02520 } else {
521 delete socket;
522 }
523
[email protected]8ae03f42010-07-07 19:08:10524 // If there are no more releasing sockets, then we might have to process
525 // multiple available socket slots, since we stalled their processing until
526 // all sockets have been released. Note that ProcessPendingRequest() will
527 // invoke user callbacks, so |num_releasing_sockets_| may change.
528 //
529 // This code has been known to infinite loop. Set a counter and CHECK to make
530 // sure it doesn't get ridiculously high.
[email protected]4f2abec2010-02-03 18:10:16531
[email protected]8ae03f42010-07-07 19:08:10532 int iterations = 0;
533 while (num_releasing_sockets_ == 0) {
534 CHECK_LT(iterations, 1000) << "Probably stuck in an infinite loop.";
535 std::string top_group_name;
536 Group* top_group = NULL;
537 int stalled_group_count = FindTopStalledGroup(&top_group, &top_group_name);
538 if (stalled_group_count >= 1) {
539 if (ReachedMaxSocketsLimit()) {
540 if (idle_socket_count() > 0) {
541 CloseOneIdleSocket();
542 } else {
543 // We can't activate more sockets since we're already at our global
544 // limit.
545 may_have_stalled_group_ = true;
546 return;
547 }
548 }
549
550 ProcessPendingRequest(top_group_name, top_group);
[email protected]d7027bb2010-05-10 18:58:54551 } else {
[email protected]8ae03f42010-07-07 19:08:10552 may_have_stalled_group_ = false;
[email protected]4f2abec2010-02-03 18:10:16553 return;
[email protected]d7027bb2010-05-10 18:58:54554 }
[email protected]8ae03f42010-07-07 19:08:10555
556 iterations++;
[email protected]4f2abec2010-02-03 18:10:16557 }
[email protected]ff579d42009-06-24 15:47:02558}
559
[email protected]211d2172009-07-22 15:48:53560// Search for the highest priority pending request, amongst the groups that
561// are not at the |max_sockets_per_group_| limit. Note: for requests with
562// the same priority, the winner is based on group hash ordering (and not
563// insertion order).
[email protected]d80a4322009-08-14 07:07:49564int ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group,
565 std::string* group_name) {
[email protected]211d2172009-07-22 15:48:53566 Group* top_group = NULL;
567 const std::string* top_group_name = NULL;
568 int stalled_group_count = 0;
569 for (GroupMap::iterator i = group_map_.begin();
570 i != group_map_.end(); ++i) {
571 Group& group = i->second;
572 const RequestQueue& queue = group.pending_requests;
573 if (queue.empty())
574 continue;
[email protected]6427fe22010-04-16 22:27:41575 bool has_unused_slot =
576 group.HasAvailableSocketSlot(max_sockets_per_group_) &&
577 group.pending_requests.size() > group.jobs.size();
578 if (has_unused_slot) {
[email protected]211d2172009-07-22 15:48:53579 stalled_group_count++;
[email protected]6427fe22010-04-16 22:27:41580 bool has_higher_priority = !top_group ||
581 group.TopPendingPriority() < top_group->TopPendingPriority();
582 if (has_higher_priority) {
583 top_group = &group;
584 top_group_name = &i->first;
585 }
[email protected]211d2172009-07-22 15:48:53586 }
587 }
588 if (top_group) {
589 *group = top_group;
590 *group_name = *top_group_name;
591 }
592 return stalled_group_count;
593}
594
[email protected]d80a4322009-08-14 07:07:49595void ClientSocketPoolBaseHelper::OnConnectJobComplete(
596 int result, ConnectJob* job) {
[email protected]b6501d3d2010-06-03 23:53:34597 // Running callbacks can cause the last outside reference to be released.
598 // Hold onto a reference.
599 scoped_refptr<ClientSocketPoolBaseHelper> ref_holder(this);
600
[email protected]2ab05b52009-07-01 23:57:58601 DCHECK_NE(ERR_IO_PENDING, result);
602 const std::string group_name = job->group_name();
[email protected]ff579d42009-06-24 15:47:02603 GroupMap::iterator group_it = group_map_.find(group_name);
604 CHECK(group_it != group_map_.end());
605 Group& group = group_it->second;
606
[email protected]5fc08e32009-07-15 17:09:57607 scoped_ptr<ClientSocket> socket(job->ReleaseSocket());
[email protected]ff579d42009-06-24 15:47:02608
[email protected]9e743cd2010-03-16 07:03:53609 BoundNetLog job_log = job->net_log();
[email protected]4d3b05d2010-01-27 21:27:29610 RemoveConnectJob(job, &group);
[email protected]5fc08e32009-07-15 17:09:57611
[email protected]4d3b05d2010-01-27 21:27:29612 if (result == OK) {
613 DCHECK(socket.get());
[email protected]fd7b7c92009-08-20 19:38:30614 if (!group.pending_requests.empty()) {
[email protected]4d3b05d2010-01-27 21:27:29615 scoped_ptr<const Request> r(RemoveRequestFromQueue(
[email protected]fd7b7c92009-08-20 19:38:30616 group.pending_requests.begin(), &group.pending_requests));
[email protected]06650c52010-06-03 00:49:17617 LogBoundConnectJobToRequest(job_log.source(), r.get());
[email protected]4d3b05d2010-01-27 21:27:29618 HandOutSocket(
619 socket.release(), false /* unused socket */, r->handle(),
[email protected]9e743cd2010-03-16 07:03:53620 base::TimeDelta(), &group, r->net_log());
[email protected]06650c52010-06-03 00:49:17621 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL);
[email protected]4d3b05d2010-01-27 21:27:29622 r->callback()->Run(result);
[email protected]5fc08e32009-07-15 17:09:57623 } else {
[email protected]4d3b05d2010-01-27 21:27:29624 AddIdleSocket(socket.release(), false /* unused socket */, &group);
[email protected]8ae03f42010-07-07 19:08:10625 OnAvailableSocketSlot(group_name, &group);
[email protected]5fc08e32009-07-15 17:09:57626 }
[email protected]94c20472010-01-14 08:14:36627 } else {
[email protected]4d3b05d2010-01-27 21:27:29628 DCHECK(!socket.get());
629 if (!group.pending_requests.empty()) {
630 scoped_ptr<const Request> r(RemoveRequestFromQueue(
631 group.pending_requests.begin(), &group.pending_requests));
[email protected]06650c52010-06-03 00:49:17632 LogBoundConnectJobToRequest(job_log.source(), r.get());
633 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL,
634 new NetLogIntegerParameter("net_error", result));
[email protected]4d3b05d2010-01-27 21:27:29635 r->callback()->Run(result);
636 }
[email protected]8ae03f42010-07-07 19:08:10637 MaybeOnAvailableSocketSlot(group_name);
[email protected]ff579d42009-06-24 15:47:02638 }
[email protected]ff579d42009-06-24 15:47:02639}
640
[email protected]66761b952010-06-25 21:30:38641void ClientSocketPoolBaseHelper::OnIPAddressChanged() {
642 Flush();
643}
644
[email protected]a7e38572010-06-07 18:22:24645void ClientSocketPoolBaseHelper::Flush() {
646 pool_generation_number_++;
[email protected]b6501d3d2010-06-03 23:53:34647 CancelAllConnectJobs();
[email protected]a554a8262010-05-20 00:13:52648 CloseIdleSockets();
649}
650
[email protected]25eea382010-07-10 23:55:26651void ClientSocketPoolBaseHelper::RemoveConnectJob(const ConnectJob* job,
[email protected]4d3b05d2010-01-27 21:27:29652 Group* group) {
[email protected]b1f031dd2010-03-02 23:19:33653 CHECK_GT(connecting_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53654 connecting_socket_count_--;
655
[email protected]25eea382010-07-10 23:55:26656 DCHECK(group);
657 DCHECK(ContainsKey(group->jobs, job));
658 group->jobs.erase(job);
659
660 // If we've got no more jobs for this group, then we no longer need a
661 // backup job either.
662 if (group->jobs.empty())
663 group->CleanupBackupJob();
664
[email protected]8ae03f42010-07-07 19:08:10665 DCHECK(job);
666 delete job;
[email protected]2ab05b52009-07-01 23:57:58667}
[email protected]ff579d42009-06-24 15:47:02668
[email protected]8ae03f42010-07-07 19:08:10669void ClientSocketPoolBaseHelper::MaybeOnAvailableSocketSlot(
670 const std::string& group_name) {
[email protected]934152ea2010-06-29 01:02:50671 GroupMap::iterator it = group_map_.find(group_name);
672 if (it != group_map_.end()) {
673 Group& group = it->second;
[email protected]8ae03f42010-07-07 19:08:10674 if (group.HasAvailableSocketSlot(max_sockets_per_group_))
675 OnAvailableSocketSlot(group_name, &group);
676 }
677}
[email protected]ff579d42009-06-24 15:47:02678
[email protected]8ae03f42010-07-07 19:08:10679void ClientSocketPoolBaseHelper::OnAvailableSocketSlot(
680 const std::string& group_name, Group* group) {
681 if (may_have_stalled_group_) {
682 std::string top_group_name;
683 Group* top_group = NULL;
684 int stalled_group_count = FindTopStalledGroup(&top_group, &top_group_name);
685 if (stalled_group_count == 0 ||
686 (stalled_group_count == 1 && top_group->num_releasing_sockets == 0)) {
687 may_have_stalled_group_ = false;
688 }
689 if (stalled_group_count >= 1)
690 ProcessPendingRequest(top_group_name, top_group);
691 } else if (!group->pending_requests.empty()) {
692 ProcessPendingRequest(group_name, group);
693 // |group| may no longer be valid after this point. Be careful not to
694 // access it again.
695 } else if (group->IsEmpty()) {
696 // Delete |group| if no longer needed. |group| will no longer be valid.
697 group_map_.erase(group_name);
698 }
699}
[email protected]06650c52010-06-03 00:49:17700
[email protected]8ae03f42010-07-07 19:08:10701void ClientSocketPoolBaseHelper::ProcessPendingRequest(
702 const std::string& group_name, Group* group) {
703 int rv = RequestSocketInternal(group_name, *group->pending_requests.begin());
704
705 if (rv != ERR_IO_PENDING) {
706 scoped_ptr<const Request> r(RemoveRequestFromQueue(
707 group->pending_requests.begin(), &group->pending_requests));
708
709 scoped_refptr<NetLog::EventParameters> params;
710 if (rv != OK)
711 params = new NetLogIntegerParameter("net_error", rv);
712 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, params);
713 r->callback()->Run(rv);
714 if (rv != OK) {
715 // |group| may be invalid after the callback, we need to search
716 // |group_map_| again.
717 MaybeOnAvailableSocketSlot(group_name);
[email protected]2ab05b52009-07-01 23:57:58718 }
719 }
720}
721
[email protected]d80a4322009-08-14 07:07:49722void ClientSocketPoolBaseHelper::HandOutSocket(
[email protected]2ab05b52009-07-01 23:57:58723 ClientSocket* socket,
724 bool reused,
725 ClientSocketHandle* handle,
[email protected]f9d285c2009-08-17 19:54:29726 base::TimeDelta idle_time,
[email protected]fd4fe0b2010-02-08 23:02:15727 Group* group,
[email protected]9e743cd2010-03-16 07:03:53728 const BoundNetLog& net_log) {
[email protected]2ab05b52009-07-01 23:57:58729 DCHECK(socket);
730 handle->set_socket(socket);
731 handle->set_is_reused(reused);
[email protected]f9d285c2009-08-17 19:54:29732 handle->set_idle_time(idle_time);
[email protected]a7e38572010-06-07 18:22:24733 handle->set_pool_id(pool_generation_number_);
[email protected]211d2172009-07-22 15:48:53734
[email protected]d13f51b2010-04-27 23:20:45735 if (reused) {
[email protected]ec11be62010-04-28 19:28:09736 net_log.AddEvent(
[email protected]d13f51b2010-04-27 23:20:45737 NetLog::TYPE_SOCKET_POOL_REUSED_AN_EXISTING_SOCKET,
[email protected]ec11be62010-04-28 19:28:09738 new NetLogIntegerParameter(
739 "idle_ms", static_cast<int>(idle_time.InMilliseconds())));
[email protected]fd4fe0b2010-02-08 23:02:15740 }
[email protected]d13f51b2010-04-27 23:20:45741
[email protected]06650c52010-06-03 00:49:17742 net_log.AddEvent(NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET,
743 new NetLogSourceParameter(
744 "source_dependency", socket->NetLog().source()));
[email protected]fd4fe0b2010-02-08 23:02:15745
[email protected]211d2172009-07-22 15:48:53746 handed_out_socket_count_++;
[email protected]2ab05b52009-07-01 23:57:58747 group->active_socket_count++;
[email protected]ff579d42009-06-24 15:47:02748}
749
[email protected]d80a4322009-08-14 07:07:49750void ClientSocketPoolBaseHelper::AddIdleSocket(
[email protected]5fc08e32009-07-15 17:09:57751 ClientSocket* socket, bool used, Group* group) {
752 DCHECK(socket);
753 IdleSocket idle_socket;
754 idle_socket.socket = socket;
755 idle_socket.start_time = base::TimeTicks::Now();
756 idle_socket.used = used;
757
758 group->idle_sockets.push_back(idle_socket);
759 IncrementIdleCount();
760}
761
[email protected]d80a4322009-08-14 07:07:49762void ClientSocketPoolBaseHelper::CancelAllConnectJobs() {
[email protected]5fc08e32009-07-15 17:09:57763 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end();) {
764 Group& group = i->second;
[email protected]4d3b05d2010-01-27 21:27:29765 connecting_socket_count_ -= group.jobs.size();
[email protected]5fc08e32009-07-15 17:09:57766 STLDeleteElements(&group.jobs);
767
[email protected]6b624c62010-03-14 08:37:32768 if (group.backup_task) {
769 group.backup_task->Cancel();
770 group.backup_task = NULL;
771 }
772
[email protected]5fc08e32009-07-15 17:09:57773 // Delete group if no longer needed.
774 if (group.IsEmpty()) {
[email protected]5fc08e32009-07-15 17:09:57775 group_map_.erase(i++);
776 } else {
777 ++i;
778 }
779 }
780}
781
[email protected]d80a4322009-08-14 07:07:49782bool ClientSocketPoolBaseHelper::ReachedMaxSocketsLimit() const {
[email protected]211d2172009-07-22 15:48:53783 // Each connecting socket will eventually connect and be handed out.
[email protected]43a21b82010-06-10 21:30:54784 int total = handed_out_socket_count_ + connecting_socket_count_ +
785 idle_socket_count();
[email protected]211d2172009-07-22 15:48:53786 DCHECK_LE(total, max_sockets_);
[email protected]c901f6d2010-04-27 17:48:28787 if (total < max_sockets_)
788 return false;
789 LOG(WARNING) << "ReachedMaxSocketsLimit: " << total << "/" << max_sockets_;
790 return true;
[email protected]211d2172009-07-22 15:48:53791}
792
[email protected]43a21b82010-06-10 21:30:54793void ClientSocketPoolBaseHelper::CloseOneIdleSocket() {
794 CHECK_GT(idle_socket_count(), 0);
795
796 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end(); ++i) {
797 Group& group = i->second;
798
799 if (!group.idle_sockets.empty()) {
800 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
801 delete j->socket;
802 group.idle_sockets.erase(j);
803 DecrementIdleCount();
804 if (group.IsEmpty())
805 group_map_.erase(i);
806
807 return;
808 }
809 }
810
811 LOG(DFATAL) << "No idle socket found to close!.";
812}
813
[email protected]d80a4322009-08-14 07:07:49814} // namespace internal
815
[email protected]ff579d42009-06-24 15:47:02816} // namespace net