blob: af87eba836eacfe093ba2171a89646c07bab35f8 [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,
61 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 {
205 request->net_log().AddEvent(NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP);
206 }
207 return ERR_IO_PENDING;
208 }
209
[email protected]65552102010-04-09 22:58:10210 // Try to reuse a socket.
211 while (!group.idle_sockets.empty()) {
212 IdleSocket idle_socket = group.idle_sockets.back();
213 group.idle_sockets.pop_back();
214 DecrementIdleCount();
215 if (idle_socket.socket->IsConnectedAndIdle()) {
216 // We found one we can reuse!
217 base::TimeDelta idle_time =
218 base::TimeTicks::Now() - idle_socket.start_time;
219 HandOutSocket(
220 idle_socket.socket, idle_socket.used, handle, idle_time, &group,
221 request->net_log());
222 return OK;
223 }
224 delete idle_socket.socket;
225 }
226
[email protected]5edbf8d2010-01-13 18:44:11227 // See if we already have enough connect jobs or sockets that will be released
228 // soon.
[email protected]4d3b05d2010-01-27 21:27:29229 if (group.HasReleasingSockets()) {
[email protected]5edbf8d2010-01-13 18:44:11230 return ERR_IO_PENDING;
231 }
232
[email protected]ff579d42009-06-24 15:47:02233 // We couldn't find a socket to reuse, so allocate and connect a new one.
[email protected]9e743cd2010-03-16 07:03:53234 BoundNetLog job_net_log = BoundNetLog::Make(
235 request->net_log().net_log(), NetLog::SOURCE_CONNECT_JOB);
[email protected]a2006ece2010-04-23 16:44:02236 request->net_log().BeginEventWithInteger(
237 NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID, job_net_log.source().id);
[email protected]fd7b7c92009-08-20 19:38:30238
[email protected]2ab05b52009-07-01 23:57:58239 scoped_ptr<ConnectJob> connect_job(
[email protected]fd7b7c92009-08-20 19:38:30240 connect_job_factory_->NewConnectJob(group_name, *request, this,
[email protected]9e743cd2010-03-16 07:03:53241 job_net_log));
[email protected]ff579d42009-06-24 15:47:02242
[email protected]2ab05b52009-07-01 23:57:58243 int rv = connect_job->Connect();
[email protected]2ab05b52009-07-01 23:57:58244 if (rv == OK) {
[email protected]a2006ece2010-04-23 16:44:02245 request->net_log().EndEventWithInteger(
246 NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID, job_net_log.source().id);
[email protected]2ab05b52009-07-01 23:57:58247 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */,
[email protected]9e743cd2010-03-16 07:03:53248 handle, base::TimeDelta(), &group, request->net_log());
[email protected]2ab05b52009-07-01 23:57:58249 } else if (rv == ERR_IO_PENDING) {
[email protected]6b624c62010-03-14 08:37:32250 // If we don't have any sockets in this group, set a timer for potentially
251 // creating a new one. If the SYN is lost, this backup socket may complete
252 // before the slow socket, improving end user latency.
[email protected]7c28e9a2010-03-20 01:16:13253 if (group.IsEmpty() && !group.backup_job && backup_jobs_enabled_) {
[email protected]6b624c62010-03-14 08:37:32254 group.backup_job = connect_job_factory_->NewConnectJob(group_name,
255 *request,
256 this,
[email protected]9e743cd2010-03-16 07:03:53257 job_net_log);
[email protected]6b624c62010-03-14 08:37:32258 StartBackupSocketTimer(group_name);
259 }
260
[email protected]211d2172009-07-22 15:48:53261 connecting_socket_count_++;
262
[email protected]5fc08e32009-07-15 17:09:57263 ConnectJob* job = connect_job.release();
[email protected]5fc08e32009-07-15 17:09:57264 group.jobs.insert(job);
[email protected]a2006ece2010-04-23 16:44:02265 } else {
266 request->net_log().EndEventWithInteger(
267 NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID, job_net_log.source().id);
268 if (group.IsEmpty())
269 group_map_.erase(group_name);
[email protected]2ab05b52009-07-01 23:57:58270 }
[email protected]ff579d42009-06-24 15:47:02271
[email protected]2ab05b52009-07-01 23:57:58272 return rv;
[email protected]ff579d42009-06-24 15:47:02273}
274
[email protected]6b624c62010-03-14 08:37:32275void ClientSocketPoolBaseHelper::StartBackupSocketTimer(
276 const std::string& group_name) {
277 CHECK(ContainsKey(group_map_, group_name));
278 Group& group = group_map_[group_name];
279
280 // Only allow one timer pending to create a backup socket.
281 if (group.backup_task)
282 return;
283
284 group.backup_task = method_factory_.NewRunnableMethod(
285 &ClientSocketPoolBaseHelper::OnBackupSocketTimerFired, group_name);
286 MessageLoop::current()->PostDelayedTask(FROM_HERE, group.backup_task,
287 ConnectRetryIntervalMs());
288}
289
290void ClientSocketPoolBaseHelper::OnBackupSocketTimerFired(
291 const std::string& group_name) {
292 CHECK(ContainsKey(group_map_, group_name));
293
294 Group& group = group_map_[group_name];
295
296 CHECK(group.backup_task);
297 group.backup_task = NULL;
298
299 CHECK(group.backup_job);
300
301 // If our backup job is waiting on DNS, just reset the timer.
302 CHECK(group.jobs.size());
303 if ((*group.jobs.begin())->GetLoadState() == LOAD_STATE_RESOLVING_HOST) {
[email protected]9e743cd2010-03-16 07:03:53304 group.backup_job->net_log().EndEvent(
305 NetLog::TYPE_SOCKET_BACKUP_TIMER_EXTENDED);
[email protected]6b624c62010-03-14 08:37:32306 StartBackupSocketTimer(group_name);
307 return;
308 }
309
[email protected]9e743cd2010-03-16 07:03:53310 group.backup_job->net_log().AddEvent(NetLog::TYPE_SOCKET_BACKUP_CREATED);
[email protected]6b624c62010-03-14 08:37:32311 SIMPLE_STATS_COUNTER("socket.backup_created");
312 int rv = group.backup_job->Connect();
[email protected]c83658c2010-03-24 08:19:34313 connecting_socket_count_++;
314 group.jobs.insert(group.backup_job);
315 ConnectJob* job = group.backup_job;
316 group.backup_job = NULL;
317 if (rv != ERR_IO_PENDING)
318 OnConnectJobComplete(rv, job);
[email protected]6b624c62010-03-14 08:37:32319}
320
[email protected]d80a4322009-08-14 07:07:49321void ClientSocketPoolBaseHelper::CancelRequest(
322 const std::string& group_name, const ClientSocketHandle* handle) {
[email protected]ff579d42009-06-24 15:47:02323 CHECK(ContainsKey(group_map_, group_name));
324
325 Group& group = group_map_[group_name];
326
[email protected]ff579d42009-06-24 15:47:02327 // Search pending_requests for matching handle.
328 RequestQueue::iterator it = group.pending_requests.begin();
329 for (; it != group.pending_requests.end(); ++it) {
[email protected]d80a4322009-08-14 07:07:49330 if ((*it)->handle() == handle) {
[email protected]fd7b7c92009-08-20 19:38:30331 const Request* req = RemoveRequestFromQueue(it, &group.pending_requests);
[email protected]9e743cd2010-03-16 07:03:53332 req->net_log().AddEvent(NetLog::TYPE_CANCELLED);
333 req->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]fd7b7c92009-08-20 19:38:30334 delete req;
[email protected]a796bcec2010-03-22 17:17:26335 // Let one connect job connect and become idle for potential future use.
[email protected]4d3b05d2010-01-27 21:27:29336 if (group.jobs.size() > group.pending_requests.size() + 1) {
[email protected]974ebd62009-08-03 23:14:34337 // TODO(willchan): Cancel the job in the earliest LoadState.
[email protected]4d3b05d2010-01-27 21:27:29338 RemoveConnectJob(*group.jobs.begin(), &group);
[email protected]974ebd62009-08-03 23:14:34339 OnAvailableSocketSlot(group_name, &group);
340 }
[email protected]ff579d42009-06-24 15:47:02341 return;
342 }
343 }
[email protected]ff579d42009-06-24 15:47:02344}
345
[email protected]d80a4322009-08-14 07:07:49346void ClientSocketPoolBaseHelper::ReleaseSocket(const std::string& group_name,
347 ClientSocket* socket) {
[email protected]5edbf8d2010-01-13 18:44:11348 Group& group = group_map_[group_name];
349 group.num_releasing_sockets++;
350 DCHECK_LE(group.num_releasing_sockets, group.active_socket_count);
[email protected]ff579d42009-06-24 15:47:02351 // Run this asynchronously to allow the caller to finish before we let
352 // another to begin doing work. This also avoids nasty recursion issues.
353 // NOTE: We cannot refer to the handle argument after this method returns.
354 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]d80a4322009-08-14 07:07:49355 this, &ClientSocketPoolBaseHelper::DoReleaseSocket, group_name, socket));
[email protected]ff579d42009-06-24 15:47:02356}
357
[email protected]d80a4322009-08-14 07:07:49358void ClientSocketPoolBaseHelper::CloseIdleSockets() {
[email protected]ff579d42009-06-24 15:47:02359 CleanupIdleSockets(true);
360}
361
[email protected]d80a4322009-08-14 07:07:49362int ClientSocketPoolBaseHelper::IdleSocketCountInGroup(
[email protected]ff579d42009-06-24 15:47:02363 const std::string& group_name) const {
364 GroupMap::const_iterator i = group_map_.find(group_name);
365 CHECK(i != group_map_.end());
366
367 return i->second.idle_sockets.size();
368}
369
[email protected]d80a4322009-08-14 07:07:49370LoadState ClientSocketPoolBaseHelper::GetLoadState(
[email protected]ff579d42009-06-24 15:47:02371 const std::string& group_name,
372 const ClientSocketHandle* handle) const {
373 if (!ContainsKey(group_map_, group_name)) {
374 NOTREACHED() << "ClientSocketPool does not contain group: " << group_name
375 << " for handle: " << handle;
376 return LOAD_STATE_IDLE;
377 }
378
379 // Can't use operator[] since it is non-const.
380 const Group& group = group_map_.find(group_name)->second;
381
[email protected]ff579d42009-06-24 15:47:02382 // Search pending_requests for matching handle.
383 RequestQueue::const_iterator it = group.pending_requests.begin();
[email protected]5fc08e32009-07-15 17:09:57384 for (size_t i = 0; it != group.pending_requests.end(); ++it, ++i) {
[email protected]d80a4322009-08-14 07:07:49385 if ((*it)->handle() == handle) {
[email protected]4d3b05d2010-01-27 21:27:29386 if (i < group.jobs.size()) {
[email protected]5fc08e32009-07-15 17:09:57387 LoadState max_state = LOAD_STATE_IDLE;
388 for (ConnectJobSet::const_iterator job_it = group.jobs.begin();
389 job_it != group.jobs.end(); ++job_it) {
[email protected]46451352009-09-01 14:54:21390 max_state = std::max(max_state, (*job_it)->GetLoadState());
[email protected]5fc08e32009-07-15 17:09:57391 }
392 return max_state;
393 } else {
394 // TODO(wtc): Add a state for being on the wait list.
395 // See http://www.crbug.com/5077.
396 return LOAD_STATE_IDLE;
397 }
[email protected]ff579d42009-06-24 15:47:02398 }
399 }
400
401 NOTREACHED();
402 return LOAD_STATE_IDLE;
403}
404
[email protected]d80a4322009-08-14 07:07:49405bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup(
[email protected]9bf28db2009-08-29 01:35:16406 base::TimeTicks now,
407 base::TimeDelta timeout) const {
408 bool timed_out = (now - start_time) >= timeout;
[email protected]5fc08e32009-07-15 17:09:57409 return timed_out ||
410 !(used ? socket->IsConnectedAndIdle() : socket->IsConnected());
[email protected]ff579d42009-06-24 15:47:02411}
412
[email protected]d80a4322009-08-14 07:07:49413void ClientSocketPoolBaseHelper::CleanupIdleSockets(bool force) {
[email protected]ff579d42009-06-24 15:47:02414 if (idle_socket_count_ == 0)
415 return;
416
417 // Current time value. Retrieving it once at the function start rather than
418 // inside the inner loop, since it shouldn't change by any meaningful amount.
419 base::TimeTicks now = base::TimeTicks::Now();
420
421 GroupMap::iterator i = group_map_.begin();
422 while (i != group_map_.end()) {
423 Group& group = i->second;
424
425 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
426 while (j != group.idle_sockets.end()) {
[email protected]9bf28db2009-08-29 01:35:16427 base::TimeDelta timeout =
428 j->used ? used_idle_socket_timeout_ : unused_idle_socket_timeout_;
429 if (force || j->ShouldCleanup(now, timeout)) {
[email protected]ff579d42009-06-24 15:47:02430 delete j->socket;
431 j = group.idle_sockets.erase(j);
432 DecrementIdleCount();
433 } else {
434 ++j;
435 }
436 }
437
438 // Delete group if no longer needed.
[email protected]2ab05b52009-07-01 23:57:58439 if (group.IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02440 group_map_.erase(i++);
441 } else {
442 ++i;
443 }
444 }
445}
446
[email protected]d80a4322009-08-14 07:07:49447void ClientSocketPoolBaseHelper::IncrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02448 if (++idle_socket_count_ == 1)
449 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this,
[email protected]d80a4322009-08-14 07:07:49450 &ClientSocketPoolBaseHelper::OnCleanupTimerFired);
[email protected]ff579d42009-06-24 15:47:02451}
452
[email protected]d80a4322009-08-14 07:07:49453void ClientSocketPoolBaseHelper::DecrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02454 if (--idle_socket_count_ == 0)
455 timer_.Stop();
456}
457
[email protected]d80a4322009-08-14 07:07:49458void ClientSocketPoolBaseHelper::DoReleaseSocket(const std::string& group_name,
459 ClientSocket* socket) {
[email protected]ff579d42009-06-24 15:47:02460 GroupMap::iterator i = group_map_.find(group_name);
461 CHECK(i != group_map_.end());
462
463 Group& group = i->second;
464
[email protected]5edbf8d2010-01-13 18:44:11465 group.num_releasing_sockets--;
466 DCHECK_GE(group.num_releasing_sockets, 0);
467
[email protected]b1f031dd2010-03-02 23:19:33468 CHECK_GT(handed_out_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53469 handed_out_socket_count_--;
470
[email protected]b1f031dd2010-03-02 23:19:33471 CHECK_GT(group.active_socket_count, 0);
[email protected]2ab05b52009-07-01 23:57:58472 group.active_socket_count--;
[email protected]ff579d42009-06-24 15:47:02473
474 const bool can_reuse = socket->IsConnectedAndIdle();
475 if (can_reuse) {
[email protected]5fc08e32009-07-15 17:09:57476 AddIdleSocket(socket, true /* used socket */, &group);
[email protected]ff579d42009-06-24 15:47:02477 } else {
478 delete socket;
479 }
480
[email protected]2ab05b52009-07-01 23:57:58481 OnAvailableSocketSlot(group_name, &group);
[email protected]4f2abec2010-02-03 18:10:16482
483 // If there are no more releasing sockets, then we might have to process
484 // multiple available socket slots, since we stalled their processing until
485 // all sockets have been released.
[email protected]4f1e4982010-03-02 18:31:04486 i = group_map_.find(group_name);
487 if (i == group_map_.end() || i->second.num_releasing_sockets > 0)
[email protected]4f2abec2010-02-03 18:10:16488 return;
489
490 while (true) {
491 // We can't activate more sockets since we're already at our global limit.
492 if (ReachedMaxSocketsLimit())
493 return;
[email protected]616925a2010-03-02 19:02:38494
[email protected]4f2abec2010-02-03 18:10:16495 // |group| might now be deleted.
496 i = group_map_.find(group_name);
497 if (i == group_map_.end())
498 return;
499
500 group = i->second;
[email protected]616925a2010-03-02 19:02:38501
[email protected]4f2abec2010-02-03 18:10:16502 // If we already have enough ConnectJobs to satisfy the pending requests,
503 // don't bother starting up more.
504 if (group.pending_requests.size() <= group.jobs.size())
505 return;
506
507 if (!group.HasAvailableSocketSlot(max_sockets_per_group_))
508 return;
509
510 OnAvailableSocketSlot(group_name, &group);
511 }
[email protected]ff579d42009-06-24 15:47:02512}
513
[email protected]211d2172009-07-22 15:48:53514// Search for the highest priority pending request, amongst the groups that
515// are not at the |max_sockets_per_group_| limit. Note: for requests with
516// the same priority, the winner is based on group hash ordering (and not
517// insertion order).
[email protected]d80a4322009-08-14 07:07:49518int ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group,
519 std::string* group_name) {
[email protected]211d2172009-07-22 15:48:53520 Group* top_group = NULL;
521 const std::string* top_group_name = NULL;
522 int stalled_group_count = 0;
523 for (GroupMap::iterator i = group_map_.begin();
524 i != group_map_.end(); ++i) {
525 Group& group = i->second;
526 const RequestQueue& queue = group.pending_requests;
527 if (queue.empty())
528 continue;
[email protected]6427fe22010-04-16 22:27:41529 bool has_unused_slot =
530 group.HasAvailableSocketSlot(max_sockets_per_group_) &&
531 group.pending_requests.size() > group.jobs.size();
532 if (has_unused_slot) {
[email protected]211d2172009-07-22 15:48:53533 stalled_group_count++;
[email protected]6427fe22010-04-16 22:27:41534 bool has_higher_priority = !top_group ||
535 group.TopPendingPriority() < top_group->TopPendingPriority();
536 if (has_higher_priority) {
537 top_group = &group;
538 top_group_name = &i->first;
539 }
[email protected]211d2172009-07-22 15:48:53540 }
541 }
542 if (top_group) {
543 *group = top_group;
544 *group_name = *top_group_name;
545 }
546 return stalled_group_count;
547}
548
[email protected]d80a4322009-08-14 07:07:49549void ClientSocketPoolBaseHelper::OnConnectJobComplete(
550 int result, ConnectJob* job) {
[email protected]2ab05b52009-07-01 23:57:58551 DCHECK_NE(ERR_IO_PENDING, result);
552 const std::string group_name = job->group_name();
[email protected]ff579d42009-06-24 15:47:02553 GroupMap::iterator group_it = group_map_.find(group_name);
554 CHECK(group_it != group_map_.end());
555 Group& group = group_it->second;
556
[email protected]6b624c62010-03-14 08:37:32557 // We've had a connect on the socket; discard any pending backup job
558 // for this group and kill the pending task.
559 group.CleanupBackupJob();
560
[email protected]5fc08e32009-07-15 17:09:57561 scoped_ptr<ClientSocket> socket(job->ReleaseSocket());
[email protected]ff579d42009-06-24 15:47:02562
[email protected]9e743cd2010-03-16 07:03:53563 BoundNetLog job_log = job->net_log();
[email protected]4d3b05d2010-01-27 21:27:29564 RemoveConnectJob(job, &group);
[email protected]5fc08e32009-07-15 17:09:57565
[email protected]4d3b05d2010-01-27 21:27:29566 if (result == OK) {
567 DCHECK(socket.get());
[email protected]fd7b7c92009-08-20 19:38:30568 if (!group.pending_requests.empty()) {
[email protected]4d3b05d2010-01-27 21:27:29569 scoped_ptr<const Request> r(RemoveRequestFromQueue(
[email protected]fd7b7c92009-08-20 19:38:30570 group.pending_requests.begin(), &group.pending_requests));
[email protected]a2006ece2010-04-23 16:44:02571 r->net_log().EndEventWithInteger(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID,
[email protected]9e743cd2010-03-16 07:03:53572 job_log.source().id);
573 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]4d3b05d2010-01-27 21:27:29574 HandOutSocket(
575 socket.release(), false /* unused socket */, r->handle(),
[email protected]9e743cd2010-03-16 07:03:53576 base::TimeDelta(), &group, r->net_log());
[email protected]4d3b05d2010-01-27 21:27:29577 r->callback()->Run(result);
[email protected]5fc08e32009-07-15 17:09:57578 } else {
[email protected]4d3b05d2010-01-27 21:27:29579 AddIdleSocket(socket.release(), false /* unused socket */, &group);
580 OnAvailableSocketSlot(group_name, &group);
[email protected]5fc08e32009-07-15 17:09:57581 }
[email protected]94c20472010-01-14 08:14:36582 } else {
[email protected]4d3b05d2010-01-27 21:27:29583 DCHECK(!socket.get());
584 if (!group.pending_requests.empty()) {
585 scoped_ptr<const Request> r(RemoveRequestFromQueue(
586 group.pending_requests.begin(), &group.pending_requests));
[email protected]a2006ece2010-04-23 16:44:02587 r->net_log().EndEventWithInteger(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID,
[email protected]9e743cd2010-03-16 07:03:53588 job_log.source().id);
589 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]4d3b05d2010-01-27 21:27:29590 r->callback()->Run(result);
591 }
592 MaybeOnAvailableSocketSlot(group_name);
[email protected]ff579d42009-06-24 15:47:02593 }
[email protected]ff579d42009-06-24 15:47:02594}
595
[email protected]61a86c42010-04-19 22:45:53596void ClientSocketPoolBaseHelper::OnIPAddressChanged() {
597 CloseIdleSockets();
598}
599
[email protected]4d3b05d2010-01-27 21:27:29600void ClientSocketPoolBaseHelper::RemoveConnectJob(const ConnectJob *job,
601 Group* group) {
[email protected]b1f031dd2010-03-02 23:19:33602 CHECK_GT(connecting_socket_count_, 0);
[email protected]211d2172009-07-22 15:48:53603 connecting_socket_count_--;
604
[email protected]4d3b05d2010-01-27 21:27:29605 DCHECK(job);
606 delete job;
[email protected]5fc08e32009-07-15 17:09:57607
608 if (group) {
609 DCHECK(ContainsKey(group->jobs, job));
610 group->jobs.erase(job);
611 }
[email protected]ff579d42009-06-24 15:47:02612}
613
[email protected]d80a4322009-08-14 07:07:49614void ClientSocketPoolBaseHelper::MaybeOnAvailableSocketSlot(
[email protected]2ab05b52009-07-01 23:57:58615 const std::string& group_name) {
616 GroupMap::iterator it = group_map_.find(group_name);
617 if (it != group_map_.end()) {
618 Group& group = it->second;
619 if (group.HasAvailableSocketSlot(max_sockets_per_group_))
620 OnAvailableSocketSlot(group_name, &group);
621 }
622}
[email protected]ff579d42009-06-24 15:47:02623
[email protected]d80a4322009-08-14 07:07:49624void ClientSocketPoolBaseHelper::OnAvailableSocketSlot(
625 const std::string& group_name, Group* group) {
[email protected]211d2172009-07-22 15:48:53626 if (may_have_stalled_group_) {
627 std::string top_group_name;
[email protected]bed37d442009-08-20 19:58:20628 Group* top_group = NULL;
[email protected]211d2172009-07-22 15:48:53629 int stalled_group_count = FindTopStalledGroup(&top_group, &top_group_name);
630 if (stalled_group_count <= 1)
631 may_have_stalled_group_ = false;
[email protected]42df4e8e2010-04-13 22:02:56632 if (stalled_group_count >= 1)
[email protected]211d2172009-07-22 15:48:53633 ProcessPendingRequest(top_group_name, top_group);
634 } else if (!group->pending_requests.empty()) {
[email protected]ff579d42009-06-24 15:47:02635 ProcessPendingRequest(group_name, group);
636 // |group| may no longer be valid after this point. Be careful not to
637 // access it again.
[email protected]2ab05b52009-07-01 23:57:58638 } else if (group->IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02639 // Delete |group| if no longer needed. |group| will no longer be valid.
[email protected]ff579d42009-06-24 15:47:02640 group_map_.erase(group_name);
[email protected]ff579d42009-06-24 15:47:02641 }
642}
643
[email protected]d80a4322009-08-14 07:07:49644void ClientSocketPoolBaseHelper::ProcessPendingRequest(
645 const std::string& group_name, Group* group) {
[email protected]fd4fe0b2010-02-08 23:02:15646 scoped_ptr<const Request> r(*group->pending_requests.begin());
647 int rv = RequestSocketInternal(group_name, r.get());
[email protected]ff579d42009-06-24 15:47:02648
[email protected]2ab05b52009-07-01 23:57:58649 if (rv != ERR_IO_PENDING) {
[email protected]9e743cd2010-03-16 07:03:53650 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
[email protected]fd4fe0b2010-02-08 23:02:15651 RemoveRequestFromQueue(group->pending_requests.begin(),
652 &group->pending_requests);
[email protected]d80a4322009-08-14 07:07:49653 r->callback()->Run(rv);
[email protected]2ab05b52009-07-01 23:57:58654 if (rv != OK) {
655 // |group| may be invalid after the callback, we need to search
656 // |group_map_| again.
657 MaybeOnAvailableSocketSlot(group_name);
658 }
[email protected]d80a4322009-08-14 07:07:49659 } else {
660 r.release();
[email protected]2ab05b52009-07-01 23:57:58661 }
662}
663
[email protected]d80a4322009-08-14 07:07:49664void ClientSocketPoolBaseHelper::HandOutSocket(
[email protected]2ab05b52009-07-01 23:57:58665 ClientSocket* socket,
666 bool reused,
667 ClientSocketHandle* handle,
[email protected]f9d285c2009-08-17 19:54:29668 base::TimeDelta idle_time,
[email protected]fd4fe0b2010-02-08 23:02:15669 Group* group,
[email protected]9e743cd2010-03-16 07:03:53670 const BoundNetLog& net_log) {
[email protected]2ab05b52009-07-01 23:57:58671 DCHECK(socket);
672 handle->set_socket(socket);
673 handle->set_is_reused(reused);
[email protected]f9d285c2009-08-17 19:54:29674 handle->set_idle_time(idle_time);
[email protected]211d2172009-07-22 15:48:53675
[email protected]fd4fe0b2010-02-08 23:02:15676 if (reused)
[email protected]9e743cd2010-03-16 07:03:53677 net_log.AddStringLiteral("Reusing socket.");
[email protected]fd4fe0b2010-02-08 23:02:15678 if (idle_time != base::TimeDelta()) {
[email protected]9e743cd2010-03-16 07:03:53679 net_log.AddString(
[email protected]fd4fe0b2010-02-08 23:02:15680 StringPrintf("Socket sat idle for %" PRId64 " milliseconds",
681 idle_time.InMilliseconds()));
682 }
[email protected]a2006ece2010-04-23 16:44:02683 net_log.AddEventWithInteger(NetLog::TYPE_SOCKET_POOL_SOCKET_ID,
684 socket->NetLog().source().id);
[email protected]fd4fe0b2010-02-08 23:02:15685
[email protected]211d2172009-07-22 15:48:53686 handed_out_socket_count_++;
[email protected]2ab05b52009-07-01 23:57:58687 group->active_socket_count++;
[email protected]ff579d42009-06-24 15:47:02688}
689
[email protected]d80a4322009-08-14 07:07:49690void ClientSocketPoolBaseHelper::AddIdleSocket(
[email protected]5fc08e32009-07-15 17:09:57691 ClientSocket* socket, bool used, Group* group) {
692 DCHECK(socket);
693 IdleSocket idle_socket;
694 idle_socket.socket = socket;
695 idle_socket.start_time = base::TimeTicks::Now();
696 idle_socket.used = used;
697
698 group->idle_sockets.push_back(idle_socket);
699 IncrementIdleCount();
700}
701
[email protected]d80a4322009-08-14 07:07:49702void ClientSocketPoolBaseHelper::CancelAllConnectJobs() {
[email protected]5fc08e32009-07-15 17:09:57703 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end();) {
704 Group& group = i->second;
[email protected]4d3b05d2010-01-27 21:27:29705 connecting_socket_count_ -= group.jobs.size();
[email protected]5fc08e32009-07-15 17:09:57706 STLDeleteElements(&group.jobs);
707
[email protected]6b624c62010-03-14 08:37:32708 if (group.backup_task) {
709 group.backup_task->Cancel();
710 group.backup_task = NULL;
711 }
712
[email protected]5fc08e32009-07-15 17:09:57713 // Delete group if no longer needed.
714 if (group.IsEmpty()) {
[email protected]5fc08e32009-07-15 17:09:57715 group_map_.erase(i++);
716 } else {
717 ++i;
718 }
719 }
720}
721
[email protected]d80a4322009-08-14 07:07:49722bool ClientSocketPoolBaseHelper::ReachedMaxSocketsLimit() const {
[email protected]211d2172009-07-22 15:48:53723 // Each connecting socket will eventually connect and be handed out.
[email protected]42df4e8e2010-04-13 22:02:56724 int total = handed_out_socket_count_ + connecting_socket_count_;
[email protected]211d2172009-07-22 15:48:53725 DCHECK_LE(total, max_sockets_);
726 return total == max_sockets_;
727}
728
[email protected]d80a4322009-08-14 07:07:49729} // namespace internal
730
[email protected]ff579d42009-06-24 15:47:02731} // namespace net