blob: d35372d37ec0adcabb3fa6da3e2b367a5fce1231 [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);
45}
46
[email protected]fd7b7c92009-08-20 19:38:3047ConnectJob::~ConnectJob() {
[email protected]a2006ece2010-04-23 16:44:0248 if (delegate_ && !idle_) {
[email protected]fd7b7c92009-08-20 19:38:3049 // If the delegate was not NULLed, then NotifyDelegateOfCompletion has
[email protected]a2006ece2010-04-23 16:44:0250 // not been called yet. If we've started then we are cancelling.
[email protected]9e743cd2010-03-16 07:03:5351 net_log_.AddEvent(NetLog::TYPE_CANCELLED);
52 net_log_.EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB);
[email protected]fd7b7c92009-08-20 19:38:3053 }
54}
[email protected]2ab05b52009-07-01 23:57:5855
[email protected]974ebd62009-08-03 23:14:3456int ConnectJob::Connect() {
57 if (timeout_duration_ != base::TimeDelta())
58 timer_.Start(timeout_duration_, this, &ConnectJob::OnTimeout);
[email protected]fd7b7c92009-08-20 19:38:3059
[email protected]fd9c0d92010-03-23 17:47:4960 net_log_.BeginEventWithString(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB,
[email protected]d13f51b2010-04-27 23:20:4561 "group_name", group_name_);
[email protected]a2006ece2010-04-23 16:44:0262 idle_ = false;
[email protected]fd7b7c92009-08-20 19:38:3063
64 int rv = ConnectInternal();
65
66 if (rv != ERR_IO_PENDING) {
67 delegate_ = NULL;
[email protected]9e743cd2010-03-16 07:03:5368 net_log_.EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB);
[email protected]fd7b7c92009-08-20 19:38:3069 }
70
71 return rv;
72}
73
74void ConnectJob::NotifyDelegateOfCompletion(int rv) {
75 // The delegate will delete |this|.
76 Delegate *delegate = delegate_;
77 delegate_ = NULL;
78
[email protected]9e743cd2010-03-16 07:03:5379 net_log_.EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB);
[email protected]fd7b7c92009-08-20 19:38:3080
81 delegate->OnConnectJobComplete(rv, this);
[email protected]974ebd62009-08-03 23:14:3482}
83
[email protected]a796bcec2010-03-22 17:17:2684void ConnectJob::ResetTimer(base::TimeDelta remaining_time) {
85 timer_.Stop();
86 timer_.Start(remaining_time, this, &ConnectJob::OnTimeout);
87}
88
[email protected]974ebd62009-08-03 23:14:3489void ConnectJob::OnTimeout() {
[email protected]6e713f02009-08-06 02:56:4090 // Make sure the socket is NULL before calling into |delegate|.
91 set_socket(NULL);
[email protected]fd7b7c92009-08-20 19:38:3092
[email protected]9e743cd2010-03-16 07:03:5393 net_log_.AddEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_TIMED_OUT);
[email protected]fd7b7c92009-08-20 19:38:3094
95 NotifyDelegateOfCompletion(ERR_TIMED_OUT);
[email protected]974ebd62009-08-03 23:14:3496}
97
[email protected]d80a4322009-08-14 07:07:4998namespace internal {
99
[email protected]fd4fe0b2010-02-08 23:02:15100ClientSocketPoolBaseHelper::Request::Request(
101 ClientSocketHandle* handle,
102 CompletionCallback* callback,
103 RequestPriority priority,
[email protected]9e743cd2010-03-16 07:03:53104 const BoundNetLog& net_log)
[email protected]fd4fe0b2010-02-08 23:02:15105 : handle_(handle), callback_(callback), priority_(priority),
[email protected]9e743cd2010-03-16 07:03:53106 net_log_(net_log) {}
[email protected]fd4fe0b2010-02-08 23:02:15107
108ClientSocketPoolBaseHelper::Request::~Request() {}
109
[email protected]d80a4322009-08-14 07:07:49110ClientSocketPoolBaseHelper::ClientSocketPoolBaseHelper(
[email protected]211d2172009-07-22 15:48:53111 int max_sockets,
[email protected]ff579d42009-06-24 15:47:02112 int max_sockets_per_group,
[email protected]9bf28db2009-08-29 01:35:16113 base::TimeDelta unused_idle_socket_timeout,
114 base::TimeDelta used_idle_socket_timeout,
[email protected]61a86c42010-04-19 22:45:53115 ConnectJobFactory* connect_job_factory,
116 NetworkChangeNotifier* network_change_notifier)
[email protected]ff579d42009-06-24 15:47:02117 : idle_socket_count_(0),
[email protected]211d2172009-07-22 15:48:53118 connecting_socket_count_(0),
119 handed_out_socket_count_(0),
120 max_sockets_(max_sockets),
[email protected]ff579d42009-06-24 15:47:02121 max_sockets_per_group_(max_sockets_per_group),
[email protected]9bf28db2009-08-29 01:35:16122 unused_idle_socket_timeout_(unused_idle_socket_timeout),
123 used_idle_socket_timeout_(used_idle_socket_timeout),
[email protected]211d2172009-07-22 15:48:53124 may_have_stalled_group_(false),
[email protected]100d5fb92009-12-21 21:08:35125 connect_job_factory_(connect_job_factory),
[email protected]61a86c42010-04-19 22:45:53126 network_change_notifier_(network_change_notifier),
[email protected]7c28e9a2010-03-20 01:16:13127 backup_jobs_enabled_(false),
[email protected]6b624c62010-03-14 08:37:32128 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
[email protected]211d2172009-07-22 15:48:53129 DCHECK_LE(0, max_sockets_per_group);
130 DCHECK_LE(max_sockets_per_group, max_sockets);
[email protected]61a86c42010-04-19 22:45:53131
132 if (network_change_notifier_)
133 network_change_notifier_->AddObserver(this);
[email protected]211d2172009-07-22 15:48:53134}
[email protected]ff579d42009-06-24 15:47:02135
[email protected]d80a4322009-08-14 07:07:49136ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() {
[email protected]4d3b05d2010-01-27 21:27:29137 CancelAllConnectJobs();
138
[email protected]ff579d42009-06-24 15:47:02139 // Clean up any idle sockets. Assert that we have no remaining active
140 // sockets or pending requests. They should have all been cleaned up prior
141 // to the manager being destroyed.
142 CloseIdleSockets();
[email protected]6b624c62010-03-14 08:37:32143 CHECK(group_map_.empty());
[email protected]4d3b05d2010-01-27 21:27:29144 DCHECK_EQ(0, connecting_socket_count_);
[email protected]61a86c42010-04-19 22:45:53145
146 if (network_change_notifier_)
147 network_change_notifier_->RemoveObserver(this);
[email protected]ff579d42009-06-24 15:47:02148}
149
150// InsertRequestIntoQueue inserts the request into the queue based on
151// priority. Highest priorities are closest to the front. Older requests are
152// prioritized over requests of equal priority.
153//
154// static
[email protected]d80a4322009-08-14 07:07:49155void ClientSocketPoolBaseHelper::InsertRequestIntoQueue(
156 const Request* r, RequestQueue* pending_requests) {
[email protected]ff579d42009-06-24 15:47:02157 RequestQueue::iterator it = pending_requests->begin();
[email protected]ac790b42009-12-02 04:31:31158 while (it != pending_requests->end() && r->priority() >= (*it)->priority())
[email protected]ff579d42009-06-24 15:47:02159 ++it;
160 pending_requests->insert(it, r);
161}
162
[email protected]fd7b7c92009-08-20 19:38:30163// static
164const ClientSocketPoolBaseHelper::Request*
165ClientSocketPoolBaseHelper::RemoveRequestFromQueue(
166 RequestQueue::iterator it, RequestQueue* pending_requests) {
167 const Request* req = *it;
[email protected]fd7b7c92009-08-20 19:38:30168 pending_requests->erase(it);
169 return req;
170}
171
[email protected]d80a4322009-08-14 07:07:49172int ClientSocketPoolBaseHelper::RequestSocket(
[email protected]ff579d42009-06-24 15:47:02173 const std::string& group_name,
[email protected]d80a4322009-08-14 07:07:49174 const Request* request) {
[email protected]9e743cd2010-03-16 07:03:53175 request->net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]fd4fe0b2010-02-08 23:02:15176 Group& group = group_map_[group_name];
177 int rv = RequestSocketInternal(group_name, request);
178 if (rv != ERR_IO_PENDING)
[email protected]9e743cd2010-03-16 07:03:53179 request->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]fd4fe0b2010-02-08 23:02:15180 else
181 InsertRequestIntoQueue(request, &group.pending_requests);
182 return rv;
183}
184
185int ClientSocketPoolBaseHelper::RequestSocketInternal(
186 const std::string& group_name,
187 const Request* request) {
[email protected]d80a4322009-08-14 07:07:49188 DCHECK_GE(request->priority(), 0);
189 CompletionCallback* const callback = request->callback();
190 CHECK(callback);
191 ClientSocketHandle* const handle = request->handle();
192 CHECK(handle);
[email protected]ff579d42009-06-24 15:47:02193 Group& group = group_map_[group_name];
194
[email protected]42df4e8e2010-04-13 22:02:56195 // Can we make another active socket now?
196 if (ReachedMaxSocketsLimit() ||
197 !group.HasAvailableSocketSlot(max_sockets_per_group_)) {
198 if (ReachedMaxSocketsLimit()) {
199 // We could check if we really have a stalled group here, but it requires
200 // a scan of all groups, so just flip a flag here, and do the check later.
201 may_have_stalled_group_ = true;
202
203 request->net_log().AddEvent(NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS);
204 } else {
[email protected]d13f51b2010-04-27 23:20:45205 request->net_log().AddEvent(
206 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP);
[email protected]42df4e8e2010-04-13 22:02:56207 }
208 return ERR_IO_PENDING;
209 }
210
[email protected]65552102010-04-09 22:58:10211 // Try to reuse a socket.
212 while (!group.idle_sockets.empty()) {
213 IdleSocket idle_socket = group.idle_sockets.back();
214 group.idle_sockets.pop_back();
215 DecrementIdleCount();
216 if (idle_socket.socket->IsConnectedAndIdle()) {
217 // We found one we can reuse!
218 base::TimeDelta idle_time =
219 base::TimeTicks::Now() - idle_socket.start_time;
220 HandOutSocket(
221 idle_socket.socket, idle_socket.used, handle, idle_time, &group,
222 request->net_log());
223 return OK;
224 }
225 delete idle_socket.socket;
226 }
227
[email protected]5edbf8d2010-01-13 18:44:11228 // See if we already have enough connect jobs or sockets that will be released
229 // soon.
[email protected]4d3b05d2010-01-27 21:27:29230 if (group.HasReleasingSockets()) {
[email protected]5edbf8d2010-01-13 18:44:11231 return ERR_IO_PENDING;
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]9e743cd2010-03-16 07:03:53235 BoundNetLog job_net_log = BoundNetLog::Make(
236 request->net_log().net_log(), NetLog::SOURCE_CONNECT_JOB);
[email protected]a2006ece2010-04-23 16:44:02237 request->net_log().BeginEventWithInteger(
[email protected]d13f51b2010-04-27 23:20:45238 NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID,
239 "source_id", job_net_log.source().id);
[email protected]fd7b7c92009-08-20 19:38:30240
[email protected]2ab05b52009-07-01 23:57:58241 scoped_ptr<ConnectJob> connect_job(
[email protected]fd7b7c92009-08-20 19:38:30242 connect_job_factory_->NewConnectJob(group_name, *request, this,
[email protected]9e743cd2010-03-16 07:03:53243 job_net_log));
[email protected]ff579d42009-06-24 15:47:02244
[email protected]2ab05b52009-07-01 23:57:58245 int rv = connect_job->Connect();
[email protected]2ab05b52009-07-01 23:57:58246 if (rv == OK) {
[email protected]a2006ece2010-04-23 16:44:02247 request->net_log().EndEventWithInteger(
[email protected]d13f51b2010-04-27 23:20:45248 NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID,
249 "source_id", job_net_log.source().id);
[email protected]2ab05b52009-07-01 23:57:58250 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */,
[email protected]9e743cd2010-03-16 07:03:53251 handle, base::TimeDelta(), &group, request->net_log());
[email protected]2ab05b52009-07-01 23:57:58252 } else if (rv == ERR_IO_PENDING) {
[email protected]6b624c62010-03-14 08:37:32253 // If we don't have any sockets in this group, set a timer for potentially
254 // creating a new one. If the SYN is lost, this backup socket may complete
255 // before the slow socket, improving end user latency.
[email protected]7c28e9a2010-03-20 01:16:13256 if (group.IsEmpty() && !group.backup_job && backup_jobs_enabled_) {
[email protected]6b624c62010-03-14 08:37:32257 group.backup_job = connect_job_factory_->NewConnectJob(group_name,
258 *request,
259 this,
[email protected]9e743cd2010-03-16 07:03:53260 job_net_log);
[email protected]6b624c62010-03-14 08:37:32261 StartBackupSocketTimer(group_name);
262 }
263
[email protected]211d2172009-07-22 15:48:53264 connecting_socket_count_++;
265
[email protected]5fc08e32009-07-15 17:09:57266 ConnectJob* job = connect_job.release();
[email protected]5fc08e32009-07-15 17:09:57267 group.jobs.insert(job);
[email protected]a2006ece2010-04-23 16:44:02268 } else {
269 request->net_log().EndEventWithInteger(
[email protected]d13f51b2010-04-27 23:20:45270 NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID,
271 "source_id", job_net_log.source().id);
[email protected]a2006ece2010-04-23 16:44:02272 if (group.IsEmpty())
273 group_map_.erase(group_name);
[email protected]2ab05b52009-07-01 23:57:58274 }
[email protected]ff579d42009-06-24 15:47:02275
[email protected]2ab05b52009-07-01 23:57:58276 return rv;
[email protected]ff579d42009-06-24 15:47:02277}
278
[email protected]6b624c62010-03-14 08:37:32279void ClientSocketPoolBaseHelper::StartBackupSocketTimer(
280 const std::string& group_name) {
281 CHECK(ContainsKey(group_map_, group_name));
282 Group& group = group_map_[group_name];
283
284 // Only allow one timer pending to create a backup socket.
285 if (group.backup_task)
286 return;
287
288 group.backup_task = method_factory_.NewRunnableMethod(
289 &ClientSocketPoolBaseHelper::OnBackupSocketTimerFired, group_name);
290 MessageLoop::current()->PostDelayedTask(FROM_HERE, group.backup_task,
291 ConnectRetryIntervalMs());
292}
293
294void ClientSocketPoolBaseHelper::OnBackupSocketTimerFired(
295 const std::string& group_name) {
296 CHECK(ContainsKey(group_map_, group_name));
297
298 Group& group = group_map_[group_name];
299
300 CHECK(group.backup_task);
301 group.backup_task = NULL;
302
303 CHECK(group.backup_job);
304
[email protected]c901f6d2010-04-27 17:48:28305 // If our backup job is waiting on DNS, or if we can't create any sockets
306 // right now due to limits, just reset the timer.
[email protected]6b624c62010-03-14 08:37:32307 CHECK(group.jobs.size());
[email protected]c901f6d2010-04-27 17:48:28308 if (ReachedMaxSocketsLimit() ||
309 !group.HasAvailableSocketSlot(max_sockets_per_group_) ||
310 (*group.jobs.begin())->GetLoadState() == LOAD_STATE_RESOLVING_HOST) {
[email protected]9e743cd2010-03-16 07:03:53311 group.backup_job->net_log().EndEvent(
312 NetLog::TYPE_SOCKET_BACKUP_TIMER_EXTENDED);
[email protected]6b624c62010-03-14 08:37:32313 StartBackupSocketTimer(group_name);
314 return;
315 }
316
[email protected]9e743cd2010-03-16 07:03:53317 group.backup_job->net_log().AddEvent(NetLog::TYPE_SOCKET_BACKUP_CREATED);
[email protected]6b624c62010-03-14 08:37:32318 SIMPLE_STATS_COUNTER("socket.backup_created");
319 int rv = group.backup_job->Connect();
[email protected]c83658c2010-03-24 08:19:34320 connecting_socket_count_++;
321 group.jobs.insert(group.backup_job);
322 ConnectJob* job = group.backup_job;
323 group.backup_job = NULL;
324 if (rv != ERR_IO_PENDING)
325 OnConnectJobComplete(rv, job);
[email protected]6b624c62010-03-14 08:37:32326}
327
[email protected]d80a4322009-08-14 07:07:49328void ClientSocketPoolBaseHelper::CancelRequest(
329 const std::string& group_name, const ClientSocketHandle* handle) {
[email protected]ff579d42009-06-24 15:47:02330 CHECK(ContainsKey(group_map_, group_name));
331
332 Group& group = group_map_[group_name];
333
[email protected]ff579d42009-06-24 15:47:02334 // Search pending_requests for matching handle.
335 RequestQueue::iterator it = group.pending_requests.begin();
336 for (; it != group.pending_requests.end(); ++it) {
[email protected]d80a4322009-08-14 07:07:49337 if ((*it)->handle() == handle) {
[email protected]fd7b7c92009-08-20 19:38:30338 const Request* req = RemoveRequestFromQueue(it, &group.pending_requests);
[email protected]9e743cd2010-03-16 07:03:53339 req->net_log().AddEvent(NetLog::TYPE_CANCELLED);
340 req->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]fd7b7c92009-08-20 19:38:30341 delete req;
[email protected]a796bcec2010-03-22 17:17:26342 // Let one connect job connect and become idle for potential future use.
[email protected]4d3b05d2010-01-27 21:27:29343 if (group.jobs.size() > group.pending_requests.size() + 1) {
[email protected]974ebd62009-08-03 23:14:34344 // TODO(willchan): Cancel the job in the earliest LoadState.
[email protected]4d3b05d2010-01-27 21:27:29345 RemoveConnectJob(*group.jobs.begin(), &group);
[email protected]974ebd62009-08-03 23:14:34346 OnAvailableSocketSlot(group_name, &group);
347 }
[email protected]ff579d42009-06-24 15:47:02348 return;
349 }
350 }
[email protected]ff579d42009-06-24 15:47:02351}
352
[email protected]d80a4322009-08-14 07:07:49353void ClientSocketPoolBaseHelper::ReleaseSocket(const std::string& group_name,
354 ClientSocket* socket) {
[email protected]5edbf8d2010-01-13 18:44:11355 Group& group = group_map_[group_name];
356 group.num_releasing_sockets++;
357 DCHECK_LE(group.num_releasing_sockets, group.active_socket_count);
[email protected]ff579d42009-06-24 15:47:02358 // Run this asynchronously to allow the caller to finish before we let
359 // another to begin doing work. This also avoids nasty recursion issues.
360 // NOTE: We cannot refer to the handle argument after this method returns.
361 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]d80a4322009-08-14 07:07:49362 this, &ClientSocketPoolBaseHelper::DoReleaseSocket, group_name, socket));
[email protected]ff579d42009-06-24 15:47:02363}
364
[email protected]d80a4322009-08-14 07:07:49365void ClientSocketPoolBaseHelper::CloseIdleSockets() {
[email protected]ff579d42009-06-24 15:47:02366 CleanupIdleSockets(true);
367}
368
[email protected]d80a4322009-08-14 07:07:49369int ClientSocketPoolBaseHelper::IdleSocketCountInGroup(
[email protected]ff579d42009-06-24 15:47:02370 const std::string& group_name) const {
371 GroupMap::const_iterator i = group_map_.find(group_name);
372 CHECK(i != group_map_.end());
373
374 return i->second.idle_sockets.size();
375}
376
[email protected]d80a4322009-08-14 07:07:49377LoadState ClientSocketPoolBaseHelper::GetLoadState(
[email protected]ff579d42009-06-24 15:47:02378 const std::string& group_name,
379 const ClientSocketHandle* handle) const {
380 if (!ContainsKey(group_map_, group_name)) {
381 NOTREACHED() << "ClientSocketPool does not contain group: " << group_name
382 << " for handle: " << handle;
383 return LOAD_STATE_IDLE;
384 }
385
386 // Can't use operator[] since it is non-const.
387 const Group& group = group_map_.find(group_name)->second;
388
[email protected]ff579d42009-06-24 15:47:02389 // Search pending_requests for matching handle.
390 RequestQueue::const_iterator it = group.pending_requests.begin();
[email protected]5fc08e32009-07-15 17:09:57391 for (size_t i = 0; it != group.pending_requests.end(); ++it, ++i) {
[email protected]d80a4322009-08-14 07:07:49392 if ((*it)->handle() == handle) {
[email protected]4d3b05d2010-01-27 21:27:29393 if (i < group.jobs.size()) {
[email protected]5fc08e32009-07-15 17:09:57394 LoadState max_state = LOAD_STATE_IDLE;
395 for (ConnectJobSet::const_iterator job_it = group.jobs.begin();
396 job_it != group.jobs.end(); ++job_it) {
[email protected]46451352009-09-01 14:54:21397 max_state = std::max(max_state, (*job_it)->GetLoadState());
[email protected]5fc08e32009-07-15 17:09:57398 }
399 return max_state;
400 } else {
401 // TODO(wtc): Add a state for being on the wait list.
402 // See http://www.crbug.com/5077.
403 return LOAD_STATE_IDLE;
404 }
[email protected]ff579d42009-06-24 15:47:02405 }
406 }
407
408 NOTREACHED();
409 return LOAD_STATE_IDLE;
410}
411
[email protected]d80a4322009-08-14 07:07:49412bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup(
[email protected]9bf28db2009-08-29 01:35:16413 base::TimeTicks now,
414 base::TimeDelta timeout) const {
415 bool timed_out = (now - start_time) >= timeout;
[email protected]5fc08e32009-07-15 17:09:57416 return timed_out ||
417 !(used ? socket->IsConnectedAndIdle() : socket->IsConnected());
[email protected]ff579d42009-06-24 15:47:02418}
419
[email protected]d80a4322009-08-14 07:07:49420void ClientSocketPoolBaseHelper::CleanupIdleSockets(bool force) {
[email protected]ff579d42009-06-24 15:47:02421 if (idle_socket_count_ == 0)
422 return;
423
424 // Current time value. Retrieving it once at the function start rather than
425 // inside the inner loop, since it shouldn't change by any meaningful amount.
426 base::TimeTicks now = base::TimeTicks::Now();
427
428 GroupMap::iterator i = group_map_.begin();
429 while (i != group_map_.end()) {
430 Group& group = i->second;
431
432 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
433 while (j != group.idle_sockets.end()) {
[email protected]9bf28db2009-08-29 01:35:16434 base::TimeDelta timeout =
435 j->used ? used_idle_socket_timeout_ : unused_idle_socket_timeout_;
436 if (force || j->ShouldCleanup(now, timeout)) {
[email protected]ff579d42009-06-24 15:47:02437 delete j->socket;
438 j = group.idle_sockets.erase(j);
439 DecrementIdleCount();
440 } else {
441 ++j;
442 }
443 }
444
445 // Delete group if no longer needed.
[email protected]2ab05b52009-07-01 23:57:58446 if (group.IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02447 group_map_.erase(i++);
448 } else {
449 ++i;
450 }
451 }
452}
453
[email protected]d80a4322009-08-14 07:07:49454void ClientSocketPoolBaseHelper::IncrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02455 if (++idle_socket_count_ == 1)
456 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this,
[email protected]d80a4322009-08-14 07:07:49457 &ClientSocketPoolBaseHelper::OnCleanupTimerFired);
[email protected]ff579d42009-06-24 15:47:02458}
459
[email protected]d80a4322009-08-14 07:07:49460void ClientSocketPoolBaseHelper::DecrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02461 if (--idle_socket_count_ == 0)
462 timer_.Stop();
463}
464
[email protected]d80a4322009-08-14 07:07:49465void ClientSocketPoolBaseHelper::DoReleaseSocket(const std::string& group_name,
466 ClientSocket* socket) {
[email protected]ff579d42009-06-24 15:47:02467 GroupMap::iterator i = group_map_.find(group_name);
468 CHECK(i != group_map_.end());
469
470 Group& group = i->second;
471
[email protected]5edbf8d2010-01-13 18:44:11472 group.num_releasing_sockets--;
473 DCHECK_GE(group.num_releasing_sockets, 0);
474
[email protected]b1f031dd2010-03-02 23:19:33475 CHECK_GT(handed_out_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53476 handed_out_socket_count_--;
477
[email protected]b1f031dd2010-03-02 23:19:33478 CHECK_GT(group.active_socket_count, 0);
[email protected]2ab05b52009-07-01 23:57:58479 group.active_socket_count--;
[email protected]ff579d42009-06-24 15:47:02480
481 const bool can_reuse = socket->IsConnectedAndIdle();
482 if (can_reuse) {
[email protected]5fc08e32009-07-15 17:09:57483 AddIdleSocket(socket, true /* used socket */, &group);
[email protected]ff579d42009-06-24 15:47:02484 } else {
485 delete socket;
486 }
487
[email protected]2ab05b52009-07-01 23:57:58488 OnAvailableSocketSlot(group_name, &group);
[email protected]4f2abec2010-02-03 18:10:16489
490 // If there are no more releasing sockets, then we might have to process
491 // multiple available socket slots, since we stalled their processing until
492 // all sockets have been released.
[email protected]4f1e4982010-03-02 18:31:04493 i = group_map_.find(group_name);
494 if (i == group_map_.end() || i->second.num_releasing_sockets > 0)
[email protected]4f2abec2010-02-03 18:10:16495 return;
496
497 while (true) {
498 // We can't activate more sockets since we're already at our global limit.
499 if (ReachedMaxSocketsLimit())
500 return;
[email protected]616925a2010-03-02 19:02:38501
[email protected]4f2abec2010-02-03 18:10:16502 // |group| might now be deleted.
503 i = group_map_.find(group_name);
504 if (i == group_map_.end())
505 return;
506
507 group = i->second;
[email protected]616925a2010-03-02 19:02:38508
[email protected]4f2abec2010-02-03 18:10:16509 // If we already have enough ConnectJobs to satisfy the pending requests,
510 // don't bother starting up more.
511 if (group.pending_requests.size() <= group.jobs.size())
512 return;
513
514 if (!group.HasAvailableSocketSlot(max_sockets_per_group_))
515 return;
516
517 OnAvailableSocketSlot(group_name, &group);
518 }
[email protected]ff579d42009-06-24 15:47:02519}
520
[email protected]211d2172009-07-22 15:48:53521// Search for the highest priority pending request, amongst the groups that
522// are not at the |max_sockets_per_group_| limit. Note: for requests with
523// the same priority, the winner is based on group hash ordering (and not
524// insertion order).
[email protected]d80a4322009-08-14 07:07:49525int ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group,
526 std::string* group_name) {
[email protected]211d2172009-07-22 15:48:53527 Group* top_group = NULL;
528 const std::string* top_group_name = NULL;
529 int stalled_group_count = 0;
530 for (GroupMap::iterator i = group_map_.begin();
531 i != group_map_.end(); ++i) {
532 Group& group = i->second;
533 const RequestQueue& queue = group.pending_requests;
534 if (queue.empty())
535 continue;
[email protected]6427fe22010-04-16 22:27:41536 bool has_unused_slot =
537 group.HasAvailableSocketSlot(max_sockets_per_group_) &&
538 group.pending_requests.size() > group.jobs.size();
539 if (has_unused_slot) {
[email protected]211d2172009-07-22 15:48:53540 stalled_group_count++;
[email protected]6427fe22010-04-16 22:27:41541 bool has_higher_priority = !top_group ||
542 group.TopPendingPriority() < top_group->TopPendingPriority();
543 if (has_higher_priority) {
544 top_group = &group;
545 top_group_name = &i->first;
546 }
[email protected]211d2172009-07-22 15:48:53547 }
548 }
549 if (top_group) {
550 *group = top_group;
551 *group_name = *top_group_name;
552 }
553 return stalled_group_count;
554}
555
[email protected]d80a4322009-08-14 07:07:49556void ClientSocketPoolBaseHelper::OnConnectJobComplete(
557 int result, ConnectJob* job) {
[email protected]2ab05b52009-07-01 23:57:58558 DCHECK_NE(ERR_IO_PENDING, result);
559 const std::string group_name = job->group_name();
[email protected]ff579d42009-06-24 15:47:02560 GroupMap::iterator group_it = group_map_.find(group_name);
561 CHECK(group_it != group_map_.end());
562 Group& group = group_it->second;
563
[email protected]6b624c62010-03-14 08:37:32564 // We've had a connect on the socket; discard any pending backup job
565 // for this group and kill the pending task.
566 group.CleanupBackupJob();
567
[email protected]5fc08e32009-07-15 17:09:57568 scoped_ptr<ClientSocket> socket(job->ReleaseSocket());
[email protected]ff579d42009-06-24 15:47:02569
[email protected]9e743cd2010-03-16 07:03:53570 BoundNetLog job_log = job->net_log();
[email protected]4d3b05d2010-01-27 21:27:29571 RemoveConnectJob(job, &group);
[email protected]5fc08e32009-07-15 17:09:57572
[email protected]4d3b05d2010-01-27 21:27:29573 if (result == OK) {
574 DCHECK(socket.get());
[email protected]fd7b7c92009-08-20 19:38:30575 if (!group.pending_requests.empty()) {
[email protected]4d3b05d2010-01-27 21:27:29576 scoped_ptr<const Request> r(RemoveRequestFromQueue(
[email protected]fd7b7c92009-08-20 19:38:30577 group.pending_requests.begin(), &group.pending_requests));
[email protected]a2006ece2010-04-23 16:44:02578 r->net_log().EndEventWithInteger(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID,
[email protected]d13f51b2010-04-27 23:20:45579 "source_id", job_log.source().id);
[email protected]9e743cd2010-03-16 07:03:53580 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]4d3b05d2010-01-27 21:27:29581 HandOutSocket(
582 socket.release(), false /* unused socket */, r->handle(),
[email protected]9e743cd2010-03-16 07:03:53583 base::TimeDelta(), &group, r->net_log());
[email protected]4d3b05d2010-01-27 21:27:29584 r->callback()->Run(result);
[email protected]5fc08e32009-07-15 17:09:57585 } else {
[email protected]4d3b05d2010-01-27 21:27:29586 AddIdleSocket(socket.release(), false /* unused socket */, &group);
587 OnAvailableSocketSlot(group_name, &group);
[email protected]5fc08e32009-07-15 17:09:57588 }
[email protected]94c20472010-01-14 08:14:36589 } else {
[email protected]4d3b05d2010-01-27 21:27:29590 DCHECK(!socket.get());
591 if (!group.pending_requests.empty()) {
592 scoped_ptr<const Request> r(RemoveRequestFromQueue(
593 group.pending_requests.begin(), &group.pending_requests));
[email protected]a2006ece2010-04-23 16:44:02594 r->net_log().EndEventWithInteger(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID,
[email protected]d13f51b2010-04-27 23:20:45595 "source_id", job_log.source().id);
[email protected]9e743cd2010-03-16 07:03:53596 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]4d3b05d2010-01-27 21:27:29597 r->callback()->Run(result);
598 }
599 MaybeOnAvailableSocketSlot(group_name);
[email protected]ff579d42009-06-24 15:47:02600 }
[email protected]ff579d42009-06-24 15:47:02601}
602
[email protected]61a86c42010-04-19 22:45:53603void ClientSocketPoolBaseHelper::OnIPAddressChanged() {
604 CloseIdleSockets();
605}
606
[email protected]4d3b05d2010-01-27 21:27:29607void ClientSocketPoolBaseHelper::RemoveConnectJob(const ConnectJob *job,
608 Group* group) {
[email protected]b1f031dd2010-03-02 23:19:33609 CHECK_GT(connecting_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53610 connecting_socket_count_--;
611
[email protected]4d3b05d2010-01-27 21:27:29612 DCHECK(job);
613 delete job;
[email protected]5fc08e32009-07-15 17:09:57614
615 if (group) {
616 DCHECK(ContainsKey(group->jobs, job));
617 group->jobs.erase(job);
618 }
[email protected]ff579d42009-06-24 15:47:02619}
620
[email protected]d80a4322009-08-14 07:07:49621void ClientSocketPoolBaseHelper::MaybeOnAvailableSocketSlot(
[email protected]2ab05b52009-07-01 23:57:58622 const std::string& group_name) {
623 GroupMap::iterator it = group_map_.find(group_name);
624 if (it != group_map_.end()) {
625 Group& group = it->second;
626 if (group.HasAvailableSocketSlot(max_sockets_per_group_))
627 OnAvailableSocketSlot(group_name, &group);
628 }
629}
[email protected]ff579d42009-06-24 15:47:02630
[email protected]d80a4322009-08-14 07:07:49631void ClientSocketPoolBaseHelper::OnAvailableSocketSlot(
632 const std::string& group_name, Group* group) {
[email protected]211d2172009-07-22 15:48:53633 if (may_have_stalled_group_) {
634 std::string top_group_name;
[email protected]bed37d442009-08-20 19:58:20635 Group* top_group = NULL;
[email protected]211d2172009-07-22 15:48:53636 int stalled_group_count = FindTopStalledGroup(&top_group, &top_group_name);
637 if (stalled_group_count <= 1)
638 may_have_stalled_group_ = false;
[email protected]42df4e8e2010-04-13 22:02:56639 if (stalled_group_count >= 1)
[email protected]211d2172009-07-22 15:48:53640 ProcessPendingRequest(top_group_name, top_group);
641 } else if (!group->pending_requests.empty()) {
[email protected]ff579d42009-06-24 15:47:02642 ProcessPendingRequest(group_name, group);
643 // |group| may no longer be valid after this point. Be careful not to
644 // access it again.
[email protected]2ab05b52009-07-01 23:57:58645 } else if (group->IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02646 // Delete |group| if no longer needed. |group| will no longer be valid.
[email protected]ff579d42009-06-24 15:47:02647 group_map_.erase(group_name);
[email protected]ff579d42009-06-24 15:47:02648 }
649}
650
[email protected]d80a4322009-08-14 07:07:49651void ClientSocketPoolBaseHelper::ProcessPendingRequest(
652 const std::string& group_name, Group* group) {
[email protected]fd4fe0b2010-02-08 23:02:15653 scoped_ptr<const Request> r(*group->pending_requests.begin());
654 int rv = RequestSocketInternal(group_name, r.get());
[email protected]ff579d42009-06-24 15:47:02655
[email protected]2ab05b52009-07-01 23:57:58656 if (rv != ERR_IO_PENDING) {
[email protected]9e743cd2010-03-16 07:03:53657 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]fd4fe0b2010-02-08 23:02:15658 RemoveRequestFromQueue(group->pending_requests.begin(),
659 &group->pending_requests);
[email protected]d80a4322009-08-14 07:07:49660 r->callback()->Run(rv);
[email protected]2ab05b52009-07-01 23:57:58661 if (rv != OK) {
662 // |group| may be invalid after the callback, we need to search
663 // |group_map_| again.
664 MaybeOnAvailableSocketSlot(group_name);
665 }
[email protected]d80a4322009-08-14 07:07:49666 } else {
667 r.release();
[email protected]2ab05b52009-07-01 23:57:58668 }
669}
670
[email protected]d80a4322009-08-14 07:07:49671void ClientSocketPoolBaseHelper::HandOutSocket(
[email protected]2ab05b52009-07-01 23:57:58672 ClientSocket* socket,
673 bool reused,
674 ClientSocketHandle* handle,
[email protected]f9d285c2009-08-17 19:54:29675 base::TimeDelta idle_time,
[email protected]fd4fe0b2010-02-08 23:02:15676 Group* group,
[email protected]9e743cd2010-03-16 07:03:53677 const BoundNetLog& net_log) {
[email protected]2ab05b52009-07-01 23:57:58678 DCHECK(socket);
679 handle->set_socket(socket);
680 handle->set_is_reused(reused);
[email protected]f9d285c2009-08-17 19:54:29681 handle->set_idle_time(idle_time);
[email protected]211d2172009-07-22 15:48:53682
[email protected]d13f51b2010-04-27 23:20:45683 if (reused) {
684 net_log.AddEventWithInteger(
685 NetLog::TYPE_SOCKET_POOL_REUSED_AN_EXISTING_SOCKET,
686 "idle_ms", static_cast<int>(idle_time.InMilliseconds()));
[email protected]fd4fe0b2010-02-08 23:02:15687 }
[email protected]d13f51b2010-04-27 23:20:45688
[email protected]a2006ece2010-04-23 16:44:02689 net_log.AddEventWithInteger(NetLog::TYPE_SOCKET_POOL_SOCKET_ID,
[email protected]d13f51b2010-04-27 23:20:45690 "source_id", socket->NetLog().source().id);
[email protected]fd4fe0b2010-02-08 23:02:15691
[email protected]211d2172009-07-22 15:48:53692 handed_out_socket_count_++;
[email protected]2ab05b52009-07-01 23:57:58693 group->active_socket_count++;
[email protected]ff579d42009-06-24 15:47:02694}
695
[email protected]d80a4322009-08-14 07:07:49696void ClientSocketPoolBaseHelper::AddIdleSocket(
[email protected]5fc08e32009-07-15 17:09:57697 ClientSocket* socket, bool used, Group* group) {
698 DCHECK(socket);
699 IdleSocket idle_socket;
700 idle_socket.socket = socket;
701 idle_socket.start_time = base::TimeTicks::Now();
702 idle_socket.used = used;
703
704 group->idle_sockets.push_back(idle_socket);
705 IncrementIdleCount();
706}
707
[email protected]d80a4322009-08-14 07:07:49708void ClientSocketPoolBaseHelper::CancelAllConnectJobs() {
[email protected]5fc08e32009-07-15 17:09:57709 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end();) {
710 Group& group = i->second;
[email protected]4d3b05d2010-01-27 21:27:29711 connecting_socket_count_ -= group.jobs.size();
[email protected]5fc08e32009-07-15 17:09:57712 STLDeleteElements(&group.jobs);
713
[email protected]6b624c62010-03-14 08:37:32714 if (group.backup_task) {
715 group.backup_task->Cancel();
716 group.backup_task = NULL;
717 }
718
[email protected]5fc08e32009-07-15 17:09:57719 // Delete group if no longer needed.
720 if (group.IsEmpty()) {
[email protected]5fc08e32009-07-15 17:09:57721 group_map_.erase(i++);
722 } else {
723 ++i;
724 }
725 }
726}
727
[email protected]d80a4322009-08-14 07:07:49728bool ClientSocketPoolBaseHelper::ReachedMaxSocketsLimit() const {
[email protected]211d2172009-07-22 15:48:53729 // Each connecting socket will eventually connect and be handed out.
[email protected]42df4e8e2010-04-13 22:02:56730 int total = handed_out_socket_count_ + connecting_socket_count_;
[email protected]211d2172009-07-22 15:48:53731 DCHECK_LE(total, max_sockets_);
[email protected]c901f6d2010-04-27 17:48:28732 if (total < max_sockets_)
733 return false;
734 LOG(WARNING) << "ReachedMaxSocketsLimit: " << total << "/" << max_sockets_;
735 return true;
[email protected]211d2172009-07-22 15:48:53736}
737
[email protected]d80a4322009-08-14 07:07:49738} // namespace internal
739
[email protected]ff579d42009-06-24 15:47:02740} // namespace net