blob: 80857e55d396056512d0112734a2680ed492c1f1 [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]a554a8262010-05-20 00:13:52131 ConnectJobFactory* connect_job_factory,
132 NetworkChangeNotifier* network_change_notifier)
[email protected]ff579d42009-06-24 15:47:02133 : idle_socket_count_(0),
[email protected]211d2172009-07-22 15:48:53134 connecting_socket_count_(0),
135 handed_out_socket_count_(0),
[email protected]d7027bb2010-05-10 18:58:54136 num_releasing_sockets_(0),
[email protected]211d2172009-07-22 15:48:53137 max_sockets_(max_sockets),
[email protected]ff579d42009-06-24 15:47:02138 max_sockets_per_group_(max_sockets_per_group),
[email protected]9bf28db2009-08-29 01:35:16139 unused_idle_socket_timeout_(unused_idle_socket_timeout),
140 used_idle_socket_timeout_(used_idle_socket_timeout),
[email protected]211d2172009-07-22 15:48:53141 may_have_stalled_group_(false),
[email protected]100d5fb92009-12-21 21:08:35142 connect_job_factory_(connect_job_factory),
[email protected]a554a8262010-05-20 00:13:52143 network_change_notifier_(network_change_notifier),
[email protected]7c28e9a2010-03-20 01:16:13144 backup_jobs_enabled_(false),
[email protected]a7e38572010-06-07 18:22:24145 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
146 pool_generation_number_(0) {
[email protected]211d2172009-07-22 15:48:53147 DCHECK_LE(0, max_sockets_per_group);
148 DCHECK_LE(max_sockets_per_group, max_sockets);
[email protected]a554a8262010-05-20 00:13:52149
150 if (network_change_notifier_)
151 network_change_notifier_->AddObserver(this);
[email protected]211d2172009-07-22 15:48:53152}
[email protected]ff579d42009-06-24 15:47:02153
[email protected]d80a4322009-08-14 07:07:49154ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() {
[email protected]4d3b05d2010-01-27 21:27:29155 CancelAllConnectJobs();
156
[email protected]ff579d42009-06-24 15:47:02157 // Clean up any idle sockets. Assert that we have no remaining active
158 // sockets or pending requests. They should have all been cleaned up prior
159 // to the manager being destroyed.
160 CloseIdleSockets();
[email protected]6b624c62010-03-14 08:37:32161 CHECK(group_map_.empty());
[email protected]4d3b05d2010-01-27 21:27:29162 DCHECK_EQ(0, connecting_socket_count_);
[email protected]a554a8262010-05-20 00:13:52163
164 if (network_change_notifier_)
165 network_change_notifier_->RemoveObserver(this);
[email protected]ff579d42009-06-24 15:47:02166}
167
168// InsertRequestIntoQueue inserts the request into the queue based on
169// priority. Highest priorities are closest to the front. Older requests are
170// prioritized over requests of equal priority.
171//
172// static
[email protected]d80a4322009-08-14 07:07:49173void ClientSocketPoolBaseHelper::InsertRequestIntoQueue(
174 const Request* r, RequestQueue* pending_requests) {
[email protected]ff579d42009-06-24 15:47:02175 RequestQueue::iterator it = pending_requests->begin();
[email protected]ac790b42009-12-02 04:31:31176 while (it != pending_requests->end() && r->priority() >= (*it)->priority())
[email protected]ff579d42009-06-24 15:47:02177 ++it;
178 pending_requests->insert(it, r);
179}
180
[email protected]fd7b7c92009-08-20 19:38:30181// static
182const ClientSocketPoolBaseHelper::Request*
183ClientSocketPoolBaseHelper::RemoveRequestFromQueue(
184 RequestQueue::iterator it, RequestQueue* pending_requests) {
185 const Request* req = *it;
[email protected]fd7b7c92009-08-20 19:38:30186 pending_requests->erase(it);
187 return req;
188}
189
[email protected]d80a4322009-08-14 07:07:49190int ClientSocketPoolBaseHelper::RequestSocket(
[email protected]ff579d42009-06-24 15:47:02191 const std::string& group_name,
[email protected]d80a4322009-08-14 07:07:49192 const Request* request) {
[email protected]ec11be62010-04-28 19:28:09193 request->net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL, NULL);
[email protected]fd4fe0b2010-02-08 23:02:15194 Group& group = group_map_[group_name];
195 int rv = RequestSocketInternal(group_name, request);
[email protected]e7e99322010-05-04 23:30:17196 if (rv != ERR_IO_PENDING) {
[email protected]ec11be62010-04-28 19:28:09197 request->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL);
[email protected]e7e99322010-05-04 23:30:17198 delete request;
199 } else {
[email protected]fd4fe0b2010-02-08 23:02:15200 InsertRequestIntoQueue(request, &group.pending_requests);
[email protected]e7e99322010-05-04 23:30:17201 }
[email protected]fd4fe0b2010-02-08 23:02:15202 return rv;
203}
204
205int ClientSocketPoolBaseHelper::RequestSocketInternal(
206 const std::string& group_name,
207 const Request* request) {
[email protected]d80a4322009-08-14 07:07:49208 DCHECK_GE(request->priority(), 0);
209 CompletionCallback* const callback = request->callback();
210 CHECK(callback);
211 ClientSocketHandle* const handle = request->handle();
212 CHECK(handle);
[email protected]ff579d42009-06-24 15:47:02213 Group& group = group_map_[group_name];
214
[email protected]4751c742010-05-19 02:44:36215 // Can we make another active socket now?
216 if (ReachedMaxSocketsLimit() ||
217 !group.HasAvailableSocketSlot(max_sockets_per_group_)) {
218 if (ReachedMaxSocketsLimit()) {
219 // We could check if we really have a stalled group here, but it requires
220 // a scan of all groups, so just flip a flag here, and do the check later.
221 may_have_stalled_group_ = true;
222
223 request->net_log().AddEvent(NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS,
224 NULL);
225 } else {
226 request->net_log().AddEvent(
227 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP, NULL);
228 }
229 return ERR_IO_PENDING;
230 }
231
[email protected]65552102010-04-09 22:58:10232 // Try to reuse a socket.
233 while (!group.idle_sockets.empty()) {
234 IdleSocket idle_socket = group.idle_sockets.back();
235 group.idle_sockets.pop_back();
236 DecrementIdleCount();
237 if (idle_socket.socket->IsConnectedAndIdle()) {
238 // We found one we can reuse!
239 base::TimeDelta idle_time =
240 base::TimeTicks::Now() - idle_socket.start_time;
241 HandOutSocket(
242 idle_socket.socket, idle_socket.used, handle, idle_time, &group,
243 request->net_log());
244 return OK;
245 }
246 delete idle_socket.socket;
247 }
248
[email protected]5edbf8d2010-01-13 18:44:11249 // See if we already have enough connect jobs or sockets that will be released
250 // soon.
[email protected]4d3b05d2010-01-27 21:27:29251 if (group.HasReleasingSockets()) {
[email protected]5edbf8d2010-01-13 18:44:11252 return ERR_IO_PENDING;
253 }
254
[email protected]ff579d42009-06-24 15:47:02255 // We couldn't find a socket to reuse, so allocate and connect a new one.
[email protected]2ab05b52009-07-01 23:57:58256 scoped_ptr<ConnectJob> connect_job(
[email protected]06650c52010-06-03 00:49:17257 connect_job_factory_->NewConnectJob(group_name, *request, this));
[email protected]ff579d42009-06-24 15:47:02258
[email protected]2ab05b52009-07-01 23:57:58259 int rv = connect_job->Connect();
260 if (rv == OK) {
[email protected]06650c52010-06-03 00:49:17261 LogBoundConnectJobToRequest(connect_job->net_log().source(), request);
[email protected]2ab05b52009-07-01 23:57:58262 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */,
[email protected]9e743cd2010-03-16 07:03:53263 handle, base::TimeDelta(), &group, request->net_log());
[email protected]2ab05b52009-07-01 23:57:58264 } else if (rv == ERR_IO_PENDING) {
[email protected]6b624c62010-03-14 08:37:32265 // If we don't have any sockets in this group, set a timer for potentially
266 // creating a new one. If the SYN is lost, this backup socket may complete
267 // before the slow socket, improving end user latency.
[email protected]7c28e9a2010-03-20 01:16:13268 if (group.IsEmpty() && !group.backup_job && backup_jobs_enabled_) {
[email protected]6b624c62010-03-14 08:37:32269 group.backup_job = connect_job_factory_->NewConnectJob(group_name,
270 *request,
[email protected]06650c52010-06-03 00:49:17271 this);
[email protected]6b624c62010-03-14 08:37:32272 StartBackupSocketTimer(group_name);
273 }
274
[email protected]211d2172009-07-22 15:48:53275 connecting_socket_count_++;
276
[email protected]5fc08e32009-07-15 17:09:57277 ConnectJob* job = connect_job.release();
[email protected]5fc08e32009-07-15 17:09:57278 group.jobs.insert(job);
[email protected]a2006ece2010-04-23 16:44:02279 } else {
[email protected]06650c52010-06-03 00:49:17280 LogBoundConnectJobToRequest(connect_job->net_log().source(), request);
[email protected]a2006ece2010-04-23 16:44:02281 if (group.IsEmpty())
282 group_map_.erase(group_name);
[email protected]2ab05b52009-07-01 23:57:58283 }
[email protected]ff579d42009-06-24 15:47:02284
[email protected]2ab05b52009-07-01 23:57:58285 return rv;
[email protected]ff579d42009-06-24 15:47:02286}
287
[email protected]06650c52010-06-03 00:49:17288// static
289void ClientSocketPoolBaseHelper::LogBoundConnectJobToRequest(
290 const NetLog::Source& connect_job_source, const Request* request) {
291 request->net_log().AddEvent(
292 NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB,
293 new NetLogSourceParameter("source_dependency", connect_job_source));
294}
295
[email protected]6b624c62010-03-14 08:37:32296void ClientSocketPoolBaseHelper::StartBackupSocketTimer(
297 const std::string& group_name) {
298 CHECK(ContainsKey(group_map_, group_name));
299 Group& group = group_map_[group_name];
300
301 // Only allow one timer pending to create a backup socket.
302 if (group.backup_task)
303 return;
304
305 group.backup_task = method_factory_.NewRunnableMethod(
306 &ClientSocketPoolBaseHelper::OnBackupSocketTimerFired, group_name);
307 MessageLoop::current()->PostDelayedTask(FROM_HERE, group.backup_task,
308 ConnectRetryIntervalMs());
309}
310
311void ClientSocketPoolBaseHelper::OnBackupSocketTimerFired(
312 const std::string& group_name) {
313 CHECK(ContainsKey(group_map_, group_name));
314
315 Group& group = group_map_[group_name];
316
317 CHECK(group.backup_task);
318 group.backup_task = NULL;
319
320 CHECK(group.backup_job);
321
[email protected]c901f6d2010-04-27 17:48:28322 // If our backup job is waiting on DNS, or if we can't create any sockets
323 // right now due to limits, just reset the timer.
[email protected]6b624c62010-03-14 08:37:32324 CHECK(group.jobs.size());
[email protected]c901f6d2010-04-27 17:48:28325 if (ReachedMaxSocketsLimit() ||
326 !group.HasAvailableSocketSlot(max_sockets_per_group_) ||
327 (*group.jobs.begin())->GetLoadState() == LOAD_STATE_RESOLVING_HOST) {
[email protected]6b624c62010-03-14 08:37:32328 StartBackupSocketTimer(group_name);
329 return;
330 }
331
[email protected]ec11be62010-04-28 19:28:09332 group.backup_job->net_log().AddEvent(NetLog::TYPE_SOCKET_BACKUP_CREATED,
333 NULL);
[email protected]6b624c62010-03-14 08:37:32334 SIMPLE_STATS_COUNTER("socket.backup_created");
335 int rv = group.backup_job->Connect();
[email protected]c83658c2010-03-24 08:19:34336 connecting_socket_count_++;
337 group.jobs.insert(group.backup_job);
338 ConnectJob* job = group.backup_job;
339 group.backup_job = NULL;
340 if (rv != ERR_IO_PENDING)
341 OnConnectJobComplete(rv, job);
[email protected]6b624c62010-03-14 08:37:32342}
343
[email protected]d80a4322009-08-14 07:07:49344void ClientSocketPoolBaseHelper::CancelRequest(
345 const std::string& group_name, const ClientSocketHandle* handle) {
[email protected]b6501d3d2010-06-03 23:53:34346 // Running callbacks can cause the last outside reference to be released.
347 // Hold onto a reference.
348 scoped_refptr<ClientSocketPoolBaseHelper> ref_holder(this);
349
[email protected]ff579d42009-06-24 15:47:02350 CHECK(ContainsKey(group_map_, group_name));
351
352 Group& group = group_map_[group_name];
353
[email protected]ff579d42009-06-24 15:47:02354 // Search pending_requests for matching handle.
355 RequestQueue::iterator it = group.pending_requests.begin();
356 for (; it != group.pending_requests.end(); ++it) {
[email protected]d80a4322009-08-14 07:07:49357 if ((*it)->handle() == handle) {
[email protected]fd7b7c92009-08-20 19:38:30358 const Request* req = RemoveRequestFromQueue(it, &group.pending_requests);
[email protected]ec11be62010-04-28 19:28:09359 req->net_log().AddEvent(NetLog::TYPE_CANCELLED, NULL);
360 req->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL);
[email protected]fd7b7c92009-08-20 19:38:30361 delete req;
[email protected]a796bcec2010-03-22 17:17:26362 // Let one connect job connect and become idle for potential future use.
[email protected]4d3b05d2010-01-27 21:27:29363 if (group.jobs.size() > group.pending_requests.size() + 1) {
[email protected]974ebd62009-08-03 23:14:34364 // TODO(willchan): Cancel the job in the earliest LoadState.
[email protected]4d3b05d2010-01-27 21:27:29365 RemoveConnectJob(*group.jobs.begin(), &group);
[email protected]974ebd62009-08-03 23:14:34366 OnAvailableSocketSlot(group_name, &group);
367 }
[email protected]ff579d42009-06-24 15:47:02368 return;
369 }
370 }
[email protected]ff579d42009-06-24 15:47:02371}
372
[email protected]d80a4322009-08-14 07:07:49373void ClientSocketPoolBaseHelper::ReleaseSocket(const std::string& group_name,
[email protected]a7e38572010-06-07 18:22:24374 ClientSocket* socket,
375 int id) {
[email protected]5edbf8d2010-01-13 18:44:11376 Group& group = group_map_[group_name];
377 group.num_releasing_sockets++;
[email protected]d7027bb2010-05-10 18:58:54378 num_releasing_sockets_++;
[email protected]5edbf8d2010-01-13 18:44:11379 DCHECK_LE(group.num_releasing_sockets, group.active_socket_count);
[email protected]ff579d42009-06-24 15:47:02380 // Run this asynchronously to allow the caller to finish before we let
381 // another to begin doing work. This also avoids nasty recursion issues.
382 // NOTE: We cannot refer to the handle argument after this method returns.
383 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]a7e38572010-06-07 18:22:24384 this, &ClientSocketPoolBaseHelper::DoReleaseSocket, group_name, socket, id));
[email protected]ff579d42009-06-24 15:47:02385}
386
[email protected]d80a4322009-08-14 07:07:49387void ClientSocketPoolBaseHelper::CloseIdleSockets() {
[email protected]ff579d42009-06-24 15:47:02388 CleanupIdleSockets(true);
389}
390
[email protected]d80a4322009-08-14 07:07:49391int ClientSocketPoolBaseHelper::IdleSocketCountInGroup(
[email protected]ff579d42009-06-24 15:47:02392 const std::string& group_name) const {
393 GroupMap::const_iterator i = group_map_.find(group_name);
394 CHECK(i != group_map_.end());
395
396 return i->second.idle_sockets.size();
397}
398
[email protected]d80a4322009-08-14 07:07:49399LoadState ClientSocketPoolBaseHelper::GetLoadState(
[email protected]ff579d42009-06-24 15:47:02400 const std::string& group_name,
401 const ClientSocketHandle* handle) const {
402 if (!ContainsKey(group_map_, group_name)) {
403 NOTREACHED() << "ClientSocketPool does not contain group: " << group_name
404 << " for handle: " << handle;
405 return LOAD_STATE_IDLE;
406 }
407
408 // Can't use operator[] since it is non-const.
409 const Group& group = group_map_.find(group_name)->second;
410
[email protected]ff579d42009-06-24 15:47:02411 // Search pending_requests for matching handle.
412 RequestQueue::const_iterator it = group.pending_requests.begin();
[email protected]5fc08e32009-07-15 17:09:57413 for (size_t i = 0; it != group.pending_requests.end(); ++it, ++i) {
[email protected]d80a4322009-08-14 07:07:49414 if ((*it)->handle() == handle) {
[email protected]4d3b05d2010-01-27 21:27:29415 if (i < group.jobs.size()) {
[email protected]5fc08e32009-07-15 17:09:57416 LoadState max_state = LOAD_STATE_IDLE;
417 for (ConnectJobSet::const_iterator job_it = group.jobs.begin();
418 job_it != group.jobs.end(); ++job_it) {
[email protected]46451352009-09-01 14:54:21419 max_state = std::max(max_state, (*job_it)->GetLoadState());
[email protected]5fc08e32009-07-15 17:09:57420 }
421 return max_state;
422 } else {
423 // TODO(wtc): Add a state for being on the wait list.
424 // See http://www.crbug.com/5077.
425 return LOAD_STATE_IDLE;
426 }
[email protected]ff579d42009-06-24 15:47:02427 }
428 }
429
430 NOTREACHED();
431 return LOAD_STATE_IDLE;
432}
433
[email protected]d80a4322009-08-14 07:07:49434bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup(
[email protected]9bf28db2009-08-29 01:35:16435 base::TimeTicks now,
436 base::TimeDelta timeout) const {
437 bool timed_out = (now - start_time) >= timeout;
[email protected]5fc08e32009-07-15 17:09:57438 return timed_out ||
439 !(used ? socket->IsConnectedAndIdle() : socket->IsConnected());
[email protected]ff579d42009-06-24 15:47:02440}
441
[email protected]d80a4322009-08-14 07:07:49442void ClientSocketPoolBaseHelper::CleanupIdleSockets(bool force) {
[email protected]ff579d42009-06-24 15:47:02443 if (idle_socket_count_ == 0)
444 return;
445
446 // Current time value. Retrieving it once at the function start rather than
447 // inside the inner loop, since it shouldn't change by any meaningful amount.
448 base::TimeTicks now = base::TimeTicks::Now();
449
450 GroupMap::iterator i = group_map_.begin();
451 while (i != group_map_.end()) {
452 Group& group = i->second;
453
454 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
455 while (j != group.idle_sockets.end()) {
[email protected]9bf28db2009-08-29 01:35:16456 base::TimeDelta timeout =
457 j->used ? used_idle_socket_timeout_ : unused_idle_socket_timeout_;
458 if (force || j->ShouldCleanup(now, timeout)) {
[email protected]ff579d42009-06-24 15:47:02459 delete j->socket;
460 j = group.idle_sockets.erase(j);
461 DecrementIdleCount();
462 } else {
463 ++j;
464 }
465 }
466
467 // Delete group if no longer needed.
[email protected]2ab05b52009-07-01 23:57:58468 if (group.IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02469 group_map_.erase(i++);
470 } else {
471 ++i;
472 }
473 }
474}
475
[email protected]d80a4322009-08-14 07:07:49476void ClientSocketPoolBaseHelper::IncrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02477 if (++idle_socket_count_ == 1)
478 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this,
[email protected]d80a4322009-08-14 07:07:49479 &ClientSocketPoolBaseHelper::OnCleanupTimerFired);
[email protected]ff579d42009-06-24 15:47:02480}
481
[email protected]d80a4322009-08-14 07:07:49482void ClientSocketPoolBaseHelper::DecrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02483 if (--idle_socket_count_ == 0)
484 timer_.Stop();
485}
486
[email protected]d80a4322009-08-14 07:07:49487void ClientSocketPoolBaseHelper::DoReleaseSocket(const std::string& group_name,
[email protected]a7e38572010-06-07 18:22:24488 ClientSocket* socket,
489 int id) {
[email protected]b6501d3d2010-06-03 23:53:34490 // Running callbacks can cause the last outside reference to be released.
491 // Hold onto a reference.
492 scoped_refptr<ClientSocketPoolBaseHelper> ref_holder(this);
493
[email protected]ff579d42009-06-24 15:47:02494 GroupMap::iterator i = group_map_.find(group_name);
495 CHECK(i != group_map_.end());
496
497 Group& group = i->second;
498
[email protected]5edbf8d2010-01-13 18:44:11499 group.num_releasing_sockets--;
500 DCHECK_GE(group.num_releasing_sockets, 0);
501
[email protected]b1f031dd2010-03-02 23:19:33502 CHECK_GT(handed_out_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53503 handed_out_socket_count_--;
504
[email protected]b1f031dd2010-03-02 23:19:33505 CHECK_GT(group.active_socket_count, 0);
[email protected]2ab05b52009-07-01 23:57:58506 group.active_socket_count--;
[email protected]ff579d42009-06-24 15:47:02507
[email protected]d7027bb2010-05-10 18:58:54508 CHECK_GT(num_releasing_sockets_, 0);
509 num_releasing_sockets_--;
510
[email protected]a7e38572010-06-07 18:22:24511 const bool can_reuse = socket->IsConnectedAndIdle() &&
512 id == pool_generation_number_;
[email protected]ff579d42009-06-24 15:47:02513 if (can_reuse) {
[email protected]5fc08e32009-07-15 17:09:57514 AddIdleSocket(socket, true /* used socket */, &group);
[email protected]ff579d42009-06-24 15:47:02515 } else {
516 delete socket;
517 }
518
[email protected]4f2abec2010-02-03 18:10:16519 // If there are no more releasing sockets, then we might have to process
520 // multiple available socket slots, since we stalled their processing until
[email protected]d7027bb2010-05-10 18:58:54521 // all sockets have been released. Note that ProcessPendingRequest() will
522 // invoke user callbacks, so |num_releasing_sockets_| may change.
523 //
524 // This code has been known to infinite loop. Set a counter and CHECK to make
525 // sure it doesn't get ridiculously high.
[email protected]4f2abec2010-02-03 18:10:16526
[email protected]d7027bb2010-05-10 18:58:54527 int iterations = 0;
528 while (num_releasing_sockets_ == 0) {
529 CHECK_LT(iterations, 1000) << "Probably stuck in an infinite loop.";
530 std::string top_group_name;
531 Group* top_group = NULL;
532 int stalled_group_count = FindTopStalledGroup(&top_group, &top_group_name);
533 if (stalled_group_count >= 1) {
534 if (ReachedMaxSocketsLimit()) {
[email protected]4751c742010-05-19 02:44:36535 // We can't activate more sockets since we're already at our global
536 // limit.
537 may_have_stalled_group_ = true;
538 return;
[email protected]d7027bb2010-05-10 18:58:54539 }
540
541 ProcessPendingRequest(top_group_name, top_group);
542 } else {
543 may_have_stalled_group_ = false;
[email protected]4f2abec2010-02-03 18:10:16544 return;
[email protected]d7027bb2010-05-10 18:58:54545 }
[email protected]616925a2010-03-02 19:02:38546
[email protected]d7027bb2010-05-10 18:58:54547 iterations++;
[email protected]4f2abec2010-02-03 18:10:16548 }
[email protected]ff579d42009-06-24 15:47:02549}
550
[email protected]211d2172009-07-22 15:48:53551// Search for the highest priority pending request, amongst the groups that
552// are not at the |max_sockets_per_group_| limit. Note: for requests with
553// the same priority, the winner is based on group hash ordering (and not
554// insertion order).
[email protected]d80a4322009-08-14 07:07:49555int ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group,
556 std::string* group_name) {
[email protected]211d2172009-07-22 15:48:53557 Group* top_group = NULL;
558 const std::string* top_group_name = NULL;
559 int stalled_group_count = 0;
560 for (GroupMap::iterator i = group_map_.begin();
561 i != group_map_.end(); ++i) {
562 Group& group = i->second;
563 const RequestQueue& queue = group.pending_requests;
564 if (queue.empty())
565 continue;
[email protected]6427fe22010-04-16 22:27:41566 bool has_unused_slot =
567 group.HasAvailableSocketSlot(max_sockets_per_group_) &&
568 group.pending_requests.size() > group.jobs.size();
569 if (has_unused_slot) {
[email protected]211d2172009-07-22 15:48:53570 stalled_group_count++;
[email protected]6427fe22010-04-16 22:27:41571 bool has_higher_priority = !top_group ||
572 group.TopPendingPriority() < top_group->TopPendingPriority();
573 if (has_higher_priority) {
574 top_group = &group;
575 top_group_name = &i->first;
576 }
[email protected]211d2172009-07-22 15:48:53577 }
578 }
579 if (top_group) {
580 *group = top_group;
581 *group_name = *top_group_name;
582 }
583 return stalled_group_count;
584}
585
[email protected]d80a4322009-08-14 07:07:49586void ClientSocketPoolBaseHelper::OnConnectJobComplete(
587 int result, ConnectJob* job) {
[email protected]b6501d3d2010-06-03 23:53:34588 // Running callbacks can cause the last outside reference to be released.
589 // Hold onto a reference.
590 scoped_refptr<ClientSocketPoolBaseHelper> ref_holder(this);
591
[email protected]2ab05b52009-07-01 23:57:58592 DCHECK_NE(ERR_IO_PENDING, result);
593 const std::string group_name = job->group_name();
[email protected]ff579d42009-06-24 15:47:02594 GroupMap::iterator group_it = group_map_.find(group_name);
595 CHECK(group_it != group_map_.end());
596 Group& group = group_it->second;
597
[email protected]6b624c62010-03-14 08:37:32598 // We've had a connect on the socket; discard any pending backup job
599 // for this group and kill the pending task.
600 group.CleanupBackupJob();
601
[email protected]5fc08e32009-07-15 17:09:57602 scoped_ptr<ClientSocket> socket(job->ReleaseSocket());
[email protected]ff579d42009-06-24 15:47:02603
[email protected]9e743cd2010-03-16 07:03:53604 BoundNetLog job_log = job->net_log();
[email protected]4d3b05d2010-01-27 21:27:29605 RemoveConnectJob(job, &group);
[email protected]5fc08e32009-07-15 17:09:57606
[email protected]4d3b05d2010-01-27 21:27:29607 if (result == OK) {
608 DCHECK(socket.get());
[email protected]fd7b7c92009-08-20 19:38:30609 if (!group.pending_requests.empty()) {
[email protected]4d3b05d2010-01-27 21:27:29610 scoped_ptr<const Request> r(RemoveRequestFromQueue(
[email protected]fd7b7c92009-08-20 19:38:30611 group.pending_requests.begin(), &group.pending_requests));
[email protected]06650c52010-06-03 00:49:17612 LogBoundConnectJobToRequest(job_log.source(), r.get());
[email protected]4d3b05d2010-01-27 21:27:29613 HandOutSocket(
614 socket.release(), false /* unused socket */, r->handle(),
[email protected]9e743cd2010-03-16 07:03:53615 base::TimeDelta(), &group, r->net_log());
[email protected]06650c52010-06-03 00:49:17616 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL);
[email protected]4d3b05d2010-01-27 21:27:29617 r->callback()->Run(result);
[email protected]5fc08e32009-07-15 17:09:57618 } else {
[email protected]4d3b05d2010-01-27 21:27:29619 AddIdleSocket(socket.release(), false /* unused socket */, &group);
620 OnAvailableSocketSlot(group_name, &group);
[email protected]5fc08e32009-07-15 17:09:57621 }
[email protected]94c20472010-01-14 08:14:36622 } else {
[email protected]4d3b05d2010-01-27 21:27:29623 DCHECK(!socket.get());
624 if (!group.pending_requests.empty()) {
625 scoped_ptr<const Request> r(RemoveRequestFromQueue(
626 group.pending_requests.begin(), &group.pending_requests));
[email protected]06650c52010-06-03 00:49:17627 LogBoundConnectJobToRequest(job_log.source(), r.get());
628 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL,
629 new NetLogIntegerParameter("net_error", result));
[email protected]4d3b05d2010-01-27 21:27:29630 r->callback()->Run(result);
631 }
632 MaybeOnAvailableSocketSlot(group_name);
[email protected]ff579d42009-06-24 15:47:02633 }
[email protected]ff579d42009-06-24 15:47:02634}
635
[email protected]a7e38572010-06-07 18:22:24636void ClientSocketPoolBaseHelper::Flush() {
637 pool_generation_number_++;
[email protected]b6501d3d2010-06-03 23:53:34638 CancelAllConnectJobs();
[email protected]a554a8262010-05-20 00:13:52639 CloseIdleSockets();
640}
641
[email protected]4d3b05d2010-01-27 21:27:29642void ClientSocketPoolBaseHelper::RemoveConnectJob(const ConnectJob *job,
643 Group* group) {
[email protected]b1f031dd2010-03-02 23:19:33644 CHECK_GT(connecting_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53645 connecting_socket_count_--;
646
[email protected]4d3b05d2010-01-27 21:27:29647 DCHECK(job);
648 delete job;
[email protected]5fc08e32009-07-15 17:09:57649
650 if (group) {
651 DCHECK(ContainsKey(group->jobs, job));
652 group->jobs.erase(job);
653 }
[email protected]ff579d42009-06-24 15:47:02654}
655
[email protected]d80a4322009-08-14 07:07:49656void ClientSocketPoolBaseHelper::MaybeOnAvailableSocketSlot(
[email protected]2ab05b52009-07-01 23:57:58657 const std::string& group_name) {
658 GroupMap::iterator it = group_map_.find(group_name);
659 if (it != group_map_.end()) {
660 Group& group = it->second;
661 if (group.HasAvailableSocketSlot(max_sockets_per_group_))
662 OnAvailableSocketSlot(group_name, &group);
663 }
664}
[email protected]ff579d42009-06-24 15:47:02665
[email protected]d80a4322009-08-14 07:07:49666void ClientSocketPoolBaseHelper::OnAvailableSocketSlot(
667 const std::string& group_name, Group* group) {
[email protected]211d2172009-07-22 15:48:53668 if (may_have_stalled_group_) {
669 std::string top_group_name;
[email protected]bed37d442009-08-20 19:58:20670 Group* top_group = NULL;
[email protected]211d2172009-07-22 15:48:53671 int stalled_group_count = FindTopStalledGroup(&top_group, &top_group_name);
[email protected]d7027bb2010-05-10 18:58:54672 if (stalled_group_count == 0 ||
673 (stalled_group_count == 1 && top_group->num_releasing_sockets == 0)) {
[email protected]211d2172009-07-22 15:48:53674 may_have_stalled_group_ = false;
[email protected]d7027bb2010-05-10 18:58:54675 }
[email protected]4751c742010-05-19 02:44:36676 if (stalled_group_count >= 1)
[email protected]211d2172009-07-22 15:48:53677 ProcessPendingRequest(top_group_name, top_group);
678 } else if (!group->pending_requests.empty()) {
[email protected]ff579d42009-06-24 15:47:02679 ProcessPendingRequest(group_name, group);
680 // |group| may no longer be valid after this point. Be careful not to
681 // access it again.
[email protected]2ab05b52009-07-01 23:57:58682 } else if (group->IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02683 // Delete |group| if no longer needed. |group| will no longer be valid.
[email protected]ff579d42009-06-24 15:47:02684 group_map_.erase(group_name);
[email protected]ff579d42009-06-24 15:47:02685 }
686}
687
[email protected]d80a4322009-08-14 07:07:49688void ClientSocketPoolBaseHelper::ProcessPendingRequest(
689 const std::string& group_name, Group* group) {
[email protected]e7e99322010-05-04 23:30:17690 int rv = RequestSocketInternal(group_name, *group->pending_requests.begin());
[email protected]ff579d42009-06-24 15:47:02691
[email protected]2ab05b52009-07-01 23:57:58692 if (rv != ERR_IO_PENDING) {
[email protected]e7e99322010-05-04 23:30:17693 scoped_ptr<const Request> r(RemoveRequestFromQueue(
694 group->pending_requests.begin(), &group->pending_requests));
[email protected]06650c52010-06-03 00:49:17695
696 scoped_refptr<NetLog::EventParameters> params;
697 if (rv != OK)
698 params = new NetLogIntegerParameter("net_error", rv);
699 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, params);
[email protected]d80a4322009-08-14 07:07:49700 r->callback()->Run(rv);
[email protected]2ab05b52009-07-01 23:57:58701 if (rv != OK) {
702 // |group| may be invalid after the callback, we need to search
703 // |group_map_| again.
704 MaybeOnAvailableSocketSlot(group_name);
705 }
[email protected]2ab05b52009-07-01 23:57:58706 }
707}
708
[email protected]d80a4322009-08-14 07:07:49709void ClientSocketPoolBaseHelper::HandOutSocket(
[email protected]2ab05b52009-07-01 23:57:58710 ClientSocket* socket,
711 bool reused,
712 ClientSocketHandle* handle,
[email protected]f9d285c2009-08-17 19:54:29713 base::TimeDelta idle_time,
[email protected]fd4fe0b2010-02-08 23:02:15714 Group* group,
[email protected]9e743cd2010-03-16 07:03:53715 const BoundNetLog& net_log) {
[email protected]2ab05b52009-07-01 23:57:58716 DCHECK(socket);
717 handle->set_socket(socket);
718 handle->set_is_reused(reused);
[email protected]f9d285c2009-08-17 19:54:29719 handle->set_idle_time(idle_time);
[email protected]a7e38572010-06-07 18:22:24720 handle->set_pool_id(pool_generation_number_);
[email protected]211d2172009-07-22 15:48:53721
[email protected]d13f51b2010-04-27 23:20:45722 if (reused) {
[email protected]ec11be62010-04-28 19:28:09723 net_log.AddEvent(
[email protected]d13f51b2010-04-27 23:20:45724 NetLog::TYPE_SOCKET_POOL_REUSED_AN_EXISTING_SOCKET,
[email protected]ec11be62010-04-28 19:28:09725 new NetLogIntegerParameter(
726 "idle_ms", static_cast<int>(idle_time.InMilliseconds())));
[email protected]fd4fe0b2010-02-08 23:02:15727 }
[email protected]d13f51b2010-04-27 23:20:45728
[email protected]06650c52010-06-03 00:49:17729 net_log.AddEvent(NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET,
730 new NetLogSourceParameter(
731 "source_dependency", socket->NetLog().source()));
[email protected]fd4fe0b2010-02-08 23:02:15732
[email protected]211d2172009-07-22 15:48:53733 handed_out_socket_count_++;
[email protected]2ab05b52009-07-01 23:57:58734 group->active_socket_count++;
[email protected]ff579d42009-06-24 15:47:02735}
736
[email protected]d80a4322009-08-14 07:07:49737void ClientSocketPoolBaseHelper::AddIdleSocket(
[email protected]5fc08e32009-07-15 17:09:57738 ClientSocket* socket, bool used, Group* group) {
739 DCHECK(socket);
740 IdleSocket idle_socket;
741 idle_socket.socket = socket;
742 idle_socket.start_time = base::TimeTicks::Now();
743 idle_socket.used = used;
744
745 group->idle_sockets.push_back(idle_socket);
746 IncrementIdleCount();
747}
748
[email protected]d80a4322009-08-14 07:07:49749void ClientSocketPoolBaseHelper::CancelAllConnectJobs() {
[email protected]5fc08e32009-07-15 17:09:57750 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end();) {
751 Group& group = i->second;
[email protected]4d3b05d2010-01-27 21:27:29752 connecting_socket_count_ -= group.jobs.size();
[email protected]5fc08e32009-07-15 17:09:57753 STLDeleteElements(&group.jobs);
754
[email protected]6b624c62010-03-14 08:37:32755 if (group.backup_task) {
756 group.backup_task->Cancel();
757 group.backup_task = NULL;
758 }
759
[email protected]5fc08e32009-07-15 17:09:57760 // Delete group if no longer needed.
761 if (group.IsEmpty()) {
[email protected]5fc08e32009-07-15 17:09:57762 group_map_.erase(i++);
763 } else {
764 ++i;
765 }
766 }
767}
768
[email protected]d80a4322009-08-14 07:07:49769bool ClientSocketPoolBaseHelper::ReachedMaxSocketsLimit() const {
[email protected]211d2172009-07-22 15:48:53770 // Each connecting socket will eventually connect and be handed out.
[email protected]4751c742010-05-19 02:44:36771 int total = handed_out_socket_count_ + connecting_socket_count_;
[email protected]211d2172009-07-22 15:48:53772 DCHECK_LE(total, max_sockets_);
[email protected]c901f6d2010-04-27 17:48:28773 if (total < max_sockets_)
774 return false;
775 LOG(WARNING) << "ReachedMaxSocketsLimit: " << total << "/" << max_sockets_;
776 return true;
[email protected]211d2172009-07-22 15:48:53777}
778
[email protected]d80a4322009-08-14 07:07:49779} // namespace internal
780
[email protected]ff579d42009-06-24 15:47:02781} // namespace net