blob: 68f04e1eaf24fc8e20eb83631ee13b1bd8ce1d82 [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),
135 max_sockets_(max_sockets),
[email protected]ff579d42009-06-24 15:47:02136 max_sockets_per_group_(max_sockets_per_group),
[email protected]9bf28db2009-08-29 01:35:16137 unused_idle_socket_timeout_(unused_idle_socket_timeout),
138 used_idle_socket_timeout_(used_idle_socket_timeout),
[email protected]100d5fb92009-12-21 21:08:35139 connect_job_factory_(connect_job_factory),
[email protected]7c28e9a2010-03-20 01:16:13140 backup_jobs_enabled_(false),
[email protected]a7e38572010-06-07 18:22:24141 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
[email protected]934152ea2010-06-29 01:02:50142 pool_generation_number_(0),
143 last_stalled_group_count_(0) {
[email protected]211d2172009-07-22 15:48:53144 DCHECK_LE(0, max_sockets_per_group);
145 DCHECK_LE(max_sockets_per_group, max_sockets);
[email protected]a554a8262010-05-20 00:13:52146
[email protected]66761b952010-06-25 21:30:38147 NetworkChangeNotifier::AddObserver(this);
[email protected]211d2172009-07-22 15:48:53148}
[email protected]ff579d42009-06-24 15:47:02149
[email protected]d80a4322009-08-14 07:07:49150ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() {
[email protected]4d3b05d2010-01-27 21:27:29151 CancelAllConnectJobs();
152
[email protected]ff579d42009-06-24 15:47:02153 // Clean up any idle sockets. Assert that we have no remaining active
154 // sockets or pending requests. They should have all been cleaned up prior
155 // to the manager being destroyed.
156 CloseIdleSockets();
[email protected]6b624c62010-03-14 08:37:32157 CHECK(group_map_.empty());
[email protected]4d3b05d2010-01-27 21:27:29158 DCHECK_EQ(0, connecting_socket_count_);
[email protected]a554a8262010-05-20 00:13:52159
[email protected]66761b952010-06-25 21:30:38160 NetworkChangeNotifier::RemoveObserver(this);
[email protected]ff579d42009-06-24 15:47:02161}
162
163// InsertRequestIntoQueue inserts the request into the queue based on
164// priority. Highest priorities are closest to the front. Older requests are
165// prioritized over requests of equal priority.
166//
167// static
[email protected]d80a4322009-08-14 07:07:49168void ClientSocketPoolBaseHelper::InsertRequestIntoQueue(
169 const Request* r, RequestQueue* pending_requests) {
[email protected]ff579d42009-06-24 15:47:02170 RequestQueue::iterator it = pending_requests->begin();
[email protected]ac790b42009-12-02 04:31:31171 while (it != pending_requests->end() && r->priority() >= (*it)->priority())
[email protected]ff579d42009-06-24 15:47:02172 ++it;
173 pending_requests->insert(it, r);
174}
175
[email protected]fd7b7c92009-08-20 19:38:30176// static
177const ClientSocketPoolBaseHelper::Request*
178ClientSocketPoolBaseHelper::RemoveRequestFromQueue(
179 RequestQueue::iterator it, RequestQueue* pending_requests) {
180 const Request* req = *it;
[email protected]fd7b7c92009-08-20 19:38:30181 pending_requests->erase(it);
182 return req;
183}
184
[email protected]d80a4322009-08-14 07:07:49185int ClientSocketPoolBaseHelper::RequestSocket(
[email protected]ff579d42009-06-24 15:47:02186 const std::string& group_name,
[email protected]d80a4322009-08-14 07:07:49187 const Request* request) {
[email protected]ec11be62010-04-28 19:28:09188 request->net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL, NULL);
[email protected]fd4fe0b2010-02-08 23:02:15189 Group& group = group_map_[group_name];
[email protected]934152ea2010-06-29 01:02:50190
[email protected]fd4fe0b2010-02-08 23:02:15191 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]934152ea2010-06-29 01:02:50212 if (AssignIdleSocketToGroup(&group, request))
213 return OK;
[email protected]65552102010-04-09 22:58:10214
[email protected]43a21b82010-06-10 21:30:54215 // Can we make another active socket now?
216 if (!group.HasAvailableSocketSlot(max_sockets_per_group_)) {
217 request->net_log().AddEvent(
218 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP, NULL);
219 return ERR_IO_PENDING;
220 }
221
222 if (ReachedMaxSocketsLimit()) {
223 if (idle_socket_count() > 0) {
224 CloseOneIdleSocket();
225 } else {
226 // We could check if we really have a stalled group here, but it requires
227 // a scan of all groups, so just flip a flag here, and do the check later.
[email protected]43a21b82010-06-10 21:30:54228 request->net_log().AddEvent(
229 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL);
230 return ERR_IO_PENDING;
231 }
232 }
233
[email protected]ff579d42009-06-24 15:47:02234 // We couldn't find a socket to reuse, so allocate and connect a new one.
[email protected]2ab05b52009-07-01 23:57:58235 scoped_ptr<ConnectJob> connect_job(
[email protected]06650c52010-06-03 00:49:17236 connect_job_factory_->NewConnectJob(group_name, *request, this));
[email protected]ff579d42009-06-24 15:47:02237
[email protected]2ab05b52009-07-01 23:57:58238 int rv = connect_job->Connect();
239 if (rv == OK) {
[email protected]06650c52010-06-03 00:49:17240 LogBoundConnectJobToRequest(connect_job->net_log().source(), request);
[email protected]2ab05b52009-07-01 23:57:58241 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */,
[email protected]9e743cd2010-03-16 07:03:53242 handle, base::TimeDelta(), &group, request->net_log());
[email protected]2ab05b52009-07-01 23:57:58243 } else if (rv == ERR_IO_PENDING) {
[email protected]6b624c62010-03-14 08:37:32244 // If we don't have any sockets in this group, set a timer for potentially
245 // creating a new one. If the SYN is lost, this backup socket may complete
246 // before the slow socket, improving end user latency.
[email protected]7c28e9a2010-03-20 01:16:13247 if (group.IsEmpty() && !group.backup_job && backup_jobs_enabled_) {
[email protected]6b624c62010-03-14 08:37:32248 group.backup_job = connect_job_factory_->NewConnectJob(group_name,
249 *request,
[email protected]06650c52010-06-03 00:49:17250 this);
[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]a2006ece2010-04-23 16:44:02258 } else {
[email protected]06650c52010-06-03 00:49:17259 LogBoundConnectJobToRequest(connect_job->net_log().source(), request);
[email protected]a2006ece2010-04-23 16:44:02260 if (group.IsEmpty())
261 group_map_.erase(group_name);
[email protected]2ab05b52009-07-01 23:57:58262 }
[email protected]ff579d42009-06-24 15:47:02263
[email protected]2ab05b52009-07-01 23:57:58264 return rv;
[email protected]ff579d42009-06-24 15:47:02265}
266
[email protected]934152ea2010-06-29 01:02:50267bool ClientSocketPoolBaseHelper::AssignIdleSocketToGroup(Group* group,
268 const Request* request) {
269 // Iterate through the list of idle sockets until we find one or exhaust
270 // the list.
271 while (!group->idle_sockets.empty()) {
272 IdleSocket idle_socket = group->idle_sockets.back();
273 group->idle_sockets.pop_back();
274 DecrementIdleCount();
275 if (idle_socket.socket->IsConnectedAndIdle()) {
276 // We found one we can reuse!
277 base::TimeDelta idle_time =
278 base::TimeTicks::Now() - idle_socket.start_time;
279 HandOutSocket(
280 idle_socket.socket, idle_socket.used, request->handle(), idle_time,
281 group, request->net_log());
282 return true;
283 }
284 delete idle_socket.socket;
285 }
286 return false;
287}
288
[email protected]06650c52010-06-03 00:49:17289// static
290void ClientSocketPoolBaseHelper::LogBoundConnectJobToRequest(
291 const NetLog::Source& connect_job_source, const Request* request) {
292 request->net_log().AddEvent(
293 NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB,
294 new NetLogSourceParameter("source_dependency", connect_job_source));
295}
296
[email protected]6b624c62010-03-14 08:37:32297void ClientSocketPoolBaseHelper::StartBackupSocketTimer(
298 const std::string& group_name) {
299 CHECK(ContainsKey(group_map_, group_name));
300 Group& group = group_map_[group_name];
301
302 // Only allow one timer pending to create a backup socket.
303 if (group.backup_task)
304 return;
305
306 group.backup_task = method_factory_.NewRunnableMethod(
307 &ClientSocketPoolBaseHelper::OnBackupSocketTimerFired, group_name);
308 MessageLoop::current()->PostDelayedTask(FROM_HERE, group.backup_task,
309 ConnectRetryIntervalMs());
310}
311
312void ClientSocketPoolBaseHelper::OnBackupSocketTimerFired(
313 const std::string& group_name) {
314 CHECK(ContainsKey(group_map_, group_name));
315
316 Group& group = group_map_[group_name];
317
318 CHECK(group.backup_task);
319 group.backup_task = NULL;
320
321 CHECK(group.backup_job);
322
[email protected]c901f6d2010-04-27 17:48:28323 // If our backup job is waiting on DNS, or if we can't create any sockets
324 // right now due to limits, just reset the timer.
[email protected]6b624c62010-03-14 08:37:32325 CHECK(group.jobs.size());
[email protected]c901f6d2010-04-27 17:48:28326 if (ReachedMaxSocketsLimit() ||
327 !group.HasAvailableSocketSlot(max_sockets_per_group_) ||
328 (*group.jobs.begin())->GetLoadState() == LOAD_STATE_RESOLVING_HOST) {
[email protected]6b624c62010-03-14 08:37:32329 StartBackupSocketTimer(group_name);
330 return;
331 }
332
[email protected]ec11be62010-04-28 19:28:09333 group.backup_job->net_log().AddEvent(NetLog::TYPE_SOCKET_BACKUP_CREATED,
334 NULL);
[email protected]6b624c62010-03-14 08:37:32335 SIMPLE_STATS_COUNTER("socket.backup_created");
336 int rv = group.backup_job->Connect();
[email protected]c83658c2010-03-24 08:19:34337 connecting_socket_count_++;
338 group.jobs.insert(group.backup_job);
339 ConnectJob* job = group.backup_job;
340 group.backup_job = NULL;
341 if (rv != ERR_IO_PENDING)
342 OnConnectJobComplete(rv, job);
[email protected]6b624c62010-03-14 08:37:32343}
344
[email protected]d80a4322009-08-14 07:07:49345void ClientSocketPoolBaseHelper::CancelRequest(
346 const std::string& group_name, const ClientSocketHandle* handle) {
[email protected]b6501d3d2010-06-03 23:53:34347 // Running callbacks can cause the last outside reference to be released.
348 // Hold onto a reference.
349 scoped_refptr<ClientSocketPoolBaseHelper> ref_holder(this);
350
[email protected]ff579d42009-06-24 15:47:02351 CHECK(ContainsKey(group_map_, group_name));
352
353 Group& group = group_map_[group_name];
354
[email protected]ff579d42009-06-24 15:47:02355 // Search pending_requests for matching handle.
356 RequestQueue::iterator it = group.pending_requests.begin();
357 for (; it != group.pending_requests.end(); ++it) {
[email protected]d80a4322009-08-14 07:07:49358 if ((*it)->handle() == handle) {
[email protected]fd7b7c92009-08-20 19:38:30359 const Request* req = RemoveRequestFromQueue(it, &group.pending_requests);
[email protected]ec11be62010-04-28 19:28:09360 req->net_log().AddEvent(NetLog::TYPE_CANCELLED, NULL);
361 req->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL);
[email protected]fd7b7c92009-08-20 19:38:30362 delete req;
[email protected]934152ea2010-06-29 01:02:50363
364 // We let the job run, unless we're at the socket limit.
365 if (group.jobs.size() && ReachedMaxSocketsLimit()) {
[email protected]4d3b05d2010-01-27 21:27:29366 RemoveConnectJob(*group.jobs.begin(), &group);
[email protected]934152ea2010-06-29 01:02:50367 CheckForStalledSocketGroups();
[email protected]974ebd62009-08-03 23:14:34368 }
[email protected]934152ea2010-06-29 01:02:50369 break;
[email protected]ff579d42009-06-24 15:47:02370 }
371 }
[email protected]ff579d42009-06-24 15:47:02372}
373
[email protected]d80a4322009-08-14 07:07:49374void ClientSocketPoolBaseHelper::CloseIdleSockets() {
[email protected]ff579d42009-06-24 15:47:02375 CleanupIdleSockets(true);
376}
377
[email protected]d80a4322009-08-14 07:07:49378int ClientSocketPoolBaseHelper::IdleSocketCountInGroup(
[email protected]ff579d42009-06-24 15:47:02379 const std::string& group_name) const {
380 GroupMap::const_iterator i = group_map_.find(group_name);
381 CHECK(i != group_map_.end());
382
383 return i->second.idle_sockets.size();
384}
385
[email protected]d80a4322009-08-14 07:07:49386LoadState ClientSocketPoolBaseHelper::GetLoadState(
[email protected]ff579d42009-06-24 15:47:02387 const std::string& group_name,
388 const ClientSocketHandle* handle) const {
389 if (!ContainsKey(group_map_, group_name)) {
390 NOTREACHED() << "ClientSocketPool does not contain group: " << group_name
391 << " for handle: " << handle;
392 return LOAD_STATE_IDLE;
393 }
394
395 // Can't use operator[] since it is non-const.
396 const Group& group = group_map_.find(group_name)->second;
397
[email protected]ff579d42009-06-24 15:47:02398 // Search pending_requests for matching handle.
399 RequestQueue::const_iterator it = group.pending_requests.begin();
[email protected]5fc08e32009-07-15 17:09:57400 for (size_t i = 0; it != group.pending_requests.end(); ++it, ++i) {
[email protected]d80a4322009-08-14 07:07:49401 if ((*it)->handle() == handle) {
[email protected]4d3b05d2010-01-27 21:27:29402 if (i < group.jobs.size()) {
[email protected]5fc08e32009-07-15 17:09:57403 LoadState max_state = LOAD_STATE_IDLE;
404 for (ConnectJobSet::const_iterator job_it = group.jobs.begin();
405 job_it != group.jobs.end(); ++job_it) {
[email protected]46451352009-09-01 14:54:21406 max_state = std::max(max_state, (*job_it)->GetLoadState());
[email protected]5fc08e32009-07-15 17:09:57407 }
408 return max_state;
409 } else {
410 // TODO(wtc): Add a state for being on the wait list.
411 // See http://www.crbug.com/5077.
412 return LOAD_STATE_IDLE;
413 }
[email protected]ff579d42009-06-24 15:47:02414 }
415 }
416
417 NOTREACHED();
418 return LOAD_STATE_IDLE;
419}
420
[email protected]d80a4322009-08-14 07:07:49421bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup(
[email protected]9bf28db2009-08-29 01:35:16422 base::TimeTicks now,
423 base::TimeDelta timeout) const {
424 bool timed_out = (now - start_time) >= timeout;
[email protected]5fc08e32009-07-15 17:09:57425 return timed_out ||
426 !(used ? socket->IsConnectedAndIdle() : socket->IsConnected());
[email protected]ff579d42009-06-24 15:47:02427}
428
[email protected]d80a4322009-08-14 07:07:49429void ClientSocketPoolBaseHelper::CleanupIdleSockets(bool force) {
[email protected]ff579d42009-06-24 15:47:02430 if (idle_socket_count_ == 0)
431 return;
432
433 // Current time value. Retrieving it once at the function start rather than
434 // inside the inner loop, since it shouldn't change by any meaningful amount.
435 base::TimeTicks now = base::TimeTicks::Now();
436
437 GroupMap::iterator i = group_map_.begin();
438 while (i != group_map_.end()) {
439 Group& group = i->second;
440
441 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
442 while (j != group.idle_sockets.end()) {
[email protected]9bf28db2009-08-29 01:35:16443 base::TimeDelta timeout =
444 j->used ? used_idle_socket_timeout_ : unused_idle_socket_timeout_;
445 if (force || j->ShouldCleanup(now, timeout)) {
[email protected]ff579d42009-06-24 15:47:02446 delete j->socket;
447 j = group.idle_sockets.erase(j);
448 DecrementIdleCount();
449 } else {
450 ++j;
451 }
452 }
453
454 // Delete group if no longer needed.
[email protected]2ab05b52009-07-01 23:57:58455 if (group.IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02456 group_map_.erase(i++);
457 } else {
458 ++i;
459 }
460 }
461}
462
[email protected]d80a4322009-08-14 07:07:49463void ClientSocketPoolBaseHelper::IncrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02464 if (++idle_socket_count_ == 1)
465 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this,
[email protected]d80a4322009-08-14 07:07:49466 &ClientSocketPoolBaseHelper::OnCleanupTimerFired);
[email protected]ff579d42009-06-24 15:47:02467}
468
[email protected]d80a4322009-08-14 07:07:49469void ClientSocketPoolBaseHelper::DecrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02470 if (--idle_socket_count_ == 0)
471 timer_.Stop();
472}
473
[email protected]934152ea2010-06-29 01:02:50474void ClientSocketPoolBaseHelper::ReleaseSocket(const std::string& group_name,
475 ClientSocket* socket,
476 int id) {
[email protected]b6501d3d2010-06-03 23:53:34477 // Running callbacks can cause the last outside reference to be released.
478 // Hold onto a reference.
479 scoped_refptr<ClientSocketPoolBaseHelper> ref_holder(this);
480
[email protected]ff579d42009-06-24 15:47:02481 GroupMap::iterator i = group_map_.find(group_name);
482 CHECK(i != group_map_.end());
483
484 Group& group = i->second;
485
[email protected]b1f031dd2010-03-02 23:19:33486 CHECK_GT(handed_out_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53487 handed_out_socket_count_--;
488
[email protected]b1f031dd2010-03-02 23:19:33489 CHECK_GT(group.active_socket_count, 0);
[email protected]2ab05b52009-07-01 23:57:58490 group.active_socket_count--;
[email protected]ff579d42009-06-24 15:47:02491
[email protected]a7e38572010-06-07 18:22:24492 const bool can_reuse = socket->IsConnectedAndIdle() &&
493 id == pool_generation_number_;
[email protected]ff579d42009-06-24 15:47:02494 if (can_reuse) {
[email protected]934152ea2010-06-29 01:02:50495 // Add it to the idle list.
[email protected]5fc08e32009-07-15 17:09:57496 AddIdleSocket(socket, true /* used socket */, &group);
[email protected]934152ea2010-06-29 01:02:50497 OnAvailableSocketSlot(group_name, MayHaveStalledGroups());
[email protected]ff579d42009-06-24 15:47:02498 } else {
499 delete socket;
500 }
[email protected]934152ea2010-06-29 01:02:50501 // Check to see if there are stalled groups that can resume now.
502 CheckForStalledSocketGroups();
503}
[email protected]ff579d42009-06-24 15:47:02504
[email protected]934152ea2010-06-29 01:02:50505void ClientSocketPoolBaseHelper::CheckForStalledSocketGroups() {
506 // If we have idle sockets, see if we can give one to the top-stalled group.
507 std::string top_group_name;
508 Group* top_group = NULL;
509 last_stalled_group_count_ = FindTopStalledGroup(&top_group, &top_group_name);
510 if (!last_stalled_group_count_)
511 return;
[email protected]4f2abec2010-02-03 18:10:16512
[email protected]934152ea2010-06-29 01:02:50513 if (ReachedMaxSocketsLimit()) {
514 if (idle_socket_count() > 0) {
515 CloseOneIdleSocket();
[email protected]d7027bb2010-05-10 18:58:54516 } else {
[email protected]934152ea2010-06-29 01:02:50517 // We can't activate more sockets since we're already at our global
518 // limit.
[email protected]4f2abec2010-02-03 18:10:16519 return;
[email protected]d7027bb2010-05-10 18:58:54520 }
[email protected]4f2abec2010-02-03 18:10:16521 }
[email protected]934152ea2010-06-29 01:02:50522
523 // Note: we don't loop on waking stalled groups. If the stalled group is at
524 // its limit, may be left with other stalled groups that could be
525 // waken. This isn't optimal, but there is no starvation, so to avoid
526 // the looping we leave it at this.
527 OnAvailableSocketSlot(top_group_name, false);
528}
529
530bool ClientSocketPoolBaseHelper::MayHaveStalledGroups() {
531 return last_stalled_group_count_ > 0 || ReachedMaxSocketsLimit();
[email protected]ff579d42009-06-24 15:47:02532}
533
[email protected]211d2172009-07-22 15:48:53534// Search for the highest priority pending request, amongst the groups that
535// are not at the |max_sockets_per_group_| limit. Note: for requests with
536// the same priority, the winner is based on group hash ordering (and not
537// insertion order).
[email protected]d80a4322009-08-14 07:07:49538int ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group,
539 std::string* group_name) {
[email protected]211d2172009-07-22 15:48:53540 Group* top_group = NULL;
541 const std::string* top_group_name = NULL;
542 int stalled_group_count = 0;
543 for (GroupMap::iterator i = group_map_.begin();
544 i != group_map_.end(); ++i) {
545 Group& group = i->second;
546 const RequestQueue& queue = group.pending_requests;
547 if (queue.empty())
548 continue;
[email protected]6427fe22010-04-16 22:27:41549 bool has_unused_slot =
550 group.HasAvailableSocketSlot(max_sockets_per_group_) &&
551 group.pending_requests.size() > group.jobs.size();
552 if (has_unused_slot) {
[email protected]211d2172009-07-22 15:48:53553 stalled_group_count++;
[email protected]6427fe22010-04-16 22:27:41554 bool has_higher_priority = !top_group ||
555 group.TopPendingPriority() < top_group->TopPendingPriority();
556 if (has_higher_priority) {
557 top_group = &group;
558 top_group_name = &i->first;
559 }
[email protected]211d2172009-07-22 15:48:53560 }
561 }
562 if (top_group) {
563 *group = top_group;
564 *group_name = *top_group_name;
565 }
566 return stalled_group_count;
567}
568
[email protected]d80a4322009-08-14 07:07:49569void ClientSocketPoolBaseHelper::OnConnectJobComplete(
570 int result, ConnectJob* job) {
[email protected]b6501d3d2010-06-03 23:53:34571 // Running callbacks can cause the last outside reference to be released.
572 // Hold onto a reference.
573 scoped_refptr<ClientSocketPoolBaseHelper> ref_holder(this);
574
[email protected]2ab05b52009-07-01 23:57:58575 DCHECK_NE(ERR_IO_PENDING, result);
576 const std::string group_name = job->group_name();
[email protected]ff579d42009-06-24 15:47:02577 GroupMap::iterator group_it = group_map_.find(group_name);
578 CHECK(group_it != group_map_.end());
579 Group& group = group_it->second;
580
[email protected]6b624c62010-03-14 08:37:32581 // We've had a connect on the socket; discard any pending backup job
582 // for this group and kill the pending task.
583 group.CleanupBackupJob();
584
[email protected]5fc08e32009-07-15 17:09:57585 scoped_ptr<ClientSocket> socket(job->ReleaseSocket());
[email protected]ff579d42009-06-24 15:47:02586
[email protected]9e743cd2010-03-16 07:03:53587 BoundNetLog job_log = job->net_log();
[email protected]4d3b05d2010-01-27 21:27:29588 RemoveConnectJob(job, &group);
[email protected]5fc08e32009-07-15 17:09:57589
[email protected]4d3b05d2010-01-27 21:27:29590 if (result == OK) {
591 DCHECK(socket.get());
[email protected]fd7b7c92009-08-20 19:38:30592 if (!group.pending_requests.empty()) {
[email protected]4d3b05d2010-01-27 21:27:29593 scoped_ptr<const Request> r(RemoveRequestFromQueue(
[email protected]fd7b7c92009-08-20 19:38:30594 group.pending_requests.begin(), &group.pending_requests));
[email protected]06650c52010-06-03 00:49:17595 LogBoundConnectJobToRequest(job_log.source(), r.get());
[email protected]4d3b05d2010-01-27 21:27:29596 HandOutSocket(
597 socket.release(), false /* unused socket */, r->handle(),
[email protected]9e743cd2010-03-16 07:03:53598 base::TimeDelta(), &group, r->net_log());
[email protected]06650c52010-06-03 00:49:17599 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL);
[email protected]4d3b05d2010-01-27 21:27:29600 r->callback()->Run(result);
[email protected]5fc08e32009-07-15 17:09:57601 } else {
[email protected]4d3b05d2010-01-27 21:27:29602 AddIdleSocket(socket.release(), false /* unused socket */, &group);
[email protected]934152ea2010-06-29 01:02:50603 OnAvailableSocketSlot(group_name, MayHaveStalledGroups());
[email protected]5fc08e32009-07-15 17:09:57604 }
[email protected]94c20472010-01-14 08:14:36605 } else {
[email protected]4d3b05d2010-01-27 21:27:29606 DCHECK(!socket.get());
607 if (!group.pending_requests.empty()) {
608 scoped_ptr<const Request> r(RemoveRequestFromQueue(
609 group.pending_requests.begin(), &group.pending_requests));
[email protected]06650c52010-06-03 00:49:17610 LogBoundConnectJobToRequest(job_log.source(), r.get());
611 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL,
612 new NetLogIntegerParameter("net_error", result));
[email protected]4d3b05d2010-01-27 21:27:29613 r->callback()->Run(result);
614 }
[email protected]934152ea2010-06-29 01:02:50615 OnAvailableSocketSlot(group_name, MayHaveStalledGroups());
[email protected]ff579d42009-06-24 15:47:02616 }
[email protected]ff579d42009-06-24 15:47:02617}
618
[email protected]66761b952010-06-25 21:30:38619void ClientSocketPoolBaseHelper::OnIPAddressChanged() {
620 Flush();
621}
622
[email protected]a7e38572010-06-07 18:22:24623void ClientSocketPoolBaseHelper::Flush() {
624 pool_generation_number_++;
[email protected]b6501d3d2010-06-03 23:53:34625 CancelAllConnectJobs();
[email protected]a554a8262010-05-20 00:13:52626 CloseIdleSockets();
627}
628
[email protected]4d3b05d2010-01-27 21:27:29629void ClientSocketPoolBaseHelper::RemoveConnectJob(const ConnectJob *job,
630 Group* group) {
[email protected]b1f031dd2010-03-02 23:19:33631 CHECK_GT(connecting_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53632 connecting_socket_count_--;
633
[email protected]5fc08e32009-07-15 17:09:57634 if (group) {
635 DCHECK(ContainsKey(group->jobs, job));
636 group->jobs.erase(job);
637 }
[email protected]ff579d42009-06-24 15:47:02638
[email protected]934152ea2010-06-29 01:02:50639 DCHECK(job);
640 delete job;
[email protected]2ab05b52009-07-01 23:57:58641}
[email protected]ff579d42009-06-24 15:47:02642
[email protected]d80a4322009-08-14 07:07:49643void ClientSocketPoolBaseHelper::OnAvailableSocketSlot(
[email protected]934152ea2010-06-29 01:02:50644 const std::string& group_name, bool was_at_socket_limit) {
645 // Go back to the message loop before processing the request wakeup
646 // so that we don't get recursive and lengthy stacks.
647 MessageLoop::current()->PostTask(FROM_HERE,
648 NewRunnableMethod(
649 this,
650 &ClientSocketPoolBaseHelper::ProcessPendingRequest,
651 group_name,
652 was_at_socket_limit));
[email protected]ff579d42009-06-24 15:47:02653}
654
[email protected]d80a4322009-08-14 07:07:49655void ClientSocketPoolBaseHelper::ProcessPendingRequest(
[email protected]934152ea2010-06-29 01:02:50656 const std::string& group_name, bool was_at_socket_limit) {
657 GroupMap::iterator it = group_map_.find(group_name);
658 if (it != group_map_.end()) {
659 Group& group = it->second;
660 if (!group.pending_requests.empty()) {
661 int rv = RequestSocketInternal(group_name,
662 *group.pending_requests.begin());
663 if (rv != ERR_IO_PENDING) {
664 scoped_ptr<const Request> request(RemoveRequestFromQueue(
665 group.pending_requests.begin(), &group.pending_requests));
[email protected]ff579d42009-06-24 15:47:02666
[email protected]934152ea2010-06-29 01:02:50667 scoped_refptr<NetLog::EventParameters> params;
668 if (rv != OK)
669 params = new NetLogIntegerParameter("net_error", rv);
670 request->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, params);
671 request->callback()->Run(rv);
672 }
[email protected]06650c52010-06-03 00:49:17673
[email protected]934152ea2010-06-29 01:02:50674 // |group| may no longer be valid after this point. Be careful not to
675 // access it again.
676 if (group.IsEmpty()) {
677 // Delete |group| if no longer needed. |group| will no longer be valid.
678 group_map_.erase(group_name);
679 }
[email protected]2ab05b52009-07-01 23:57:58680 }
681 }
[email protected]934152ea2010-06-29 01:02:50682
683 if (was_at_socket_limit)
684 CheckForStalledSocketGroups();
[email protected]2ab05b52009-07-01 23:57:58685}
686
[email protected]d80a4322009-08-14 07:07:49687void ClientSocketPoolBaseHelper::HandOutSocket(
[email protected]2ab05b52009-07-01 23:57:58688 ClientSocket* socket,
689 bool reused,
690 ClientSocketHandle* handle,
[email protected]f9d285c2009-08-17 19:54:29691 base::TimeDelta idle_time,
[email protected]fd4fe0b2010-02-08 23:02:15692 Group* group,
[email protected]9e743cd2010-03-16 07:03:53693 const BoundNetLog& net_log) {
[email protected]2ab05b52009-07-01 23:57:58694 DCHECK(socket);
695 handle->set_socket(socket);
696 handle->set_is_reused(reused);
[email protected]f9d285c2009-08-17 19:54:29697 handle->set_idle_time(idle_time);
[email protected]a7e38572010-06-07 18:22:24698 handle->set_pool_id(pool_generation_number_);
[email protected]211d2172009-07-22 15:48:53699
[email protected]d13f51b2010-04-27 23:20:45700 if (reused) {
[email protected]ec11be62010-04-28 19:28:09701 net_log.AddEvent(
[email protected]d13f51b2010-04-27 23:20:45702 NetLog::TYPE_SOCKET_POOL_REUSED_AN_EXISTING_SOCKET,
[email protected]ec11be62010-04-28 19:28:09703 new NetLogIntegerParameter(
704 "idle_ms", static_cast<int>(idle_time.InMilliseconds())));
[email protected]fd4fe0b2010-02-08 23:02:15705 }
[email protected]d13f51b2010-04-27 23:20:45706
[email protected]06650c52010-06-03 00:49:17707 net_log.AddEvent(NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET,
708 new NetLogSourceParameter(
709 "source_dependency", socket->NetLog().source()));
[email protected]fd4fe0b2010-02-08 23:02:15710
[email protected]211d2172009-07-22 15:48:53711 handed_out_socket_count_++;
[email protected]2ab05b52009-07-01 23:57:58712 group->active_socket_count++;
[email protected]ff579d42009-06-24 15:47:02713}
714
[email protected]d80a4322009-08-14 07:07:49715void ClientSocketPoolBaseHelper::AddIdleSocket(
[email protected]5fc08e32009-07-15 17:09:57716 ClientSocket* socket, bool used, Group* group) {
717 DCHECK(socket);
718 IdleSocket idle_socket;
719 idle_socket.socket = socket;
720 idle_socket.start_time = base::TimeTicks::Now();
721 idle_socket.used = used;
722
723 group->idle_sockets.push_back(idle_socket);
724 IncrementIdleCount();
725}
726
[email protected]d80a4322009-08-14 07:07:49727void ClientSocketPoolBaseHelper::CancelAllConnectJobs() {
[email protected]5fc08e32009-07-15 17:09:57728 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end();) {
729 Group& group = i->second;
[email protected]4d3b05d2010-01-27 21:27:29730 connecting_socket_count_ -= group.jobs.size();
[email protected]5fc08e32009-07-15 17:09:57731 STLDeleteElements(&group.jobs);
732
[email protected]6b624c62010-03-14 08:37:32733 if (group.backup_task) {
734 group.backup_task->Cancel();
735 group.backup_task = NULL;
736 }
737
[email protected]5fc08e32009-07-15 17:09:57738 // Delete group if no longer needed.
739 if (group.IsEmpty()) {
[email protected]5fc08e32009-07-15 17:09:57740 group_map_.erase(i++);
741 } else {
742 ++i;
743 }
744 }
745}
746
[email protected]d80a4322009-08-14 07:07:49747bool ClientSocketPoolBaseHelper::ReachedMaxSocketsLimit() const {
[email protected]211d2172009-07-22 15:48:53748 // Each connecting socket will eventually connect and be handed out.
[email protected]43a21b82010-06-10 21:30:54749 int total = handed_out_socket_count_ + connecting_socket_count_ +
750 idle_socket_count();
[email protected]211d2172009-07-22 15:48:53751 DCHECK_LE(total, max_sockets_);
[email protected]c901f6d2010-04-27 17:48:28752 if (total < max_sockets_)
753 return false;
754 LOG(WARNING) << "ReachedMaxSocketsLimit: " << total << "/" << max_sockets_;
755 return true;
[email protected]211d2172009-07-22 15:48:53756}
757
[email protected]43a21b82010-06-10 21:30:54758void ClientSocketPoolBaseHelper::CloseOneIdleSocket() {
759 CHECK_GT(idle_socket_count(), 0);
760
761 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end(); ++i) {
762 Group& group = i->second;
763
764 if (!group.idle_sockets.empty()) {
765 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
766 delete j->socket;
767 group.idle_sockets.erase(j);
768 DecrementIdleCount();
769 if (group.IsEmpty())
770 group_map_.erase(i);
771
772 return;
773 }
774 }
775
776 LOG(DFATAL) << "No idle socket found to close!.";
777}
778
[email protected]d80a4322009-08-14 07:07:49779} // namespace internal
780
[email protected]ff579d42009-06-24 15:47:02781} // namespace net