blob: e4a611b35ff2b1d4a6043a4fa20ffe03c0808748 [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]211d2172009-07-22 15:48:53135 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]05ea9ff2010-07-15 19:08:21142 pool_generation_number_(0) {
[email protected]211d2172009-07-22 15:48:53143 DCHECK_LE(0, max_sockets_per_group);
144 DCHECK_LE(max_sockets_per_group, max_sockets);
[email protected]a554a8262010-05-20 00:13:52145
[email protected]66761b952010-06-25 21:30:38146 NetworkChangeNotifier::AddObserver(this);
[email protected]211d2172009-07-22 15:48:53147}
[email protected]ff579d42009-06-24 15:47:02148
[email protected]d80a4322009-08-14 07:07:49149ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() {
[email protected]4d3b05d2010-01-27 21:27:29150 CancelAllConnectJobs();
151
[email protected]ff579d42009-06-24 15:47:02152 // Clean up any idle sockets. Assert that we have no remaining active
153 // sockets or pending requests. They should have all been cleaned up prior
154 // to the manager being destroyed.
155 CloseIdleSockets();
[email protected]6b624c62010-03-14 08:37:32156 CHECK(group_map_.empty());
[email protected]05ea9ff2010-07-15 19:08:21157 DCHECK(pending_callback_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]eb5a99382010-07-11 03:18:26190
[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]05ea9ff2010-07-15 19:08:21194 CHECK(!request->handle()->is_initialized());
[email protected]e7e99322010-05-04 23:30:17195 delete request;
196 } else {
[email protected]fd4fe0b2010-02-08 23:02:15197 InsertRequestIntoQueue(request, &group.pending_requests);
[email protected]e7e99322010-05-04 23:30:17198 }
[email protected]fd4fe0b2010-02-08 23:02:15199 return rv;
200}
201
202int ClientSocketPoolBaseHelper::RequestSocketInternal(
203 const std::string& group_name,
204 const Request* request) {
[email protected]d80a4322009-08-14 07:07:49205 DCHECK_GE(request->priority(), 0);
206 CompletionCallback* const callback = request->callback();
207 CHECK(callback);
208 ClientSocketHandle* const handle = request->handle();
209 CHECK(handle);
[email protected]ff579d42009-06-24 15:47:02210 Group& group = group_map_[group_name];
211
[email protected]65552102010-04-09 22:58:10212 // Try to reuse a socket.
[email protected]eb5a99382010-07-11 03:18:26213 if (AssignIdleSocketToGroup(&group, request))
214 return OK;
[email protected]65552102010-04-09 22:58:10215
[email protected]43a21b82010-06-10 21:30:54216 // Can we make another active socket now?
217 if (!group.HasAvailableSocketSlot(max_sockets_per_group_)) {
218 request->net_log().AddEvent(
219 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP, NULL);
220 return ERR_IO_PENDING;
221 }
222
223 if (ReachedMaxSocketsLimit()) {
224 if (idle_socket_count() > 0) {
225 CloseOneIdleSocket();
226 } else {
227 // We could check if we really have a stalled group here, but it requires
228 // a scan of all groups, so just flip a flag here, and do the check later.
[email protected]43a21b82010-06-10 21:30:54229 request->net_log().AddEvent(
230 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL);
231 return ERR_IO_PENDING;
232 }
233 }
234
[email protected]ff579d42009-06-24 15:47:02235 // We couldn't find a socket to reuse, so allocate and connect a new one.
[email protected]2ab05b52009-07-01 23:57:58236 scoped_ptr<ConnectJob> connect_job(
[email protected]06650c52010-06-03 00:49:17237 connect_job_factory_->NewConnectJob(group_name, *request, this));
[email protected]ff579d42009-06-24 15:47:02238
[email protected]2ab05b52009-07-01 23:57:58239 int rv = connect_job->Connect();
240 if (rv == OK) {
[email protected]06650c52010-06-03 00:49:17241 LogBoundConnectJobToRequest(connect_job->net_log().source(), request);
[email protected]2ab05b52009-07-01 23:57:58242 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */,
[email protected]9e743cd2010-03-16 07:03:53243 handle, base::TimeDelta(), &group, request->net_log());
[email protected]2ab05b52009-07-01 23:57:58244 } else if (rv == ERR_IO_PENDING) {
[email protected]6b624c62010-03-14 08:37:32245 // If we don't have any sockets in this group, set a timer for potentially
246 // creating a new one. If the SYN is lost, this backup socket may complete
247 // before the slow socket, improving end user latency.
[email protected]7c28e9a2010-03-20 01:16:13248 if (group.IsEmpty() && !group.backup_job && backup_jobs_enabled_) {
[email protected]6b624c62010-03-14 08:37:32249 group.backup_job = connect_job_factory_->NewConnectJob(group_name,
250 *request,
[email protected]06650c52010-06-03 00:49:17251 this);
[email protected]6b624c62010-03-14 08:37:32252 StartBackupSocketTimer(group_name);
253 }
254
[email protected]211d2172009-07-22 15:48:53255 connecting_socket_count_++;
256
[email protected]5fc08e32009-07-15 17:09:57257 ConnectJob* job = connect_job.release();
[email protected]5fc08e32009-07-15 17:09:57258 group.jobs.insert(job);
[email protected]a2006ece2010-04-23 16:44:02259 } else {
[email protected]06650c52010-06-03 00:49:17260 LogBoundConnectJobToRequest(connect_job->net_log().source(), request);
[email protected]e60e47a2010-07-14 03:37:18261 connect_job->GetAdditionalErrorState(handle);
[email protected]e772db3f2010-07-12 18:11:13262 ClientSocket* error_socket = connect_job->ReleaseSocket();
263 if (error_socket) {
264 HandOutSocket(error_socket, false /* not reused */, handle,
265 base::TimeDelta(), &group, request->net_log());
[email protected]05ea9ff2010-07-15 19:08:21266 } else if (group.IsEmpty()) {
[email protected]a2006ece2010-04-23 16:44:02267 group_map_.erase(group_name);
[email protected]05ea9ff2010-07-15 19:08:21268 }
[email protected]2ab05b52009-07-01 23:57:58269 }
[email protected]ff579d42009-06-24 15:47:02270
[email protected]2ab05b52009-07-01 23:57:58271 return rv;
[email protected]ff579d42009-06-24 15:47:02272}
273
[email protected]05ea9ff2010-07-15 19:08:21274bool ClientSocketPoolBaseHelper::AssignIdleSocketToGroup(
275 Group* group, const Request* request) {
[email protected]eb5a99382010-07-11 03:18:26276 // Iterate through the list of idle sockets until we find one or exhaust
277 // the list.
278 while (!group->idle_sockets.empty()) {
279 IdleSocket idle_socket = group->idle_sockets.back();
280 group->idle_sockets.pop_back();
281 DecrementIdleCount();
282 if (idle_socket.socket->IsConnectedAndIdle()) {
283 // We found one we can reuse!
284 base::TimeDelta idle_time =
285 base::TimeTicks::Now() - idle_socket.start_time;
286 HandOutSocket(
287 idle_socket.socket, idle_socket.used, request->handle(), idle_time,
288 group, request->net_log());
289 return true;
290 }
291 delete idle_socket.socket;
292 }
293 return false;
294}
295
[email protected]06650c52010-06-03 00:49:17296// static
297void ClientSocketPoolBaseHelper::LogBoundConnectJobToRequest(
298 const NetLog::Source& connect_job_source, const Request* request) {
299 request->net_log().AddEvent(
300 NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB,
301 new NetLogSourceParameter("source_dependency", connect_job_source));
302}
303
[email protected]6b624c62010-03-14 08:37:32304void ClientSocketPoolBaseHelper::StartBackupSocketTimer(
305 const std::string& group_name) {
306 CHECK(ContainsKey(group_map_, group_name));
307 Group& group = group_map_[group_name];
308
309 // Only allow one timer pending to create a backup socket.
310 if (group.backup_task)
311 return;
312
313 group.backup_task = method_factory_.NewRunnableMethod(
314 &ClientSocketPoolBaseHelper::OnBackupSocketTimerFired, group_name);
315 MessageLoop::current()->PostDelayedTask(FROM_HERE, group.backup_task,
316 ConnectRetryIntervalMs());
317}
318
319void ClientSocketPoolBaseHelper::OnBackupSocketTimerFired(
320 const std::string& group_name) {
321 CHECK(ContainsKey(group_map_, group_name));
322
323 Group& group = group_map_[group_name];
324
325 CHECK(group.backup_task);
326 group.backup_task = NULL;
327
328 CHECK(group.backup_job);
329
[email protected]25eea382010-07-10 23:55:26330 // If there are no more jobs pending, there is no work to do.
331 // If we've done our cleanups correctly, this should not happen.
332 if (group.jobs.empty()) {
333 NOTREACHED();
334 return;
335 }
336
[email protected]c901f6d2010-04-27 17:48:28337 // If our backup job is waiting on DNS, or if we can't create any sockets
338 // right now due to limits, just reset the timer.
[email protected]c901f6d2010-04-27 17:48:28339 if (ReachedMaxSocketsLimit() ||
340 !group.HasAvailableSocketSlot(max_sockets_per_group_) ||
341 (*group.jobs.begin())->GetLoadState() == LOAD_STATE_RESOLVING_HOST) {
[email protected]6b624c62010-03-14 08:37:32342 StartBackupSocketTimer(group_name);
343 return;
344 }
345
[email protected]ec11be62010-04-28 19:28:09346 group.backup_job->net_log().AddEvent(NetLog::TYPE_SOCKET_BACKUP_CREATED,
347 NULL);
[email protected]6b624c62010-03-14 08:37:32348 SIMPLE_STATS_COUNTER("socket.backup_created");
349 int rv = group.backup_job->Connect();
[email protected]c83658c2010-03-24 08:19:34350 connecting_socket_count_++;
351 group.jobs.insert(group.backup_job);
352 ConnectJob* job = group.backup_job;
353 group.backup_job = NULL;
354 if (rv != ERR_IO_PENDING)
355 OnConnectJobComplete(rv, job);
[email protected]6b624c62010-03-14 08:37:32356}
357
[email protected]d80a4322009-08-14 07:07:49358void ClientSocketPoolBaseHelper::CancelRequest(
[email protected]05ea9ff2010-07-15 19:08:21359 const std::string& group_name, ClientSocketHandle* handle) {
360 PendingCallbackMap::iterator callback_it = pending_callback_map_.find(handle);
361 if (callback_it != pending_callback_map_.end()) {
362 int result = callback_it->second.result;
363 pending_callback_map_.erase(callback_it);
364 ClientSocket* socket = handle->release_socket();
365 if (socket) {
366 if (result != OK)
367 socket->Disconnect();
368 ReleaseSocket(handle->group_name(), socket, handle->id());
369 }
370 return;
371 }
[email protected]b6501d3d2010-06-03 23:53:34372
[email protected]ff579d42009-06-24 15:47:02373 CHECK(ContainsKey(group_map_, group_name));
374
375 Group& group = group_map_[group_name];
376
[email protected]ff579d42009-06-24 15:47:02377 // Search pending_requests for matching handle.
378 RequestQueue::iterator it = group.pending_requests.begin();
379 for (; it != group.pending_requests.end(); ++it) {
[email protected]d80a4322009-08-14 07:07:49380 if ((*it)->handle() == handle) {
[email protected]fd7b7c92009-08-20 19:38:30381 const Request* req = RemoveRequestFromQueue(it, &group.pending_requests);
[email protected]ec11be62010-04-28 19:28:09382 req->net_log().AddEvent(NetLog::TYPE_CANCELLED, NULL);
383 req->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL);
[email protected]fd7b7c92009-08-20 19:38:30384 delete req;
[email protected]eb5a99382010-07-11 03:18:26385
386 // We let the job run, unless we're at the socket limit.
387 if (group.jobs.size() && ReachedMaxSocketsLimit()) {
[email protected]4d3b05d2010-01-27 21:27:29388 RemoveConnectJob(*group.jobs.begin(), &group);
[email protected]eb5a99382010-07-11 03:18:26389 CheckForStalledSocketGroups();
[email protected]974ebd62009-08-03 23:14:34390 }
[email protected]eb5a99382010-07-11 03:18:26391 break;
[email protected]ff579d42009-06-24 15:47:02392 }
393 }
[email protected]ff579d42009-06-24 15:47:02394}
395
[email protected]d80a4322009-08-14 07:07:49396void ClientSocketPoolBaseHelper::CloseIdleSockets() {
[email protected]ff579d42009-06-24 15:47:02397 CleanupIdleSockets(true);
398}
399
[email protected]d80a4322009-08-14 07:07:49400int ClientSocketPoolBaseHelper::IdleSocketCountInGroup(
[email protected]ff579d42009-06-24 15:47:02401 const std::string& group_name) const {
402 GroupMap::const_iterator i = group_map_.find(group_name);
403 CHECK(i != group_map_.end());
404
405 return i->second.idle_sockets.size();
406}
407
[email protected]d80a4322009-08-14 07:07:49408LoadState ClientSocketPoolBaseHelper::GetLoadState(
[email protected]ff579d42009-06-24 15:47:02409 const std::string& group_name,
410 const ClientSocketHandle* handle) const {
[email protected]05ea9ff2010-07-15 19:08:21411 if (ContainsKey(pending_callback_map_, handle))
412 return LOAD_STATE_CONNECTING;
413
[email protected]ff579d42009-06-24 15:47:02414 if (!ContainsKey(group_map_, group_name)) {
415 NOTREACHED() << "ClientSocketPool does not contain group: " << group_name
416 << " for handle: " << handle;
417 return LOAD_STATE_IDLE;
418 }
419
420 // Can't use operator[] since it is non-const.
421 const Group& group = group_map_.find(group_name)->second;
422
[email protected]ff579d42009-06-24 15:47:02423 // Search pending_requests for matching handle.
424 RequestQueue::const_iterator it = group.pending_requests.begin();
[email protected]5fc08e32009-07-15 17:09:57425 for (size_t i = 0; it != group.pending_requests.end(); ++it, ++i) {
[email protected]d80a4322009-08-14 07:07:49426 if ((*it)->handle() == handle) {
[email protected]4d3b05d2010-01-27 21:27:29427 if (i < group.jobs.size()) {
[email protected]5fc08e32009-07-15 17:09:57428 LoadState max_state = LOAD_STATE_IDLE;
429 for (ConnectJobSet::const_iterator job_it = group.jobs.begin();
430 job_it != group.jobs.end(); ++job_it) {
[email protected]46451352009-09-01 14:54:21431 max_state = std::max(max_state, (*job_it)->GetLoadState());
[email protected]5fc08e32009-07-15 17:09:57432 }
433 return max_state;
434 } else {
435 // TODO(wtc): Add a state for being on the wait list.
436 // See http://www.crbug.com/5077.
437 return LOAD_STATE_IDLE;
438 }
[email protected]ff579d42009-06-24 15:47:02439 }
440 }
441
442 NOTREACHED();
443 return LOAD_STATE_IDLE;
444}
445
[email protected]d80a4322009-08-14 07:07:49446bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup(
[email protected]9bf28db2009-08-29 01:35:16447 base::TimeTicks now,
448 base::TimeDelta timeout) const {
449 bool timed_out = (now - start_time) >= timeout;
[email protected]5fc08e32009-07-15 17:09:57450 return timed_out ||
451 !(used ? socket->IsConnectedAndIdle() : socket->IsConnected());
[email protected]ff579d42009-06-24 15:47:02452}
453
[email protected]d80a4322009-08-14 07:07:49454void ClientSocketPoolBaseHelper::CleanupIdleSockets(bool force) {
[email protected]ff579d42009-06-24 15:47:02455 if (idle_socket_count_ == 0)
456 return;
457
458 // Current time value. Retrieving it once at the function start rather than
459 // inside the inner loop, since it shouldn't change by any meaningful amount.
460 base::TimeTicks now = base::TimeTicks::Now();
461
462 GroupMap::iterator i = group_map_.begin();
463 while (i != group_map_.end()) {
464 Group& group = i->second;
465
466 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
467 while (j != group.idle_sockets.end()) {
[email protected]9bf28db2009-08-29 01:35:16468 base::TimeDelta timeout =
469 j->used ? used_idle_socket_timeout_ : unused_idle_socket_timeout_;
470 if (force || j->ShouldCleanup(now, timeout)) {
[email protected]ff579d42009-06-24 15:47:02471 delete j->socket;
472 j = group.idle_sockets.erase(j);
473 DecrementIdleCount();
474 } else {
475 ++j;
476 }
477 }
478
479 // Delete group if no longer needed.
[email protected]2ab05b52009-07-01 23:57:58480 if (group.IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02481 group_map_.erase(i++);
482 } else {
483 ++i;
484 }
485 }
486}
487
[email protected]d80a4322009-08-14 07:07:49488void ClientSocketPoolBaseHelper::IncrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02489 if (++idle_socket_count_ == 1)
490 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this,
[email protected]d80a4322009-08-14 07:07:49491 &ClientSocketPoolBaseHelper::OnCleanupTimerFired);
[email protected]ff579d42009-06-24 15:47:02492}
493
[email protected]d80a4322009-08-14 07:07:49494void ClientSocketPoolBaseHelper::DecrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02495 if (--idle_socket_count_ == 0)
496 timer_.Stop();
497}
498
[email protected]eb5a99382010-07-11 03:18:26499void ClientSocketPoolBaseHelper::ReleaseSocket(const std::string& group_name,
500 ClientSocket* socket,
501 int id) {
[email protected]ff579d42009-06-24 15:47:02502 GroupMap::iterator i = group_map_.find(group_name);
503 CHECK(i != group_map_.end());
504
505 Group& group = i->second;
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]a7e38572010-06-07 18:22:24513 const bool can_reuse = socket->IsConnectedAndIdle() &&
514 id == pool_generation_number_;
[email protected]ff579d42009-06-24 15:47:02515 if (can_reuse) {
[email protected]eb5a99382010-07-11 03:18:26516 // Add it to the idle list.
[email protected]5fc08e32009-07-15 17:09:57517 AddIdleSocket(socket, true /* used socket */, &group);
[email protected]05ea9ff2010-07-15 19:08:21518 OnAvailableSocketSlot(group_name, &group);
[email protected]ff579d42009-06-24 15:47:02519 } else {
520 delete socket;
521 }
[email protected]05ea9ff2010-07-15 19:08:21522
[email protected]eb5a99382010-07-11 03:18:26523 CheckForStalledSocketGroups();
524}
[email protected]ff579d42009-06-24 15:47:02525
[email protected]eb5a99382010-07-11 03:18:26526void ClientSocketPoolBaseHelper::CheckForStalledSocketGroups() {
527 // If we have idle sockets, see if we can give one to the top-stalled group.
528 std::string top_group_name;
529 Group* top_group = NULL;
[email protected]05ea9ff2010-07-15 19:08:21530 if (!FindTopStalledGroup(&top_group, &top_group_name))
[email protected]eb5a99382010-07-11 03:18:26531 return;
[email protected]4f2abec2010-02-03 18:10:16532
[email protected]eb5a99382010-07-11 03:18:26533 if (ReachedMaxSocketsLimit()) {
534 if (idle_socket_count() > 0) {
535 CloseOneIdleSocket();
[email protected]d7027bb2010-05-10 18:58:54536 } else {
[email protected]eb5a99382010-07-11 03:18:26537 // We can't activate more sockets since we're already at our global
538 // limit.
[email protected]4f2abec2010-02-03 18:10:16539 return;
[email protected]d7027bb2010-05-10 18:58:54540 }
[email protected]4f2abec2010-02-03 18:10:16541 }
[email protected]eb5a99382010-07-11 03:18:26542
543 // Note: we don't loop on waking stalled groups. If the stalled group is at
544 // its limit, may be left with other stalled groups that could be
[email protected]05ea9ff2010-07-15 19:08:21545 // woken. This isn't optimal, but there is no starvation, so to avoid
[email protected]eb5a99382010-07-11 03:18:26546 // the looping we leave it at this.
[email protected]05ea9ff2010-07-15 19:08:21547 OnAvailableSocketSlot(top_group_name, top_group);
[email protected]ff579d42009-06-24 15:47:02548}
549
[email protected]211d2172009-07-22 15:48:53550// Search for the highest priority pending request, amongst the groups that
551// are not at the |max_sockets_per_group_| limit. Note: for requests with
552// the same priority, the winner is based on group hash ordering (and not
553// insertion order).
[email protected]05ea9ff2010-07-15 19:08:21554bool ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group,
555 std::string* group_name) {
[email protected]211d2172009-07-22 15:48:53556 Group* top_group = NULL;
557 const std::string* top_group_name = NULL;
[email protected]05ea9ff2010-07-15 19:08:21558 bool has_stalled_group = false;
[email protected]211d2172009-07-22 15:48:53559 for (GroupMap::iterator i = group_map_.begin();
560 i != group_map_.end(); ++i) {
561 Group& group = i->second;
562 const RequestQueue& queue = group.pending_requests;
563 if (queue.empty())
564 continue;
[email protected]05ea9ff2010-07-15 19:08:21565 if (group.IsStalled(max_sockets_per_group_)) {
566 has_stalled_group = true;
[email protected]6427fe22010-04-16 22:27:41567 bool has_higher_priority = !top_group ||
568 group.TopPendingPriority() < top_group->TopPendingPriority();
569 if (has_higher_priority) {
570 top_group = &group;
571 top_group_name = &i->first;
572 }
[email protected]211d2172009-07-22 15:48:53573 }
574 }
[email protected]05ea9ff2010-07-15 19:08:21575
[email protected]211d2172009-07-22 15:48:53576 if (top_group) {
577 *group = top_group;
578 *group_name = *top_group_name;
579 }
[email protected]05ea9ff2010-07-15 19:08:21580 return has_stalled_group;
[email protected]211d2172009-07-22 15:48:53581}
582
[email protected]d80a4322009-08-14 07:07:49583void ClientSocketPoolBaseHelper::OnConnectJobComplete(
584 int result, ConnectJob* job) {
[email protected]2ab05b52009-07-01 23:57:58585 DCHECK_NE(ERR_IO_PENDING, result);
586 const std::string group_name = job->group_name();
[email protected]ff579d42009-06-24 15:47:02587 GroupMap::iterator group_it = group_map_.find(group_name);
588 CHECK(group_it != group_map_.end());
589 Group& group = group_it->second;
590
[email protected]5fc08e32009-07-15 17:09:57591 scoped_ptr<ClientSocket> socket(job->ReleaseSocket());
[email protected]ff579d42009-06-24 15:47:02592
[email protected]9e743cd2010-03-16 07:03:53593 BoundNetLog job_log = job->net_log();
[email protected]5fc08e32009-07-15 17:09:57594
[email protected]4d3b05d2010-01-27 21:27:29595 if (result == OK) {
596 DCHECK(socket.get());
[email protected]e60e47a2010-07-14 03:37:18597 RemoveConnectJob(job, &group);
[email protected]fd7b7c92009-08-20 19:38:30598 if (!group.pending_requests.empty()) {
[email protected]4d3b05d2010-01-27 21:27:29599 scoped_ptr<const Request> r(RemoveRequestFromQueue(
[email protected]fd7b7c92009-08-20 19:38:30600 group.pending_requests.begin(), &group.pending_requests));
[email protected]06650c52010-06-03 00:49:17601 LogBoundConnectJobToRequest(job_log.source(), r.get());
[email protected]4d3b05d2010-01-27 21:27:29602 HandOutSocket(
603 socket.release(), false /* unused socket */, r->handle(),
[email protected]9e743cd2010-03-16 07:03:53604 base::TimeDelta(), &group, r->net_log());
[email protected]06650c52010-06-03 00:49:17605 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL);
[email protected]05ea9ff2010-07-15 19:08:21606 InvokeUserCallbackLater(r->handle(), r->callback(), result);
[email protected]5fc08e32009-07-15 17:09:57607 } else {
[email protected]4d3b05d2010-01-27 21:27:29608 AddIdleSocket(socket.release(), false /* unused socket */, &group);
[email protected]05ea9ff2010-07-15 19:08:21609 OnAvailableSocketSlot(group_name, &group);
610 CheckForStalledSocketGroups();
[email protected]5fc08e32009-07-15 17:09:57611 }
[email protected]94c20472010-01-14 08:14:36612 } else {
[email protected]e772db3f2010-07-12 18:11:13613 // If we got a socket, it must contain error information so pass that
614 // up so that the caller can retrieve it.
615 bool handed_out_socket = false;
[email protected]4d3b05d2010-01-27 21:27:29616 if (!group.pending_requests.empty()) {
617 scoped_ptr<const Request> r(RemoveRequestFromQueue(
618 group.pending_requests.begin(), &group.pending_requests));
[email protected]06650c52010-06-03 00:49:17619 LogBoundConnectJobToRequest(job_log.source(), r.get());
[email protected]e60e47a2010-07-14 03:37:18620 job->GetAdditionalErrorState(r->handle());
621 RemoveConnectJob(job, &group);
[email protected]e772db3f2010-07-12 18:11:13622 if (socket.get()) {
623 handed_out_socket = true;
624 HandOutSocket(socket.release(), false /* unused socket */, r->handle(),
625 base::TimeDelta(), &group, r->net_log());
626 }
[email protected]06650c52010-06-03 00:49:17627 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL,
628 new NetLogIntegerParameter("net_error", result));
[email protected]05ea9ff2010-07-15 19:08:21629 InvokeUserCallbackLater(r->handle(), r->callback(), result);
[email protected]e60e47a2010-07-14 03:37:18630 } else {
631 RemoveConnectJob(job, &group);
[email protected]4d3b05d2010-01-27 21:27:29632 }
[email protected]05ea9ff2010-07-15 19:08:21633 if (!handed_out_socket) {
634 OnAvailableSocketSlot(group_name, &group);
635 CheckForStalledSocketGroups();
636 }
[email protected]ff579d42009-06-24 15:47:02637 }
[email protected]ff579d42009-06-24 15:47:02638}
639
[email protected]66761b952010-06-25 21:30:38640void ClientSocketPoolBaseHelper::OnIPAddressChanged() {
641 Flush();
642}
643
[email protected]a7e38572010-06-07 18:22:24644void ClientSocketPoolBaseHelper::Flush() {
645 pool_generation_number_++;
[email protected]b6501d3d2010-06-03 23:53:34646 CancelAllConnectJobs();
[email protected]a554a8262010-05-20 00:13:52647 CloseIdleSockets();
648}
649
[email protected]25eea382010-07-10 23:55:26650void ClientSocketPoolBaseHelper::RemoveConnectJob(const ConnectJob* job,
[email protected]4d3b05d2010-01-27 21:27:29651 Group* group) {
[email protected]b1f031dd2010-03-02 23:19:33652 CHECK_GT(connecting_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53653 connecting_socket_count_--;
654
[email protected]25eea382010-07-10 23:55:26655 DCHECK(group);
656 DCHECK(ContainsKey(group->jobs, job));
657 group->jobs.erase(job);
658
659 // If we've got no more jobs for this group, then we no longer need a
660 // backup job either.
661 if (group->jobs.empty())
662 group->CleanupBackupJob();
663
[email protected]8ae03f42010-07-07 19:08:10664 DCHECK(job);
665 delete job;
[email protected]2ab05b52009-07-01 23:57:58666}
[email protected]ff579d42009-06-24 15:47:02667
[email protected]8ae03f42010-07-07 19:08:10668void ClientSocketPoolBaseHelper::OnAvailableSocketSlot(
[email protected]05ea9ff2010-07-15 19:08:21669 const std::string& group_name, Group* group) {
670 if (!group->pending_requests.empty())
671 ProcessPendingRequest(group_name, group);
672
673 if (group->IsEmpty())
674 group_map_.erase(group_name);
[email protected]8ae03f42010-07-07 19:08:10675}
[email protected]06650c52010-06-03 00:49:17676
[email protected]8ae03f42010-07-07 19:08:10677void ClientSocketPoolBaseHelper::ProcessPendingRequest(
[email protected]05ea9ff2010-07-15 19:08:21678 const std::string& group_name, Group* group) {
679 int rv = RequestSocketInternal(group_name,
680 *group->pending_requests.begin());
681 if (rv != ERR_IO_PENDING) {
682 scoped_ptr<const Request> request(RemoveRequestFromQueue(
683 group->pending_requests.begin(), &group->pending_requests));
[email protected]8ae03f42010-07-07 19:08:10684
[email protected]05ea9ff2010-07-15 19:08:21685 scoped_refptr<NetLog::EventParameters> params;
686 if (rv != OK)
687 params = new NetLogIntegerParameter("net_error", rv);
688 request->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, params);
689 InvokeUserCallbackLater(
690 request->handle(), request->callback(), rv);
[email protected]2ab05b52009-07-01 23:57:58691 }
[email protected]2ab05b52009-07-01 23:57:58692}
693
[email protected]d80a4322009-08-14 07:07:49694void ClientSocketPoolBaseHelper::HandOutSocket(
[email protected]2ab05b52009-07-01 23:57:58695 ClientSocket* socket,
696 bool reused,
697 ClientSocketHandle* handle,
[email protected]f9d285c2009-08-17 19:54:29698 base::TimeDelta idle_time,
[email protected]fd4fe0b2010-02-08 23:02:15699 Group* group,
[email protected]9e743cd2010-03-16 07:03:53700 const BoundNetLog& net_log) {
[email protected]2ab05b52009-07-01 23:57:58701 DCHECK(socket);
702 handle->set_socket(socket);
703 handle->set_is_reused(reused);
[email protected]f9d285c2009-08-17 19:54:29704 handle->set_idle_time(idle_time);
[email protected]a7e38572010-06-07 18:22:24705 handle->set_pool_id(pool_generation_number_);
[email protected]211d2172009-07-22 15:48:53706
[email protected]d13f51b2010-04-27 23:20:45707 if (reused) {
[email protected]ec11be62010-04-28 19:28:09708 net_log.AddEvent(
[email protected]d13f51b2010-04-27 23:20:45709 NetLog::TYPE_SOCKET_POOL_REUSED_AN_EXISTING_SOCKET,
[email protected]ec11be62010-04-28 19:28:09710 new NetLogIntegerParameter(
711 "idle_ms", static_cast<int>(idle_time.InMilliseconds())));
[email protected]fd4fe0b2010-02-08 23:02:15712 }
[email protected]d13f51b2010-04-27 23:20:45713
[email protected]06650c52010-06-03 00:49:17714 net_log.AddEvent(NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET,
715 new NetLogSourceParameter(
716 "source_dependency", socket->NetLog().source()));
[email protected]fd4fe0b2010-02-08 23:02:15717
[email protected]211d2172009-07-22 15:48:53718 handed_out_socket_count_++;
[email protected]2ab05b52009-07-01 23:57:58719 group->active_socket_count++;
[email protected]ff579d42009-06-24 15:47:02720}
721
[email protected]d80a4322009-08-14 07:07:49722void ClientSocketPoolBaseHelper::AddIdleSocket(
[email protected]5fc08e32009-07-15 17:09:57723 ClientSocket* socket, bool used, Group* group) {
724 DCHECK(socket);
725 IdleSocket idle_socket;
726 idle_socket.socket = socket;
727 idle_socket.start_time = base::TimeTicks::Now();
728 idle_socket.used = used;
729
730 group->idle_sockets.push_back(idle_socket);
731 IncrementIdleCount();
732}
733
[email protected]d80a4322009-08-14 07:07:49734void ClientSocketPoolBaseHelper::CancelAllConnectJobs() {
[email protected]5fc08e32009-07-15 17:09:57735 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end();) {
736 Group& group = i->second;
[email protected]4d3b05d2010-01-27 21:27:29737 connecting_socket_count_ -= group.jobs.size();
[email protected]5fc08e32009-07-15 17:09:57738 STLDeleteElements(&group.jobs);
739
[email protected]6b624c62010-03-14 08:37:32740 if (group.backup_task) {
741 group.backup_task->Cancel();
742 group.backup_task = NULL;
743 }
744
[email protected]5fc08e32009-07-15 17:09:57745 // Delete group if no longer needed.
746 if (group.IsEmpty()) {
[email protected]5fc08e32009-07-15 17:09:57747 group_map_.erase(i++);
748 } else {
749 ++i;
750 }
751 }
752}
753
[email protected]d80a4322009-08-14 07:07:49754bool ClientSocketPoolBaseHelper::ReachedMaxSocketsLimit() const {
[email protected]211d2172009-07-22 15:48:53755 // Each connecting socket will eventually connect and be handed out.
[email protected]43a21b82010-06-10 21:30:54756 int total = handed_out_socket_count_ + connecting_socket_count_ +
757 idle_socket_count();
[email protected]211d2172009-07-22 15:48:53758 DCHECK_LE(total, max_sockets_);
[email protected]c901f6d2010-04-27 17:48:28759 if (total < max_sockets_)
760 return false;
761 LOG(WARNING) << "ReachedMaxSocketsLimit: " << total << "/" << max_sockets_;
762 return true;
[email protected]211d2172009-07-22 15:48:53763}
764
[email protected]43a21b82010-06-10 21:30:54765void ClientSocketPoolBaseHelper::CloseOneIdleSocket() {
766 CHECK_GT(idle_socket_count(), 0);
767
768 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end(); ++i) {
769 Group& group = i->second;
770
771 if (!group.idle_sockets.empty()) {
772 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
773 delete j->socket;
774 group.idle_sockets.erase(j);
775 DecrementIdleCount();
776 if (group.IsEmpty())
777 group_map_.erase(i);
778
779 return;
780 }
781 }
782
783 LOG(DFATAL) << "No idle socket found to close!.";
784}
785
[email protected]05ea9ff2010-07-15 19:08:21786void ClientSocketPoolBaseHelper::InvokeUserCallbackLater(
787 ClientSocketHandle* handle, CompletionCallback* callback, int rv) {
788 CHECK(!ContainsKey(pending_callback_map_, handle));
789 pending_callback_map_[handle] = CallbackResultPair(callback, rv);
790 MessageLoop::current()->PostTask(
791 FROM_HERE,
792 NewRunnableMethod(
793 this,
794 &ClientSocketPoolBaseHelper::InvokeUserCallback,
795 handle));
796}
797
798void ClientSocketPoolBaseHelper::InvokeUserCallback(
799 ClientSocketHandle* handle) {
800 PendingCallbackMap::iterator it = pending_callback_map_.find(handle);
801
802 // Exit if the request has already been cancelled.
803 if (it == pending_callback_map_.end())
804 return;
805
806 CHECK(!handle->is_initialized());
807 CompletionCallback* callback = it->second.callback;
808 int result = it->second.result;
809 pending_callback_map_.erase(it);
810 callback->Run(result);
811}
812
[email protected]d80a4322009-08-14 07:07:49813} // namespace internal
814
[email protected]ff579d42009-06-24 15:47:02815} // namespace net