blob: 4b6cff0820b55697dca63d1916a2477b1af1a3b2 [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]9e743cd2010-03-16 07:03:5341 net_log_(net_log) {
[email protected]2ab05b52009-07-01 23:57:5842 DCHECK(!group_name.empty());
[email protected]2ab05b52009-07-01 23:57:5843 DCHECK(delegate);
44}
45
[email protected]fd7b7c92009-08-20 19:38:3046ConnectJob::~ConnectJob() {
47 if (delegate_) {
48 // If the delegate was not NULLed, then NotifyDelegateOfCompletion has
49 // not been called yet (hence we are cancelling).
[email protected]9e743cd2010-03-16 07:03:5350 net_log_.AddEvent(NetLog::TYPE_CANCELLED);
51 net_log_.EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB);
[email protected]fd7b7c92009-08-20 19:38:3052 }
53}
[email protected]2ab05b52009-07-01 23:57:5854
[email protected]974ebd62009-08-03 23:14:3455int ConnectJob::Connect() {
56 if (timeout_duration_ != base::TimeDelta())
57 timer_.Start(timeout_duration_, this, &ConnectJob::OnTimeout);
[email protected]fd7b7c92009-08-20 19:38:3058
[email protected]fd9c0d92010-03-23 17:47:4959 net_log_.BeginEventWithString(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB,
60 group_name_);
[email protected]fd7b7c92009-08-20 19:38:3061
62 int rv = ConnectInternal();
63
64 if (rv != ERR_IO_PENDING) {
65 delegate_ = NULL;
[email protected]9e743cd2010-03-16 07:03:5366 net_log_.EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB);
[email protected]fd7b7c92009-08-20 19:38:3067 }
68
69 return rv;
70}
71
72void ConnectJob::NotifyDelegateOfCompletion(int rv) {
73 // The delegate will delete |this|.
74 Delegate *delegate = delegate_;
75 delegate_ = NULL;
76
[email protected]9e743cd2010-03-16 07:03:5377 net_log_.EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB);
[email protected]fd7b7c92009-08-20 19:38:3078
79 delegate->OnConnectJobComplete(rv, this);
[email protected]974ebd62009-08-03 23:14:3480}
81
[email protected]a796bcec2010-03-22 17:17:2682void ConnectJob::ResetTimer(base::TimeDelta remaining_time) {
83 timer_.Stop();
84 timer_.Start(remaining_time, this, &ConnectJob::OnTimeout);
85}
86
[email protected]974ebd62009-08-03 23:14:3487void ConnectJob::OnTimeout() {
[email protected]6e713f02009-08-06 02:56:4088 // Make sure the socket is NULL before calling into |delegate|.
89 set_socket(NULL);
[email protected]fd7b7c92009-08-20 19:38:3090
[email protected]9e743cd2010-03-16 07:03:5391 net_log_.AddEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_TIMED_OUT);
[email protected]fd7b7c92009-08-20 19:38:3092
93 NotifyDelegateOfCompletion(ERR_TIMED_OUT);
[email protected]974ebd62009-08-03 23:14:3494}
95
[email protected]d80a4322009-08-14 07:07:4996namespace internal {
97
[email protected]fd4fe0b2010-02-08 23:02:1598ClientSocketPoolBaseHelper::Request::Request(
99 ClientSocketHandle* handle,
100 CompletionCallback* callback,
101 RequestPriority priority,
[email protected]9e743cd2010-03-16 07:03:53102 const BoundNetLog& net_log)
[email protected]fd4fe0b2010-02-08 23:02:15103 : handle_(handle), callback_(callback), priority_(priority),
[email protected]9e743cd2010-03-16 07:03:53104 net_log_(net_log) {}
[email protected]fd4fe0b2010-02-08 23:02:15105
106ClientSocketPoolBaseHelper::Request::~Request() {}
107
[email protected]d80a4322009-08-14 07:07:49108ClientSocketPoolBaseHelper::ClientSocketPoolBaseHelper(
[email protected]211d2172009-07-22 15:48:53109 int max_sockets,
[email protected]ff579d42009-06-24 15:47:02110 int max_sockets_per_group,
[email protected]9bf28db2009-08-29 01:35:16111 base::TimeDelta unused_idle_socket_timeout,
112 base::TimeDelta used_idle_socket_timeout,
[email protected]61a86c42010-04-19 22:45:53113 ConnectJobFactory* connect_job_factory,
114 NetworkChangeNotifier* network_change_notifier)
[email protected]ff579d42009-06-24 15:47:02115 : idle_socket_count_(0),
[email protected]211d2172009-07-22 15:48:53116 connecting_socket_count_(0),
117 handed_out_socket_count_(0),
118 max_sockets_(max_sockets),
[email protected]ff579d42009-06-24 15:47:02119 max_sockets_per_group_(max_sockets_per_group),
[email protected]9bf28db2009-08-29 01:35:16120 unused_idle_socket_timeout_(unused_idle_socket_timeout),
121 used_idle_socket_timeout_(used_idle_socket_timeout),
[email protected]211d2172009-07-22 15:48:53122 may_have_stalled_group_(false),
[email protected]100d5fb92009-12-21 21:08:35123 connect_job_factory_(connect_job_factory),
[email protected]61a86c42010-04-19 22:45:53124 network_change_notifier_(network_change_notifier),
[email protected]7c28e9a2010-03-20 01:16:13125 backup_jobs_enabled_(false),
[email protected]6b624c62010-03-14 08:37:32126 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
[email protected]211d2172009-07-22 15:48:53127 DCHECK_LE(0, max_sockets_per_group);
128 DCHECK_LE(max_sockets_per_group, max_sockets);
[email protected]61a86c42010-04-19 22:45:53129
130 if (network_change_notifier_)
131 network_change_notifier_->AddObserver(this);
[email protected]211d2172009-07-22 15:48:53132}
[email protected]ff579d42009-06-24 15:47:02133
[email protected]d80a4322009-08-14 07:07:49134ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() {
[email protected]4d3b05d2010-01-27 21:27:29135 CancelAllConnectJobs();
136
[email protected]ff579d42009-06-24 15:47:02137 // Clean up any idle sockets. Assert that we have no remaining active
138 // sockets or pending requests. They should have all been cleaned up prior
139 // to the manager being destroyed.
140 CloseIdleSockets();
[email protected]6b624c62010-03-14 08:37:32141 CHECK(group_map_.empty());
[email protected]4d3b05d2010-01-27 21:27:29142 DCHECK_EQ(0, connecting_socket_count_);
[email protected]61a86c42010-04-19 22:45:53143
144 if (network_change_notifier_)
145 network_change_notifier_->RemoveObserver(this);
[email protected]ff579d42009-06-24 15:47:02146}
147
148// InsertRequestIntoQueue inserts the request into the queue based on
149// priority. Highest priorities are closest to the front. Older requests are
150// prioritized over requests of equal priority.
151//
152// static
[email protected]d80a4322009-08-14 07:07:49153void ClientSocketPoolBaseHelper::InsertRequestIntoQueue(
154 const Request* r, RequestQueue* pending_requests) {
[email protected]ff579d42009-06-24 15:47:02155 RequestQueue::iterator it = pending_requests->begin();
[email protected]ac790b42009-12-02 04:31:31156 while (it != pending_requests->end() && r->priority() >= (*it)->priority())
[email protected]ff579d42009-06-24 15:47:02157 ++it;
158 pending_requests->insert(it, r);
159}
160
[email protected]fd7b7c92009-08-20 19:38:30161// static
162const ClientSocketPoolBaseHelper::Request*
163ClientSocketPoolBaseHelper::RemoveRequestFromQueue(
164 RequestQueue::iterator it, RequestQueue* pending_requests) {
165 const Request* req = *it;
[email protected]fd7b7c92009-08-20 19:38:30166 pending_requests->erase(it);
167 return req;
168}
169
[email protected]d80a4322009-08-14 07:07:49170int ClientSocketPoolBaseHelper::RequestSocket(
[email protected]ff579d42009-06-24 15:47:02171 const std::string& group_name,
[email protected]d80a4322009-08-14 07:07:49172 const Request* request) {
[email protected]9e743cd2010-03-16 07:03:53173 request->net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]fd4fe0b2010-02-08 23:02:15174 Group& group = group_map_[group_name];
175 int rv = RequestSocketInternal(group_name, request);
176 if (rv != ERR_IO_PENDING)
[email protected]9e743cd2010-03-16 07:03:53177 request->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]fd4fe0b2010-02-08 23:02:15178 else
179 InsertRequestIntoQueue(request, &group.pending_requests);
180 return rv;
181}
182
183int ClientSocketPoolBaseHelper::RequestSocketInternal(
184 const std::string& group_name,
185 const Request* request) {
[email protected]d80a4322009-08-14 07:07:49186 DCHECK_GE(request->priority(), 0);
187 CompletionCallback* const callback = request->callback();
188 CHECK(callback);
189 ClientSocketHandle* const handle = request->handle();
190 CHECK(handle);
[email protected]ff579d42009-06-24 15:47:02191 Group& group = group_map_[group_name];
192
[email protected]42df4e8e2010-04-13 22:02:56193 // Can we make another active socket now?
194 if (ReachedMaxSocketsLimit() ||
195 !group.HasAvailableSocketSlot(max_sockets_per_group_)) {
196 if (ReachedMaxSocketsLimit()) {
197 // We could check if we really have a stalled group here, but it requires
198 // a scan of all groups, so just flip a flag here, and do the check later.
199 may_have_stalled_group_ = true;
200
201 request->net_log().AddEvent(NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS);
202 } else {
203 request->net_log().AddEvent(NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP);
204 }
205 return ERR_IO_PENDING;
206 }
207
[email protected]65552102010-04-09 22:58:10208 // Try to reuse a socket.
209 while (!group.idle_sockets.empty()) {
210 IdleSocket idle_socket = group.idle_sockets.back();
211 group.idle_sockets.pop_back();
212 DecrementIdleCount();
213 if (idle_socket.socket->IsConnectedAndIdle()) {
214 // We found one we can reuse!
215 base::TimeDelta idle_time =
216 base::TimeTicks::Now() - idle_socket.start_time;
217 HandOutSocket(
218 idle_socket.socket, idle_socket.used, handle, idle_time, &group,
219 request->net_log());
220 return OK;
221 }
222 delete idle_socket.socket;
223 }
224
[email protected]5edbf8d2010-01-13 18:44:11225 // See if we already have enough connect jobs or sockets that will be released
226 // soon.
[email protected]4d3b05d2010-01-27 21:27:29227 if (group.HasReleasingSockets()) {
[email protected]5edbf8d2010-01-13 18:44:11228 return ERR_IO_PENDING;
229 }
230
[email protected]ff579d42009-06-24 15:47:02231 // We couldn't find a socket to reuse, so allocate and connect a new one.
[email protected]9e743cd2010-03-16 07:03:53232 BoundNetLog job_net_log = BoundNetLog::Make(
233 request->net_log().net_log(), NetLog::SOURCE_CONNECT_JOB);
[email protected]fd7b7c92009-08-20 19:38:30234
[email protected]2ab05b52009-07-01 23:57:58235 scoped_ptr<ConnectJob> connect_job(
[email protected]fd7b7c92009-08-20 19:38:30236 connect_job_factory_->NewConnectJob(group_name, *request, this,
[email protected]9e743cd2010-03-16 07:03:53237 job_net_log));
[email protected]ff579d42009-06-24 15:47:02238
[email protected]2ab05b52009-07-01 23:57:58239 int rv = connect_job->Connect();
[email protected]fd7b7c92009-08-20 19:38:30240
[email protected]9e743cd2010-03-16 07:03:53241 if (rv != ERR_IO_PENDING) {
[email protected]6427fe22010-04-16 22:27:41242 request->net_log().AddEventWithInteger(
243 NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID,
244 job_net_log.source().id);
[email protected]9e743cd2010-03-16 07:03:53245 }
[email protected]fd7b7c92009-08-20 19:38:30246
[email protected]2ab05b52009-07-01 23:57:58247 if (rv == OK) {
248 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */,
[email protected]9e743cd2010-03-16 07:03:53249 handle, base::TimeDelta(), &group, request->net_log());
[email protected]2ab05b52009-07-01 23:57:58250 } else if (rv == ERR_IO_PENDING) {
[email protected]6b624c62010-03-14 08:37:32251 // If we don't have any sockets in this group, set a timer for potentially
252 // creating a new one. If the SYN is lost, this backup socket may complete
253 // before the slow socket, improving end user latency.
[email protected]7c28e9a2010-03-20 01:16:13254 if (group.IsEmpty() && !group.backup_job && backup_jobs_enabled_) {
[email protected]6b624c62010-03-14 08:37:32255 group.backup_job = connect_job_factory_->NewConnectJob(group_name,
256 *request,
257 this,
[email protected]9e743cd2010-03-16 07:03:53258 job_net_log);
[email protected]6b624c62010-03-14 08:37:32259 StartBackupSocketTimer(group_name);
260 }
261
[email protected]211d2172009-07-22 15:48:53262 connecting_socket_count_++;
263
[email protected]5fc08e32009-07-15 17:09:57264 ConnectJob* job = connect_job.release();
[email protected]5fc08e32009-07-15 17:09:57265 group.jobs.insert(job);
[email protected]2b7523d2009-07-29 20:29:23266 } else if (group.IsEmpty()) {
267 group_map_.erase(group_name);
[email protected]2ab05b52009-07-01 23:57:58268 }
[email protected]ff579d42009-06-24 15:47:02269
[email protected]2ab05b52009-07-01 23:57:58270 return rv;
[email protected]ff579d42009-06-24 15:47:02271}
272
[email protected]6b624c62010-03-14 08:37:32273void ClientSocketPoolBaseHelper::StartBackupSocketTimer(
274 const std::string& group_name) {
275 CHECK(ContainsKey(group_map_, group_name));
276 Group& group = group_map_[group_name];
277
278 // Only allow one timer pending to create a backup socket.
279 if (group.backup_task)
280 return;
281
282 group.backup_task = method_factory_.NewRunnableMethod(
283 &ClientSocketPoolBaseHelper::OnBackupSocketTimerFired, group_name);
284 MessageLoop::current()->PostDelayedTask(FROM_HERE, group.backup_task,
285 ConnectRetryIntervalMs());
286}
287
288void ClientSocketPoolBaseHelper::OnBackupSocketTimerFired(
289 const std::string& group_name) {
290 CHECK(ContainsKey(group_map_, group_name));
291
292 Group& group = group_map_[group_name];
293
294 CHECK(group.backup_task);
295 group.backup_task = NULL;
296
297 CHECK(group.backup_job);
298
299 // If our backup job is waiting on DNS, just reset the timer.
300 CHECK(group.jobs.size());
301 if ((*group.jobs.begin())->GetLoadState() == LOAD_STATE_RESOLVING_HOST) {
[email protected]9e743cd2010-03-16 07:03:53302 group.backup_job->net_log().EndEvent(
303 NetLog::TYPE_SOCKET_BACKUP_TIMER_EXTENDED);
[email protected]6b624c62010-03-14 08:37:32304 StartBackupSocketTimer(group_name);
305 return;
306 }
307
[email protected]9e743cd2010-03-16 07:03:53308 group.backup_job->net_log().AddEvent(NetLog::TYPE_SOCKET_BACKUP_CREATED);
[email protected]6b624c62010-03-14 08:37:32309 SIMPLE_STATS_COUNTER("socket.backup_created");
310 int rv = group.backup_job->Connect();
[email protected]c83658c2010-03-24 08:19:34311 connecting_socket_count_++;
312 group.jobs.insert(group.backup_job);
313 ConnectJob* job = group.backup_job;
314 group.backup_job = NULL;
315 if (rv != ERR_IO_PENDING)
316 OnConnectJobComplete(rv, job);
[email protected]6b624c62010-03-14 08:37:32317}
318
[email protected]d80a4322009-08-14 07:07:49319void ClientSocketPoolBaseHelper::CancelRequest(
320 const std::string& group_name, const ClientSocketHandle* handle) {
[email protected]ff579d42009-06-24 15:47:02321 CHECK(ContainsKey(group_map_, group_name));
322
323 Group& group = group_map_[group_name];
324
[email protected]ff579d42009-06-24 15:47:02325 // Search pending_requests for matching handle.
326 RequestQueue::iterator it = group.pending_requests.begin();
327 for (; it != group.pending_requests.end(); ++it) {
[email protected]d80a4322009-08-14 07:07:49328 if ((*it)->handle() == handle) {
[email protected]fd7b7c92009-08-20 19:38:30329 const Request* req = RemoveRequestFromQueue(it, &group.pending_requests);
[email protected]9e743cd2010-03-16 07:03:53330 req->net_log().AddEvent(NetLog::TYPE_CANCELLED);
331 req->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]fd7b7c92009-08-20 19:38:30332 delete req;
[email protected]a796bcec2010-03-22 17:17:26333 // Let one connect job connect and become idle for potential future use.
[email protected]4d3b05d2010-01-27 21:27:29334 if (group.jobs.size() > group.pending_requests.size() + 1) {
[email protected]974ebd62009-08-03 23:14:34335 // TODO(willchan): Cancel the job in the earliest LoadState.
[email protected]4d3b05d2010-01-27 21:27:29336 RemoveConnectJob(*group.jobs.begin(), &group);
[email protected]974ebd62009-08-03 23:14:34337 OnAvailableSocketSlot(group_name, &group);
338 }
[email protected]ff579d42009-06-24 15:47:02339 return;
340 }
341 }
[email protected]ff579d42009-06-24 15:47:02342}
343
[email protected]d80a4322009-08-14 07:07:49344void ClientSocketPoolBaseHelper::ReleaseSocket(const std::string& group_name,
345 ClientSocket* socket) {
[email protected]5edbf8d2010-01-13 18:44:11346 Group& group = group_map_[group_name];
347 group.num_releasing_sockets++;
348 DCHECK_LE(group.num_releasing_sockets, group.active_socket_count);
[email protected]ff579d42009-06-24 15:47:02349 // Run this asynchronously to allow the caller to finish before we let
350 // another to begin doing work. This also avoids nasty recursion issues.
351 // NOTE: We cannot refer to the handle argument after this method returns.
352 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]d80a4322009-08-14 07:07:49353 this, &ClientSocketPoolBaseHelper::DoReleaseSocket, group_name, socket));
[email protected]ff579d42009-06-24 15:47:02354}
355
[email protected]d80a4322009-08-14 07:07:49356void ClientSocketPoolBaseHelper::CloseIdleSockets() {
[email protected]ff579d42009-06-24 15:47:02357 CleanupIdleSockets(true);
358}
359
[email protected]d80a4322009-08-14 07:07:49360int ClientSocketPoolBaseHelper::IdleSocketCountInGroup(
[email protected]ff579d42009-06-24 15:47:02361 const std::string& group_name) const {
362 GroupMap::const_iterator i = group_map_.find(group_name);
363 CHECK(i != group_map_.end());
364
365 return i->second.idle_sockets.size();
366}
367
[email protected]d80a4322009-08-14 07:07:49368LoadState ClientSocketPoolBaseHelper::GetLoadState(
[email protected]ff579d42009-06-24 15:47:02369 const std::string& group_name,
370 const ClientSocketHandle* handle) const {
371 if (!ContainsKey(group_map_, group_name)) {
372 NOTREACHED() << "ClientSocketPool does not contain group: " << group_name
373 << " for handle: " << handle;
374 return LOAD_STATE_IDLE;
375 }
376
377 // Can't use operator[] since it is non-const.
378 const Group& group = group_map_.find(group_name)->second;
379
[email protected]ff579d42009-06-24 15:47:02380 // Search pending_requests for matching handle.
381 RequestQueue::const_iterator it = group.pending_requests.begin();
[email protected]5fc08e32009-07-15 17:09:57382 for (size_t i = 0; it != group.pending_requests.end(); ++it, ++i) {
[email protected]d80a4322009-08-14 07:07:49383 if ((*it)->handle() == handle) {
[email protected]4d3b05d2010-01-27 21:27:29384 if (i < group.jobs.size()) {
[email protected]5fc08e32009-07-15 17:09:57385 LoadState max_state = LOAD_STATE_IDLE;
386 for (ConnectJobSet::const_iterator job_it = group.jobs.begin();
387 job_it != group.jobs.end(); ++job_it) {
[email protected]46451352009-09-01 14:54:21388 max_state = std::max(max_state, (*job_it)->GetLoadState());
[email protected]5fc08e32009-07-15 17:09:57389 }
390 return max_state;
391 } else {
392 // TODO(wtc): Add a state for being on the wait list.
393 // See http://www.crbug.com/5077.
394 return LOAD_STATE_IDLE;
395 }
[email protected]ff579d42009-06-24 15:47:02396 }
397 }
398
399 NOTREACHED();
400 return LOAD_STATE_IDLE;
401}
402
[email protected]d80a4322009-08-14 07:07:49403bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup(
[email protected]9bf28db2009-08-29 01:35:16404 base::TimeTicks now,
405 base::TimeDelta timeout) const {
406 bool timed_out = (now - start_time) >= timeout;
[email protected]5fc08e32009-07-15 17:09:57407 return timed_out ||
408 !(used ? socket->IsConnectedAndIdle() : socket->IsConnected());
[email protected]ff579d42009-06-24 15:47:02409}
410
[email protected]d80a4322009-08-14 07:07:49411void ClientSocketPoolBaseHelper::CleanupIdleSockets(bool force) {
[email protected]ff579d42009-06-24 15:47:02412 if (idle_socket_count_ == 0)
413 return;
414
415 // Current time value. Retrieving it once at the function start rather than
416 // inside the inner loop, since it shouldn't change by any meaningful amount.
417 base::TimeTicks now = base::TimeTicks::Now();
418
419 GroupMap::iterator i = group_map_.begin();
420 while (i != group_map_.end()) {
421 Group& group = i->second;
422
423 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
424 while (j != group.idle_sockets.end()) {
[email protected]9bf28db2009-08-29 01:35:16425 base::TimeDelta timeout =
426 j->used ? used_idle_socket_timeout_ : unused_idle_socket_timeout_;
427 if (force || j->ShouldCleanup(now, timeout)) {
[email protected]ff579d42009-06-24 15:47:02428 delete j->socket;
429 j = group.idle_sockets.erase(j);
430 DecrementIdleCount();
431 } else {
432 ++j;
433 }
434 }
435
436 // Delete group if no longer needed.
[email protected]2ab05b52009-07-01 23:57:58437 if (group.IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02438 group_map_.erase(i++);
439 } else {
440 ++i;
441 }
442 }
443}
444
[email protected]d80a4322009-08-14 07:07:49445void ClientSocketPoolBaseHelper::IncrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02446 if (++idle_socket_count_ == 1)
447 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this,
[email protected]d80a4322009-08-14 07:07:49448 &ClientSocketPoolBaseHelper::OnCleanupTimerFired);
[email protected]ff579d42009-06-24 15:47:02449}
450
[email protected]d80a4322009-08-14 07:07:49451void ClientSocketPoolBaseHelper::DecrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02452 if (--idle_socket_count_ == 0)
453 timer_.Stop();
454}
455
[email protected]d80a4322009-08-14 07:07:49456void ClientSocketPoolBaseHelper::DoReleaseSocket(const std::string& group_name,
457 ClientSocket* socket) {
[email protected]ff579d42009-06-24 15:47:02458 GroupMap::iterator i = group_map_.find(group_name);
459 CHECK(i != group_map_.end());
460
461 Group& group = i->second;
462
[email protected]5edbf8d2010-01-13 18:44:11463 group.num_releasing_sockets--;
464 DCHECK_GE(group.num_releasing_sockets, 0);
465
[email protected]b1f031dd2010-03-02 23:19:33466 CHECK_GT(handed_out_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53467 handed_out_socket_count_--;
468
[email protected]b1f031dd2010-03-02 23:19:33469 CHECK_GT(group.active_socket_count, 0);
[email protected]2ab05b52009-07-01 23:57:58470 group.active_socket_count--;
[email protected]ff579d42009-06-24 15:47:02471
472 const bool can_reuse = socket->IsConnectedAndIdle();
473 if (can_reuse) {
[email protected]5fc08e32009-07-15 17:09:57474 AddIdleSocket(socket, true /* used socket */, &group);
[email protected]ff579d42009-06-24 15:47:02475 } else {
476 delete socket;
477 }
478
[email protected]2ab05b52009-07-01 23:57:58479 OnAvailableSocketSlot(group_name, &group);
[email protected]4f2abec2010-02-03 18:10:16480
481 // If there are no more releasing sockets, then we might have to process
482 // multiple available socket slots, since we stalled their processing until
483 // all sockets have been released.
[email protected]4f1e4982010-03-02 18:31:04484 i = group_map_.find(group_name);
485 if (i == group_map_.end() || i->second.num_releasing_sockets > 0)
[email protected]4f2abec2010-02-03 18:10:16486 return;
487
488 while (true) {
489 // We can't activate more sockets since we're already at our global limit.
490 if (ReachedMaxSocketsLimit())
491 return;
[email protected]616925a2010-03-02 19:02:38492
[email protected]4f2abec2010-02-03 18:10:16493 // |group| might now be deleted.
494 i = group_map_.find(group_name);
495 if (i == group_map_.end())
496 return;
497
498 group = i->second;
[email protected]616925a2010-03-02 19:02:38499
[email protected]4f2abec2010-02-03 18:10:16500 // If we already have enough ConnectJobs to satisfy the pending requests,
501 // don't bother starting up more.
502 if (group.pending_requests.size() <= group.jobs.size())
503 return;
504
505 if (!group.HasAvailableSocketSlot(max_sockets_per_group_))
506 return;
507
508 OnAvailableSocketSlot(group_name, &group);
509 }
[email protected]ff579d42009-06-24 15:47:02510}
511
[email protected]211d2172009-07-22 15:48:53512// Search for the highest priority pending request, amongst the groups that
513// are not at the |max_sockets_per_group_| limit. Note: for requests with
514// the same priority, the winner is based on group hash ordering (and not
515// insertion order).
[email protected]d80a4322009-08-14 07:07:49516int ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group,
517 std::string* group_name) {
[email protected]211d2172009-07-22 15:48:53518 Group* top_group = NULL;
519 const std::string* top_group_name = NULL;
520 int stalled_group_count = 0;
521 for (GroupMap::iterator i = group_map_.begin();
522 i != group_map_.end(); ++i) {
523 Group& group = i->second;
524 const RequestQueue& queue = group.pending_requests;
525 if (queue.empty())
526 continue;
[email protected]6427fe22010-04-16 22:27:41527 bool has_unused_slot =
528 group.HasAvailableSocketSlot(max_sockets_per_group_) &&
529 group.pending_requests.size() > group.jobs.size();
530 if (has_unused_slot) {
[email protected]211d2172009-07-22 15:48:53531 stalled_group_count++;
[email protected]6427fe22010-04-16 22:27:41532 bool has_higher_priority = !top_group ||
533 group.TopPendingPriority() < top_group->TopPendingPriority();
534 if (has_higher_priority) {
535 top_group = &group;
536 top_group_name = &i->first;
537 }
[email protected]211d2172009-07-22 15:48:53538 }
539 }
540 if (top_group) {
541 *group = top_group;
542 *group_name = *top_group_name;
543 }
544 return stalled_group_count;
545}
546
[email protected]d80a4322009-08-14 07:07:49547void ClientSocketPoolBaseHelper::OnConnectJobComplete(
548 int result, ConnectJob* job) {
[email protected]2ab05b52009-07-01 23:57:58549 DCHECK_NE(ERR_IO_PENDING, result);
550 const std::string group_name = job->group_name();
[email protected]ff579d42009-06-24 15:47:02551 GroupMap::iterator group_it = group_map_.find(group_name);
552 CHECK(group_it != group_map_.end());
553 Group& group = group_it->second;
554
[email protected]6b624c62010-03-14 08:37:32555 // We've had a connect on the socket; discard any pending backup job
556 // for this group and kill the pending task.
557 group.CleanupBackupJob();
558
[email protected]5fc08e32009-07-15 17:09:57559 scoped_ptr<ClientSocket> socket(job->ReleaseSocket());
[email protected]ff579d42009-06-24 15:47:02560
[email protected]9e743cd2010-03-16 07:03:53561 BoundNetLog job_log = job->net_log();
[email protected]4d3b05d2010-01-27 21:27:29562 RemoveConnectJob(job, &group);
[email protected]5fc08e32009-07-15 17:09:57563
[email protected]4d3b05d2010-01-27 21:27:29564 if (result == OK) {
565 DCHECK(socket.get());
[email protected]fd7b7c92009-08-20 19:38:30566 if (!group.pending_requests.empty()) {
[email protected]4d3b05d2010-01-27 21:27:29567 scoped_ptr<const Request> r(RemoveRequestFromQueue(
[email protected]fd7b7c92009-08-20 19:38:30568 group.pending_requests.begin(), &group.pending_requests));
[email protected]9e743cd2010-03-16 07:03:53569 r->net_log().AddEventWithInteger(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID,
570 job_log.source().id);
571 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]4d3b05d2010-01-27 21:27:29572 HandOutSocket(
573 socket.release(), false /* unused socket */, r->handle(),
[email protected]9e743cd2010-03-16 07:03:53574 base::TimeDelta(), &group, r->net_log());
[email protected]4d3b05d2010-01-27 21:27:29575 r->callback()->Run(result);
[email protected]5fc08e32009-07-15 17:09:57576 } else {
[email protected]4d3b05d2010-01-27 21:27:29577 AddIdleSocket(socket.release(), false /* unused socket */, &group);
578 OnAvailableSocketSlot(group_name, &group);
[email protected]5fc08e32009-07-15 17:09:57579 }
[email protected]94c20472010-01-14 08:14:36580 } else {
[email protected]4d3b05d2010-01-27 21:27:29581 DCHECK(!socket.get());
582 if (!group.pending_requests.empty()) {
583 scoped_ptr<const Request> r(RemoveRequestFromQueue(
584 group.pending_requests.begin(), &group.pending_requests));
[email protected]9e743cd2010-03-16 07:03:53585 r->net_log().AddEventWithInteger(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID,
586 job_log.source().id);
587 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]4d3b05d2010-01-27 21:27:29588 r->callback()->Run(result);
589 }
590 MaybeOnAvailableSocketSlot(group_name);
[email protected]ff579d42009-06-24 15:47:02591 }
[email protected]ff579d42009-06-24 15:47:02592}
593
[email protected]61a86c42010-04-19 22:45:53594void ClientSocketPoolBaseHelper::OnIPAddressChanged() {
595 CloseIdleSockets();
596}
597
[email protected]4d3b05d2010-01-27 21:27:29598void ClientSocketPoolBaseHelper::RemoveConnectJob(const ConnectJob *job,
599 Group* group) {
[email protected]b1f031dd2010-03-02 23:19:33600 CHECK_GT(connecting_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53601 connecting_socket_count_--;
602
[email protected]4d3b05d2010-01-27 21:27:29603 DCHECK(job);
604 delete job;
[email protected]5fc08e32009-07-15 17:09:57605
606 if (group) {
607 DCHECK(ContainsKey(group->jobs, job));
608 group->jobs.erase(job);
609 }
[email protected]ff579d42009-06-24 15:47:02610}
611
[email protected]d80a4322009-08-14 07:07:49612void ClientSocketPoolBaseHelper::MaybeOnAvailableSocketSlot(
[email protected]2ab05b52009-07-01 23:57:58613 const std::string& group_name) {
614 GroupMap::iterator it = group_map_.find(group_name);
615 if (it != group_map_.end()) {
616 Group& group = it->second;
617 if (group.HasAvailableSocketSlot(max_sockets_per_group_))
618 OnAvailableSocketSlot(group_name, &group);
619 }
620}
[email protected]ff579d42009-06-24 15:47:02621
[email protected]d80a4322009-08-14 07:07:49622void ClientSocketPoolBaseHelper::OnAvailableSocketSlot(
623 const std::string& group_name, Group* group) {
[email protected]211d2172009-07-22 15:48:53624 if (may_have_stalled_group_) {
625 std::string top_group_name;
[email protected]bed37d442009-08-20 19:58:20626 Group* top_group = NULL;
[email protected]211d2172009-07-22 15:48:53627 int stalled_group_count = FindTopStalledGroup(&top_group, &top_group_name);
628 if (stalled_group_count <= 1)
629 may_have_stalled_group_ = false;
[email protected]42df4e8e2010-04-13 22:02:56630 if (stalled_group_count >= 1)
[email protected]211d2172009-07-22 15:48:53631 ProcessPendingRequest(top_group_name, top_group);
632 } else if (!group->pending_requests.empty()) {
[email protected]ff579d42009-06-24 15:47:02633 ProcessPendingRequest(group_name, group);
634 // |group| may no longer be valid after this point. Be careful not to
635 // access it again.
[email protected]2ab05b52009-07-01 23:57:58636 } else if (group->IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02637 // Delete |group| if no longer needed. |group| will no longer be valid.
[email protected]ff579d42009-06-24 15:47:02638 group_map_.erase(group_name);
[email protected]ff579d42009-06-24 15:47:02639 }
640}
641
[email protected]d80a4322009-08-14 07:07:49642void ClientSocketPoolBaseHelper::ProcessPendingRequest(
643 const std::string& group_name, Group* group) {
[email protected]fd4fe0b2010-02-08 23:02:15644 scoped_ptr<const Request> r(*group->pending_requests.begin());
645 int rv = RequestSocketInternal(group_name, r.get());
[email protected]ff579d42009-06-24 15:47:02646
[email protected]2ab05b52009-07-01 23:57:58647 if (rv != ERR_IO_PENDING) {
[email protected]9e743cd2010-03-16 07:03:53648 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]fd4fe0b2010-02-08 23:02:15649 RemoveRequestFromQueue(group->pending_requests.begin(),
650 &group->pending_requests);
[email protected]d80a4322009-08-14 07:07:49651 r->callback()->Run(rv);
[email protected]2ab05b52009-07-01 23:57:58652 if (rv != OK) {
653 // |group| may be invalid after the callback, we need to search
654 // |group_map_| again.
655 MaybeOnAvailableSocketSlot(group_name);
656 }
[email protected]d80a4322009-08-14 07:07:49657 } else {
658 r.release();
[email protected]2ab05b52009-07-01 23:57:58659 }
660}
661
[email protected]d80a4322009-08-14 07:07:49662void ClientSocketPoolBaseHelper::HandOutSocket(
[email protected]2ab05b52009-07-01 23:57:58663 ClientSocket* socket,
664 bool reused,
665 ClientSocketHandle* handle,
[email protected]f9d285c2009-08-17 19:54:29666 base::TimeDelta idle_time,
[email protected]fd4fe0b2010-02-08 23:02:15667 Group* group,
[email protected]9e743cd2010-03-16 07:03:53668 const BoundNetLog& net_log) {
[email protected]2ab05b52009-07-01 23:57:58669 DCHECK(socket);
670 handle->set_socket(socket);
671 handle->set_is_reused(reused);
[email protected]f9d285c2009-08-17 19:54:29672 handle->set_idle_time(idle_time);
[email protected]211d2172009-07-22 15:48:53673
[email protected]fd4fe0b2010-02-08 23:02:15674 if (reused)
[email protected]9e743cd2010-03-16 07:03:53675 net_log.AddStringLiteral("Reusing socket.");
[email protected]fd4fe0b2010-02-08 23:02:15676 if (idle_time != base::TimeDelta()) {
[email protected]9e743cd2010-03-16 07:03:53677 net_log.AddString(
[email protected]fd4fe0b2010-02-08 23:02:15678 StringPrintf("Socket sat idle for %" PRId64 " milliseconds",
679 idle_time.InMilliseconds()));
680 }
681
[email protected]211d2172009-07-22 15:48:53682 handed_out_socket_count_++;
[email protected]2ab05b52009-07-01 23:57:58683 group->active_socket_count++;
[email protected]ff579d42009-06-24 15:47:02684}
685
[email protected]d80a4322009-08-14 07:07:49686void ClientSocketPoolBaseHelper::AddIdleSocket(
[email protected]5fc08e32009-07-15 17:09:57687 ClientSocket* socket, bool used, Group* group) {
688 DCHECK(socket);
689 IdleSocket idle_socket;
690 idle_socket.socket = socket;
691 idle_socket.start_time = base::TimeTicks::Now();
692 idle_socket.used = used;
693
694 group->idle_sockets.push_back(idle_socket);
695 IncrementIdleCount();
696}
697
[email protected]d80a4322009-08-14 07:07:49698void ClientSocketPoolBaseHelper::CancelAllConnectJobs() {
[email protected]5fc08e32009-07-15 17:09:57699 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end();) {
700 Group& group = i->second;
[email protected]4d3b05d2010-01-27 21:27:29701 connecting_socket_count_ -= group.jobs.size();
[email protected]5fc08e32009-07-15 17:09:57702 STLDeleteElements(&group.jobs);
703
[email protected]6b624c62010-03-14 08:37:32704 if (group.backup_task) {
705 group.backup_task->Cancel();
706 group.backup_task = NULL;
707 }
708
[email protected]5fc08e32009-07-15 17:09:57709 // Delete group if no longer needed.
710 if (group.IsEmpty()) {
[email protected]5fc08e32009-07-15 17:09:57711 group_map_.erase(i++);
712 } else {
713 ++i;
714 }
715 }
716}
717
[email protected]d80a4322009-08-14 07:07:49718bool ClientSocketPoolBaseHelper::ReachedMaxSocketsLimit() const {
[email protected]211d2172009-07-22 15:48:53719 // Each connecting socket will eventually connect and be handed out.
[email protected]42df4e8e2010-04-13 22:02:56720 int total = handed_out_socket_count_ + connecting_socket_count_;
[email protected]211d2172009-07-22 15:48:53721 DCHECK_LE(total, max_sockets_);
722 return total == max_sockets_;
723}
724
[email protected]d80a4322009-08-14 07:07:49725} // namespace internal
726
[email protected]ff579d42009-06-24 15:47:02727} // namespace net