blob: 037c9ffc561d86b6e10a3c6e058f7adc03f5c1a9 [file] [log] [blame]
[email protected]ff579d42009-06-24 15:47:021// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// 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"
8#include "base/message_loop.h"
9#include "base/stl_util-inl.h"
10#include "base/time.h"
[email protected]fd7b7c92009-08-20 19:38:3011#include "net/base/load_log.h"
[email protected]ff579d42009-06-24 15:47:0212#include "net/base/net_errors.h"
13#include "net/socket/client_socket_handle.h"
14
15using base::TimeDelta;
16
17namespace {
18
19// The timeout value, in seconds, used to clean up idle sockets that can't be
20// reused.
21//
22// Note: It's important to close idle sockets that have received data as soon
23// as possible because the received data may cause BSOD on Windows XP under
24// some conditions. See http://crbug.com/4606.
25const int kCleanupInterval = 10; // DO NOT INCREASE THIS TIMEOUT.
26
[email protected]60c4c412009-11-06 19:59:3627// The maximum size of the ConnectJob's LoadLog.
28const int kMaxNumLoadLogEntries = 50;
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,
35 const ClientSocketHandle* key_handle,
[email protected]974ebd62009-08-03 23:14:3436 base::TimeDelta timeout_duration,
[email protected]fd7b7c92009-08-20 19:38:3037 Delegate* delegate,
38 LoadLog* load_log)
[email protected]2ab05b52009-07-01 23:57:5839 : group_name_(group_name),
40 key_handle_(key_handle),
[email protected]974ebd62009-08-03 23:14:3441 timeout_duration_(timeout_duration),
[email protected]2ab05b52009-07-01 23:57:5842 delegate_(delegate),
[email protected]fd7b7c92009-08-20 19:38:3043 load_log_(load_log) {
[email protected]2ab05b52009-07-01 23:57:5844 DCHECK(!group_name.empty());
45 DCHECK(key_handle);
46 DCHECK(delegate);
47}
48
[email protected]fd7b7c92009-08-20 19:38:3049ConnectJob::~ConnectJob() {
50 if (delegate_) {
51 // If the delegate was not NULLed, then NotifyDelegateOfCompletion has
52 // not been called yet (hence we are cancelling).
53 LoadLog::AddEvent(load_log_, LoadLog::TYPE_CANCELLED);
54 LoadLog::EndEvent(load_log_, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB);
55 }
56}
[email protected]2ab05b52009-07-01 23:57:5857
[email protected]974ebd62009-08-03 23:14:3458int ConnectJob::Connect() {
59 if (timeout_duration_ != base::TimeDelta())
60 timer_.Start(timeout_duration_, this, &ConnectJob::OnTimeout);
[email protected]fd7b7c92009-08-20 19:38:3061
62 LoadLog::BeginEvent(load_log_, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB);
63
64 int rv = ConnectInternal();
65
66 if (rv != ERR_IO_PENDING) {
67 delegate_ = NULL;
68 LoadLog::EndEvent(load_log_, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB);
69 }
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
79 LoadLog::EndEvent(load_log_, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB);
80
81 delegate->OnConnectJobComplete(rv, this);
[email protected]974ebd62009-08-03 23:14:3482}
83
84void ConnectJob::OnTimeout() {
[email protected]6e713f02009-08-06 02:56:4085 // Make sure the socket is NULL before calling into |delegate|.
86 set_socket(NULL);
[email protected]fd7b7c92009-08-20 19:38:3087
88 LoadLog::AddEvent(load_log_,
89 LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB_TIMED_OUT);
90
91 NotifyDelegateOfCompletion(ERR_TIMED_OUT);
[email protected]974ebd62009-08-03 23:14:3492}
93
[email protected]d80a4322009-08-14 07:07:4994namespace internal {
95
96bool ClientSocketPoolBaseHelper::g_late_binding = false;
97
98ClientSocketPoolBaseHelper::ClientSocketPoolBaseHelper(
[email protected]211d2172009-07-22 15:48:5399 int max_sockets,
[email protected]ff579d42009-06-24 15:47:02100 int max_sockets_per_group,
[email protected]9bf28db2009-08-29 01:35:16101 base::TimeDelta unused_idle_socket_timeout,
102 base::TimeDelta used_idle_socket_timeout,
[email protected]100d5fb92009-12-21 21:08:35103 ConnectJobFactory* connect_job_factory,
104 const scoped_refptr<NetworkChangeNotifier>& network_change_notifier)
[email protected]ff579d42009-06-24 15:47:02105 : idle_socket_count_(0),
[email protected]211d2172009-07-22 15:48:53106 connecting_socket_count_(0),
107 handed_out_socket_count_(0),
108 max_sockets_(max_sockets),
[email protected]ff579d42009-06-24 15:47:02109 max_sockets_per_group_(max_sockets_per_group),
[email protected]9bf28db2009-08-29 01:35:16110 unused_idle_socket_timeout_(unused_idle_socket_timeout),
111 used_idle_socket_timeout_(used_idle_socket_timeout),
[email protected]211d2172009-07-22 15:48:53112 may_have_stalled_group_(false),
[email protected]100d5fb92009-12-21 21:08:35113 connect_job_factory_(connect_job_factory),
114 network_change_notifier_(network_change_notifier) {
[email protected]211d2172009-07-22 15:48:53115 DCHECK_LE(0, max_sockets_per_group);
116 DCHECK_LE(max_sockets_per_group, max_sockets);
[email protected]100d5fb92009-12-21 21:08:35117
118 if (network_change_notifier_)
119 network_change_notifier_->AddObserver(this);
[email protected]211d2172009-07-22 15:48:53120}
[email protected]ff579d42009-06-24 15:47:02121
[email protected]d80a4322009-08-14 07:07:49122ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() {
[email protected]5fc08e32009-07-15 17:09:57123 if (g_late_binding)
124 CancelAllConnectJobs();
[email protected]ff579d42009-06-24 15:47:02125 // Clean up any idle sockets. Assert that we have no remaining active
126 // sockets or pending requests. They should have all been cleaned up prior
127 // to the manager being destroyed.
128 CloseIdleSockets();
129 DCHECK(group_map_.empty());
130 DCHECK(connect_job_map_.empty());
[email protected]100d5fb92009-12-21 21:08:35131
132 if (network_change_notifier_)
133 network_change_notifier_->RemoveObserver(this);
[email protected]ff579d42009-06-24 15:47:02134}
135
136// InsertRequestIntoQueue inserts the request into the queue based on
137// priority. Highest priorities are closest to the front. Older requests are
138// prioritized over requests of equal priority.
139//
140// static
[email protected]d80a4322009-08-14 07:07:49141void ClientSocketPoolBaseHelper::InsertRequestIntoQueue(
142 const Request* r, RequestQueue* pending_requests) {
[email protected]fd7b7c92009-08-20 19:38:30143 LoadLog::BeginEvent(r->load_log(),
144 LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE);
145
[email protected]ff579d42009-06-24 15:47:02146 RequestQueue::iterator it = pending_requests->begin();
[email protected]ac790b42009-12-02 04:31:31147 while (it != pending_requests->end() && r->priority() >= (*it)->priority())
[email protected]ff579d42009-06-24 15:47:02148 ++it;
149 pending_requests->insert(it, r);
150}
151
[email protected]fd7b7c92009-08-20 19:38:30152// static
153const ClientSocketPoolBaseHelper::Request*
154ClientSocketPoolBaseHelper::RemoveRequestFromQueue(
155 RequestQueue::iterator it, RequestQueue* pending_requests) {
156 const Request* req = *it;
157
158 LoadLog::EndEvent(req->load_log(),
159 LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE);
160
161 pending_requests->erase(it);
162 return req;
163}
164
[email protected]d80a4322009-08-14 07:07:49165int ClientSocketPoolBaseHelper::RequestSocket(
[email protected]ff579d42009-06-24 15:47:02166 const std::string& group_name,
[email protected]d80a4322009-08-14 07:07:49167 const Request* request) {
168 DCHECK_GE(request->priority(), 0);
169 CompletionCallback* const callback = request->callback();
170 CHECK(callback);
171 ClientSocketHandle* const handle = request->handle();
172 CHECK(handle);
[email protected]ff579d42009-06-24 15:47:02173 Group& group = group_map_[group_name];
174
[email protected]ff579d42009-06-24 15:47:02175 // Can we make another active socket now?
[email protected]211d2172009-07-22 15:48:53176 if (ReachedMaxSocketsLimit() ||
177 !group.HasAvailableSocketSlot(max_sockets_per_group_)) {
178 if (ReachedMaxSocketsLimit()) {
179 // We could check if we really have a stalled group here, but it requires
180 // a scan of all groups, so just flip a flag here, and do the check later.
181 may_have_stalled_group_ = true;
[email protected]fd7b7c92009-08-20 19:38:30182
183 LoadLog::AddEvent(request->load_log(),
184 LoadLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS);
185 } else {
186 LoadLog::AddEvent(request->load_log(),
187 LoadLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP);
[email protected]211d2172009-07-22 15:48:53188 }
[email protected]d80a4322009-08-14 07:07:49189 InsertRequestIntoQueue(request, &group.pending_requests);
[email protected]ff579d42009-06-24 15:47:02190 return ERR_IO_PENDING;
191 }
192
[email protected]5edbf8d2010-01-13 18:44:11193 // Try to reuse a socket.
[email protected]ff579d42009-06-24 15:47:02194 while (!group.idle_sockets.empty()) {
195 IdleSocket idle_socket = group.idle_sockets.back();
196 group.idle_sockets.pop_back();
197 DecrementIdleCount();
198 if (idle_socket.socket->IsConnectedAndIdle()) {
199 // We found one we can reuse!
[email protected]f9d285c2009-08-17 19:54:29200 base::TimeDelta idle_time =
201 base::TimeTicks::Now() - idle_socket.start_time;
[email protected]d80a4322009-08-14 07:07:49202 HandOutSocket(
[email protected]f9d285c2009-08-17 19:54:29203 idle_socket.socket, idle_socket.used, handle, idle_time, &group);
[email protected]ff579d42009-06-24 15:47:02204 return OK;
205 }
206 delete idle_socket.socket;
207 }
208
[email protected]5edbf8d2010-01-13 18:44:11209 // See if we already have enough connect jobs or sockets that will be released
210 // soon.
211 if (g_late_binding && group.HasReleasingSockets()) {
212 InsertRequestIntoQueue(request, &group.pending_requests);
213 return ERR_IO_PENDING;
214 }
215
[email protected]ff579d42009-06-24 15:47:02216 // We couldn't find a socket to reuse, so allocate and connect a new one.
217
[email protected]fd7b7c92009-08-20 19:38:30218 // If we aren't using late binding, the job lines up with a request so
219 // just write directly into the request's LoadLog.
220 scoped_refptr<LoadLog> job_load_log = g_late_binding ?
[email protected]60c4c412009-11-06 19:59:36221 new LoadLog(kMaxNumLoadLogEntries) : request->load_log();
[email protected]fd7b7c92009-08-20 19:38:30222
[email protected]2ab05b52009-07-01 23:57:58223 scoped_ptr<ConnectJob> connect_job(
[email protected]fd7b7c92009-08-20 19:38:30224 connect_job_factory_->NewConnectJob(group_name, *request, this,
[email protected]5edbf8d2010-01-13 18:44:11225 job_load_log));
[email protected]ff579d42009-06-24 15:47:02226
[email protected]2ab05b52009-07-01 23:57:58227 int rv = connect_job->Connect();
[email protected]fd7b7c92009-08-20 19:38:30228
229 if (g_late_binding && rv != ERR_IO_PENDING && request->load_log())
230 request->load_log()->Append(job_load_log);
231
[email protected]2ab05b52009-07-01 23:57:58232 if (rv == OK) {
233 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */,
[email protected]f9d285c2009-08-17 19:54:29234 handle, base::TimeDelta(), &group);
[email protected]2ab05b52009-07-01 23:57:58235 } else if (rv == ERR_IO_PENDING) {
[email protected]211d2172009-07-22 15:48:53236 connecting_socket_count_++;
237
[email protected]5fc08e32009-07-15 17:09:57238 ConnectJob* job = connect_job.release();
239 if (g_late_binding) {
240 CHECK(!ContainsKey(connect_job_map_, handle));
[email protected]d80a4322009-08-14 07:07:49241 InsertRequestIntoQueue(request, &group.pending_requests);
[email protected]5fc08e32009-07-15 17:09:57242 } else {
[email protected]d80a4322009-08-14 07:07:49243 group.connecting_requests[handle] = request;
[email protected]5fc08e32009-07-15 17:09:57244 CHECK(!ContainsKey(connect_job_map_, handle));
245 connect_job_map_[handle] = job;
246 }
247 group.jobs.insert(job);
[email protected]2b7523d2009-07-29 20:29:23248 } else if (group.IsEmpty()) {
249 group_map_.erase(group_name);
[email protected]2ab05b52009-07-01 23:57:58250 }
[email protected]ff579d42009-06-24 15:47:02251
[email protected]2ab05b52009-07-01 23:57:58252 return rv;
[email protected]ff579d42009-06-24 15:47:02253}
254
[email protected]d80a4322009-08-14 07:07:49255void ClientSocketPoolBaseHelper::CancelRequest(
256 const std::string& group_name, const ClientSocketHandle* handle) {
[email protected]ff579d42009-06-24 15:47:02257 CHECK(ContainsKey(group_map_, group_name));
258
259 Group& group = group_map_[group_name];
260
[email protected]ff579d42009-06-24 15:47:02261 // Search pending_requests for matching handle.
262 RequestQueue::iterator it = group.pending_requests.begin();
263 for (; it != group.pending_requests.end(); ++it) {
[email protected]d80a4322009-08-14 07:07:49264 if ((*it)->handle() == handle) {
[email protected]fd7b7c92009-08-20 19:38:30265 const Request* req = RemoveRequestFromQueue(it, &group.pending_requests);
266 LoadLog::AddEvent(req->load_log(), LoadLog::TYPE_CANCELLED);
267 LoadLog::EndEvent(req->load_log(), LoadLog::TYPE_SOCKET_POOL);
268 delete req;
[email protected]974ebd62009-08-03 23:14:34269 if (g_late_binding &&
270 group.jobs.size() > group.pending_requests.size() + 1) {
271 // TODO(willchan): Cancel the job in the earliest LoadState.
272 RemoveConnectJob(handle, *group.jobs.begin(), &group);
273 OnAvailableSocketSlot(group_name, &group);
274 }
[email protected]ff579d42009-06-24 15:47:02275 return;
276 }
277 }
278
[email protected]5fc08e32009-07-15 17:09:57279 if (!g_late_binding) {
280 // It's invalid to cancel a non-existent request.
281 CHECK(ContainsKey(group.connecting_requests, handle));
[email protected]ff579d42009-06-24 15:47:02282
[email protected]5fc08e32009-07-15 17:09:57283 RequestMap::iterator map_it = group.connecting_requests.find(handle);
284 if (map_it != group.connecting_requests.end()) {
[email protected]fd7b7c92009-08-20 19:38:30285 scoped_refptr<LoadLog> log(map_it->second->load_log());
286 LoadLog::AddEvent(log, LoadLog::TYPE_CANCELLED);
287 LoadLog::EndEvent(log, LoadLog::TYPE_SOCKET_POOL);
[email protected]5fc08e32009-07-15 17:09:57288 RemoveConnectJob(handle, NULL, &group);
289 OnAvailableSocketSlot(group_name, &group);
290 }
[email protected]ff579d42009-06-24 15:47:02291 }
292}
293
[email protected]d80a4322009-08-14 07:07:49294void ClientSocketPoolBaseHelper::ReleaseSocket(const std::string& group_name,
295 ClientSocket* socket) {
[email protected]5edbf8d2010-01-13 18:44:11296 Group& group = group_map_[group_name];
297 group.num_releasing_sockets++;
298 DCHECK_LE(group.num_releasing_sockets, group.active_socket_count);
[email protected]ff579d42009-06-24 15:47:02299 // Run this asynchronously to allow the caller to finish before we let
300 // another to begin doing work. This also avoids nasty recursion issues.
301 // NOTE: We cannot refer to the handle argument after this method returns.
302 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]d80a4322009-08-14 07:07:49303 this, &ClientSocketPoolBaseHelper::DoReleaseSocket, group_name, socket));
[email protected]ff579d42009-06-24 15:47:02304}
305
[email protected]d80a4322009-08-14 07:07:49306void ClientSocketPoolBaseHelper::CloseIdleSockets() {
[email protected]ff579d42009-06-24 15:47:02307 CleanupIdleSockets(true);
308}
309
[email protected]d80a4322009-08-14 07:07:49310int ClientSocketPoolBaseHelper::IdleSocketCountInGroup(
[email protected]ff579d42009-06-24 15:47:02311 const std::string& group_name) const {
312 GroupMap::const_iterator i = group_map_.find(group_name);
313 CHECK(i != group_map_.end());
314
315 return i->second.idle_sockets.size();
316}
317
[email protected]d80a4322009-08-14 07:07:49318LoadState ClientSocketPoolBaseHelper::GetLoadState(
[email protected]ff579d42009-06-24 15:47:02319 const std::string& group_name,
320 const ClientSocketHandle* handle) const {
321 if (!ContainsKey(group_map_, group_name)) {
322 NOTREACHED() << "ClientSocketPool does not contain group: " << group_name
323 << " for handle: " << handle;
324 return LOAD_STATE_IDLE;
325 }
326
327 // Can't use operator[] since it is non-const.
328 const Group& group = group_map_.find(group_name)->second;
329
330 // Search connecting_requests for matching handle.
331 RequestMap::const_iterator map_it = group.connecting_requests.find(handle);
332 if (map_it != group.connecting_requests.end()) {
[email protected]ab838892009-06-30 18:49:05333 ConnectJobMap::const_iterator job_it = connect_job_map_.find(handle);
334 if (job_it == connect_job_map_.end()) {
335 NOTREACHED();
336 return LOAD_STATE_IDLE;
337 }
[email protected]46451352009-09-01 14:54:21338 return job_it->second->GetLoadState();
[email protected]ff579d42009-06-24 15:47:02339 }
340
341 // Search pending_requests for matching handle.
342 RequestQueue::const_iterator it = group.pending_requests.begin();
[email protected]5fc08e32009-07-15 17:09:57343 for (size_t i = 0; it != group.pending_requests.end(); ++it, ++i) {
[email protected]d80a4322009-08-14 07:07:49344 if ((*it)->handle() == handle) {
[email protected]5fc08e32009-07-15 17:09:57345 if (g_late_binding && i < group.jobs.size()) {
346 LoadState max_state = LOAD_STATE_IDLE;
347 for (ConnectJobSet::const_iterator job_it = group.jobs.begin();
348 job_it != group.jobs.end(); ++job_it) {
[email protected]46451352009-09-01 14:54:21349 max_state = std::max(max_state, (*job_it)->GetLoadState());
[email protected]5fc08e32009-07-15 17:09:57350 }
351 return max_state;
352 } else {
353 // TODO(wtc): Add a state for being on the wait list.
354 // See http://www.crbug.com/5077.
355 return LOAD_STATE_IDLE;
356 }
[email protected]ff579d42009-06-24 15:47:02357 }
358 }
359
360 NOTREACHED();
361 return LOAD_STATE_IDLE;
362}
363
[email protected]d80a4322009-08-14 07:07:49364bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup(
[email protected]9bf28db2009-08-29 01:35:16365 base::TimeTicks now,
366 base::TimeDelta timeout) const {
367 bool timed_out = (now - start_time) >= timeout;
[email protected]5fc08e32009-07-15 17:09:57368 return timed_out ||
369 !(used ? socket->IsConnectedAndIdle() : socket->IsConnected());
[email protected]ff579d42009-06-24 15:47:02370}
371
[email protected]d80a4322009-08-14 07:07:49372void ClientSocketPoolBaseHelper::CleanupIdleSockets(bool force) {
[email protected]ff579d42009-06-24 15:47:02373 if (idle_socket_count_ == 0)
374 return;
375
376 // Current time value. Retrieving it once at the function start rather than
377 // inside the inner loop, since it shouldn't change by any meaningful amount.
378 base::TimeTicks now = base::TimeTicks::Now();
379
380 GroupMap::iterator i = group_map_.begin();
381 while (i != group_map_.end()) {
382 Group& group = i->second;
383
384 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
385 while (j != group.idle_sockets.end()) {
[email protected]9bf28db2009-08-29 01:35:16386 base::TimeDelta timeout =
387 j->used ? used_idle_socket_timeout_ : unused_idle_socket_timeout_;
388 if (force || j->ShouldCleanup(now, timeout)) {
[email protected]ff579d42009-06-24 15:47:02389 delete j->socket;
390 j = group.idle_sockets.erase(j);
391 DecrementIdleCount();
392 } else {
393 ++j;
394 }
395 }
396
397 // Delete group if no longer needed.
[email protected]2ab05b52009-07-01 23:57:58398 if (group.IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02399 group_map_.erase(i++);
400 } else {
401 ++i;
402 }
403 }
404}
405
[email protected]d80a4322009-08-14 07:07:49406void ClientSocketPoolBaseHelper::IncrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02407 if (++idle_socket_count_ == 1)
408 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this,
[email protected]d80a4322009-08-14 07:07:49409 &ClientSocketPoolBaseHelper::OnCleanupTimerFired);
[email protected]ff579d42009-06-24 15:47:02410}
411
[email protected]d80a4322009-08-14 07:07:49412void ClientSocketPoolBaseHelper::DecrementIdleCount() {
[email protected]ff579d42009-06-24 15:47:02413 if (--idle_socket_count_ == 0)
414 timer_.Stop();
415}
416
[email protected]d80a4322009-08-14 07:07:49417void ClientSocketPoolBaseHelper::DoReleaseSocket(const std::string& group_name,
418 ClientSocket* socket) {
[email protected]ff579d42009-06-24 15:47:02419 GroupMap::iterator i = group_map_.find(group_name);
420 CHECK(i != group_map_.end());
421
422 Group& group = i->second;
423
[email protected]5edbf8d2010-01-13 18:44:11424 group.num_releasing_sockets--;
425 DCHECK_GE(group.num_releasing_sockets, 0);
426
[email protected]211d2172009-07-22 15:48:53427 CHECK(handed_out_socket_count_ > 0);
428 handed_out_socket_count_--;
429
[email protected]ff579d42009-06-24 15:47:02430 CHECK(group.active_socket_count > 0);
[email protected]2ab05b52009-07-01 23:57:58431 group.active_socket_count--;
[email protected]ff579d42009-06-24 15:47:02432
433 const bool can_reuse = socket->IsConnectedAndIdle();
434 if (can_reuse) {
[email protected]5fc08e32009-07-15 17:09:57435 AddIdleSocket(socket, true /* used socket */, &group);
[email protected]ff579d42009-06-24 15:47:02436 } else {
437 delete socket;
438 }
439
[email protected]2ab05b52009-07-01 23:57:58440 OnAvailableSocketSlot(group_name, &group);
[email protected]ff579d42009-06-24 15:47:02441}
442
[email protected]211d2172009-07-22 15:48:53443// Search for the highest priority pending request, amongst the groups that
444// are not at the |max_sockets_per_group_| limit. Note: for requests with
445// the same priority, the winner is based on group hash ordering (and not
446// insertion order).
[email protected]d80a4322009-08-14 07:07:49447int ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group,
448 std::string* group_name) {
[email protected]211d2172009-07-22 15:48:53449 Group* top_group = NULL;
450 const std::string* top_group_name = NULL;
451 int stalled_group_count = 0;
452 for (GroupMap::iterator i = group_map_.begin();
453 i != group_map_.end(); ++i) {
454 Group& group = i->second;
455 const RequestQueue& queue = group.pending_requests;
456 if (queue.empty())
457 continue;
458 bool has_slot = group.HasAvailableSocketSlot(max_sockets_per_group_);
459 if (has_slot)
460 stalled_group_count++;
461 bool has_higher_priority = !top_group ||
[email protected]ac790b42009-12-02 04:31:31462 group.TopPendingPriority() < top_group->TopPendingPriority();
[email protected]211d2172009-07-22 15:48:53463 if (has_slot && has_higher_priority) {
464 top_group = &group;
465 top_group_name = &i->first;
466 }
467 }
468 if (top_group) {
469 *group = top_group;
470 *group_name = *top_group_name;
471 }
472 return stalled_group_count;
473}
474
[email protected]d80a4322009-08-14 07:07:49475void ClientSocketPoolBaseHelper::OnConnectJobComplete(
476 int result, ConnectJob* job) {
[email protected]2ab05b52009-07-01 23:57:58477 DCHECK_NE(ERR_IO_PENDING, result);
478 const std::string group_name = job->group_name();
[email protected]ff579d42009-06-24 15:47:02479 GroupMap::iterator group_it = group_map_.find(group_name);
480 CHECK(group_it != group_map_.end());
481 Group& group = group_it->second;
482
[email protected]5fc08e32009-07-15 17:09:57483 const ClientSocketHandle* const key_handle = job->key_handle();
484 scoped_ptr<ClientSocket> socket(job->ReleaseSocket());
[email protected]ff579d42009-06-24 15:47:02485
[email protected]5fc08e32009-07-15 17:09:57486 if (g_late_binding) {
[email protected]fd7b7c92009-08-20 19:38:30487 scoped_refptr<LoadLog> job_load_log(job->load_log());
[email protected]5fc08e32009-07-15 17:09:57488 RemoveConnectJob(key_handle, job, &group);
489
[email protected]fd7b7c92009-08-20 19:38:30490 scoped_ptr<const Request> r;
491 if (!group.pending_requests.empty()) {
492 r.reset(RemoveRequestFromQueue(
493 group.pending_requests.begin(), &group.pending_requests));
494
495 if (r->load_log())
496 r->load_log()->Append(job_load_log);
497
498 LoadLog::EndEvent(r->load_log(), LoadLog::TYPE_SOCKET_POOL);
499 }
500
[email protected]5fc08e32009-07-15 17:09:57501 if (result == OK) {
502 DCHECK(socket.get());
[email protected]fd7b7c92009-08-20 19:38:30503 if (r.get()) {
[email protected]5fc08e32009-07-15 17:09:57504 HandOutSocket(
[email protected]f9d285c2009-08-17 19:54:29505 socket.release(), false /* unused socket */, r->handle(),
506 base::TimeDelta(), &group);
[email protected]d80a4322009-08-14 07:07:49507 r->callback()->Run(result);
[email protected]5fc08e32009-07-15 17:09:57508 } else {
509 AddIdleSocket(socket.release(), false /* unused socket */, &group);
510 OnAvailableSocketSlot(group_name, &group);
511 }
512 } else {
513 DCHECK(!socket.get());
[email protected]fd7b7c92009-08-20 19:38:30514 if (r.get())
[email protected]d80a4322009-08-14 07:07:49515 r->callback()->Run(result);
[email protected]5fc08e32009-07-15 17:09:57516 MaybeOnAvailableSocketSlot(group_name);
517 }
518
519 return;
520 }
521
522 RequestMap* request_map = &group.connecting_requests;
523 RequestMap::iterator it = request_map->find(key_handle);
[email protected]ff579d42009-06-24 15:47:02524 CHECK(it != request_map->end());
[email protected]d80a4322009-08-14 07:07:49525 const Request* request = it->second;
526 ClientSocketHandle* const handle = request->handle();
527 CompletionCallback* const callback = request->callback();
[email protected]ff579d42009-06-24 15:47:02528
[email protected]fd7b7c92009-08-20 19:38:30529 LoadLog::EndEvent(request->load_log(), LoadLog::TYPE_SOCKET_POOL);
530
[email protected]5fc08e32009-07-15 17:09:57531 RemoveConnectJob(key_handle, job, &group);
[email protected]2ab05b52009-07-01 23:57:58532
533 if (result != OK) {
[email protected]8e12ae02009-07-02 16:15:04534 DCHECK(!socket.get());
[email protected]2ab05b52009-07-01 23:57:58535 callback->Run(result); // |group| is not necessarily valid after this.
536 // |group| may be invalid after the callback, we need to search
537 // |group_map_| again.
538 MaybeOnAvailableSocketSlot(group_name);
[email protected]ff579d42009-06-24 15:47:02539 } else {
[email protected]5fc08e32009-07-15 17:09:57540 DCHECK(socket.get());
[email protected]f9d285c2009-08-17 19:54:29541 HandOutSocket(socket.release(), false /* not reused */, handle,
542 base::TimeDelta(), &group);
[email protected]2ab05b52009-07-01 23:57:58543 callback->Run(result);
[email protected]ff579d42009-06-24 15:47:02544 }
[email protected]ff579d42009-06-24 15:47:02545}
546
[email protected]100d5fb92009-12-21 21:08:35547void ClientSocketPoolBaseHelper::OnIPAddressChanged() {
548 CloseIdleSockets();
549}
550
[email protected]d80a4322009-08-14 07:07:49551void ClientSocketPoolBaseHelper::EnableLateBindingOfSockets(bool enabled) {
[email protected]5fc08e32009-07-15 17:09:57552 g_late_binding = enabled;
553}
554
[email protected]d80a4322009-08-14 07:07:49555void ClientSocketPoolBaseHelper::RemoveConnectJob(
[email protected]974ebd62009-08-03 23:14:34556 const ClientSocketHandle* handle, const ConnectJob *job, Group* group) {
[email protected]211d2172009-07-22 15:48:53557 CHECK(connecting_socket_count_ > 0);
558 connecting_socket_count_--;
559
[email protected]5fc08e32009-07-15 17:09:57560 if (g_late_binding) {
561 DCHECK(job);
562 delete job;
563 } else {
564 ConnectJobMap::iterator it = connect_job_map_.find(handle);
565 CHECK(it != connect_job_map_.end());
566 job = it->second;
567 delete job;
568 connect_job_map_.erase(it);
[email protected]d80a4322009-08-14 07:07:49569 RequestMap::iterator map_it = group->connecting_requests.find(handle);
570 CHECK(map_it != group->connecting_requests.end());
571 const Request* request = map_it->second;
572 delete request;
573 group->connecting_requests.erase(map_it);
[email protected]5fc08e32009-07-15 17:09:57574 }
575
576 if (group) {
577 DCHECK(ContainsKey(group->jobs, job));
578 group->jobs.erase(job);
579 }
[email protected]ff579d42009-06-24 15:47:02580}
581
[email protected]d80a4322009-08-14 07:07:49582void ClientSocketPoolBaseHelper::MaybeOnAvailableSocketSlot(
[email protected]2ab05b52009-07-01 23:57:58583 const std::string& group_name) {
584 GroupMap::iterator it = group_map_.find(group_name);
585 if (it != group_map_.end()) {
586 Group& group = it->second;
587 if (group.HasAvailableSocketSlot(max_sockets_per_group_))
588 OnAvailableSocketSlot(group_name, &group);
589 }
590}
[email protected]ff579d42009-06-24 15:47:02591
[email protected]d80a4322009-08-14 07:07:49592void ClientSocketPoolBaseHelper::OnAvailableSocketSlot(
593 const std::string& group_name, Group* group) {
[email protected]211d2172009-07-22 15:48:53594 if (may_have_stalled_group_) {
595 std::string top_group_name;
[email protected]bed37d442009-08-20 19:58:20596 Group* top_group = NULL;
[email protected]211d2172009-07-22 15:48:53597 int stalled_group_count = FindTopStalledGroup(&top_group, &top_group_name);
598 if (stalled_group_count <= 1)
599 may_have_stalled_group_ = false;
600 if (stalled_group_count >= 1)
601 ProcessPendingRequest(top_group_name, top_group);
602 } else if (!group->pending_requests.empty()) {
[email protected]ff579d42009-06-24 15:47:02603 ProcessPendingRequest(group_name, group);
604 // |group| may no longer be valid after this point. Be careful not to
605 // access it again.
[email protected]2ab05b52009-07-01 23:57:58606 } else if (group->IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02607 // Delete |group| if no longer needed. |group| will no longer be valid.
[email protected]ff579d42009-06-24 15:47:02608 group_map_.erase(group_name);
[email protected]ff579d42009-06-24 15:47:02609 }
610}
611
[email protected]d80a4322009-08-14 07:07:49612void ClientSocketPoolBaseHelper::ProcessPendingRequest(
613 const std::string& group_name, Group* group) {
[email protected]fd7b7c92009-08-20 19:38:30614 scoped_ptr<const Request> r(RemoveRequestFromQueue(
615 group->pending_requests.begin(), &group->pending_requests));
[email protected]ff579d42009-06-24 15:47:02616
[email protected]d80a4322009-08-14 07:07:49617 int rv = RequestSocket(group_name, r.get());
[email protected]ff579d42009-06-24 15:47:02618
[email protected]2ab05b52009-07-01 23:57:58619 if (rv != ERR_IO_PENDING) {
[email protected]fd7b7c92009-08-20 19:38:30620 LoadLog::EndEvent(r->load_log(), LoadLog::TYPE_SOCKET_POOL);
[email protected]d80a4322009-08-14 07:07:49621 r->callback()->Run(rv);
[email protected]2ab05b52009-07-01 23:57:58622 if (rv != OK) {
623 // |group| may be invalid after the callback, we need to search
624 // |group_map_| again.
625 MaybeOnAvailableSocketSlot(group_name);
626 }
[email protected]d80a4322009-08-14 07:07:49627 } else {
628 r.release();
[email protected]2ab05b52009-07-01 23:57:58629 }
630}
631
[email protected]d80a4322009-08-14 07:07:49632void ClientSocketPoolBaseHelper::HandOutSocket(
[email protected]2ab05b52009-07-01 23:57:58633 ClientSocket* socket,
634 bool reused,
635 ClientSocketHandle* handle,
[email protected]f9d285c2009-08-17 19:54:29636 base::TimeDelta idle_time,
[email protected]2ab05b52009-07-01 23:57:58637 Group* group) {
638 DCHECK(socket);
639 handle->set_socket(socket);
640 handle->set_is_reused(reused);
[email protected]f9d285c2009-08-17 19:54:29641 handle->set_idle_time(idle_time);
[email protected]211d2172009-07-22 15:48:53642
643 handed_out_socket_count_++;
[email protected]2ab05b52009-07-01 23:57:58644 group->active_socket_count++;
[email protected]ff579d42009-06-24 15:47:02645}
646
[email protected]d80a4322009-08-14 07:07:49647void ClientSocketPoolBaseHelper::AddIdleSocket(
[email protected]5fc08e32009-07-15 17:09:57648 ClientSocket* socket, bool used, Group* group) {
649 DCHECK(socket);
650 IdleSocket idle_socket;
651 idle_socket.socket = socket;
652 idle_socket.start_time = base::TimeTicks::Now();
653 idle_socket.used = used;
654
655 group->idle_sockets.push_back(idle_socket);
656 IncrementIdleCount();
657}
658
[email protected]d80a4322009-08-14 07:07:49659void ClientSocketPoolBaseHelper::CancelAllConnectJobs() {
[email protected]5fc08e32009-07-15 17:09:57660 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end();) {
661 Group& group = i->second;
662 STLDeleteElements(&group.jobs);
663
664 // Delete group if no longer needed.
665 if (group.IsEmpty()) {
[email protected]5fc08e32009-07-15 17:09:57666 group_map_.erase(i++);
667 } else {
668 ++i;
669 }
670 }
671}
672
[email protected]d80a4322009-08-14 07:07:49673bool ClientSocketPoolBaseHelper::ReachedMaxSocketsLimit() const {
[email protected]211d2172009-07-22 15:48:53674 // Each connecting socket will eventually connect and be handed out.
675 int total = handed_out_socket_count_ + connecting_socket_count_;
676 DCHECK_LE(total, max_sockets_);
677 return total == max_sockets_;
678}
679
[email protected]d80a4322009-08-14 07:07:49680} // namespace internal
681
682void EnableLateBindingOfSockets(bool enabled) {
683 internal::ClientSocketPoolBaseHelper::EnableLateBindingOfSockets(enabled);
684}
685
[email protected]ff579d42009-06-24 15:47:02686} // namespace net