Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(176)

Side by Side Diff: net/base/tcp_client_socket_pool.cc

Issue 144009: Move socket related files from net/base to net/socket. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/base/tcp_client_socket_pool.h ('k') | net/base/tcp_client_socket_pool_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // 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/base/tcp_client_socket_pool.h"
6
7 #include "base/compiler_specific.h"
8 #include "base/field_trial.h"
9 #include "base/message_loop.h"
10 #include "base/time.h"
11 #include "base/stl_util-inl.h"
12 #include "net/base/client_socket_factory.h"
13 #include "net/base/client_socket_handle.h"
14 #include "net/base/net_errors.h"
15 #include "net/base/tcp_client_socket.h"
16
17 using base::TimeDelta;
18
19 namespace {
20
21 // The timeout value, in seconds, used to clean up idle sockets that can't be
22 // reused.
23 //
24 // Note: It's important to close idle sockets that have received data as soon
25 // as possible because the received data may cause BSOD on Windows XP under
26 // some conditions. See http://crbug.com/4606.
27 const int kCleanupInterval = 10; // DO NOT INCREASE THIS TIMEOUT.
28
29 // The maximum duration, in seconds, to keep idle persistent sockets alive.
30 const int kIdleTimeout = 300; // 5 minutes.
31
32 } // namespace
33
34 namespace net {
35
36 TCPConnectingSocket::TCPConnectingSocket(
37 const std::string& group_name,
38 const HostResolver::RequestInfo& resolve_info,
39 const ClientSocketHandle* handle,
40 ClientSocketFactory* client_socket_factory,
41 ClientSocketPoolBase* pool)
42 : group_name_(group_name),
43 resolve_info_(resolve_info),
44 handle_(handle),
45 client_socket_factory_(client_socket_factory),
46 ALLOW_THIS_IN_INITIALIZER_LIST(
47 callback_(this,
48 &TCPConnectingSocket::OnIOComplete)),
49 pool_(pool),
50 resolver_(pool->GetHostResolver()) {}
51
52 TCPConnectingSocket::~TCPConnectingSocket() {
53 // We don't worry about cancelling the host resolution and TCP connect, since
54 // ~SingleRequestHostResolver and ~ClientSocket will take care of it.
55 }
56
57 int TCPConnectingSocket::Connect() {
58 int rv = resolver_.Resolve(resolve_info_, &addresses_, &callback_);
59 if (rv != ERR_IO_PENDING)
60 rv = OnIOCompleteInternal(rv, true /* synchronous */);
61 return rv;
62 }
63
64 void TCPConnectingSocket::OnIOComplete(int result) {
65 OnIOCompleteInternal(result, false /* asynchronous */);
66 }
67
68 int TCPConnectingSocket::OnIOCompleteInternal(
69 int result, bool synchronous) {
70 CHECK(result != ERR_IO_PENDING);
71
72 ClientSocketPoolBase::Request* request = pool_->GetConnectingRequest(
73 group_name_, handle_);
74 CHECK(request);
75
76 if (result == OK && request->load_state == LOAD_STATE_RESOLVING_HOST) {
77 request->load_state = LOAD_STATE_CONNECTING;
78 socket_.reset(client_socket_factory_->CreateTCPClientSocket(addresses_));
79 connect_start_time_ = base::TimeTicks::Now();
80 result = socket_->Connect(&callback_);
81 if (result == ERR_IO_PENDING)
82 return result;
83 }
84
85 if (result == OK) {
86 CHECK(request->load_state == LOAD_STATE_CONNECTING);
87 CHECK(connect_start_time_ != base::TimeTicks());
88 base::TimeDelta connect_duration =
89 base::TimeTicks::Now() - connect_start_time_;
90
91 UMA_HISTOGRAM_CLIPPED_TIMES("Net.TCP_Connection_Latency",
92 connect_duration,
93 base::TimeDelta::FromMilliseconds(1),
94 base::TimeDelta::FromMinutes(10),
95 100);
96 }
97
98 // Now, we either succeeded at Connect()'ing, or we failed at host resolution
99 // or Connect()'ing. Either way, we'll run the callback to alert the client.
100
101 CompletionCallback* callback = NULL;
102
103 if (result == OK) {
104 callback = pool_->OnConnectingRequestComplete(
105 group_name_,
106 handle_,
107 false /* don't deactivate socket */,
108 socket_.release());
109 } else {
110 callback = pool_->OnConnectingRequestComplete(
111 group_name_,
112 handle_,
113 true /* deactivate socket */,
114 NULL /* no connected socket to give */);
115 }
116
117 // |this| is deleted after this point.
118
119 CHECK(callback);
120
121 if (!synchronous)
122 callback->Run(result);
123 return result;
124 }
125
126 ClientSocketPoolBase::ClientSocketPoolBase(
127 int max_sockets_per_group,
128 HostResolver* host_resolver,
129 ConnectingSocketFactory* connecting_socket_factory)
130 : idle_socket_count_(0),
131 max_sockets_per_group_(max_sockets_per_group),
132 host_resolver_(host_resolver),
133 connecting_socket_factory_(connecting_socket_factory) {}
134
135 ClientSocketPoolBase::~ClientSocketPoolBase() {
136 // Clean up any idle sockets. Assert that we have no remaining active
137 // sockets or pending requests. They should have all been cleaned up prior
138 // to the manager being destroyed.
139 CloseIdleSockets();
140 DCHECK(group_map_.empty());
141 DCHECK(connecting_socket_map_.empty());
142 }
143
144 // InsertRequestIntoQueue inserts the request into the queue based on
145 // priority. Highest priorities are closest to the front. Older requests are
146 // prioritized over requests of equal priority.
147 //
148 // static
149 void ClientSocketPoolBase::InsertRequestIntoQueue(
150 const Request& r, RequestQueue* pending_requests) {
151 RequestQueue::iterator it = pending_requests->begin();
152 while (it != pending_requests->end() && r.priority <= it->priority)
153 ++it;
154 pending_requests->insert(it, r);
155 }
156
157 int ClientSocketPoolBase::RequestSocket(
158 const std::string& group_name,
159 const HostResolver::RequestInfo& resolve_info,
160 int priority,
161 ClientSocketHandle* handle,
162 CompletionCallback* callback) {
163 DCHECK(!resolve_info.hostname().empty());
164 DCHECK_GE(priority, 0);
165 Group& group = group_map_[group_name];
166
167 CheckSocketCounts(group);
168
169 // Can we make another active socket now?
170 if (group.active_socket_count == max_sockets_per_group_) {
171 CHECK(callback);
172 Request r(handle, callback, priority, resolve_info, LOAD_STATE_IDLE);
173 InsertRequestIntoQueue(r, &group.pending_requests);
174 return ERR_IO_PENDING;
175 }
176
177 // OK, we are going to activate one.
178 group.active_socket_count++;
179
180 while (!group.idle_sockets.empty()) {
181 IdleSocket idle_socket = group.idle_sockets.back();
182 group.idle_sockets.pop_back();
183 DecrementIdleCount();
184 if (idle_socket.socket->IsConnectedAndIdle()) {
185 // We found one we can reuse!
186 handle->set_socket(idle_socket.socket);
187 handle->set_is_reused(true);
188 group.sockets_handed_out_count++;
189 CheckSocketCounts(group);
190 return OK;
191 }
192 delete idle_socket.socket;
193 }
194
195 // We couldn't find a socket to reuse, so allocate and connect a new one.
196
197 CHECK(callback);
198 Request r(handle, callback, priority, resolve_info,
199 LOAD_STATE_RESOLVING_HOST);
200 group.connecting_requests[handle] = r;
201
202 CHECK(!ContainsKey(connecting_socket_map_, handle));
203
204 ConnectingSocket* connecting_socket =
205 connecting_socket_factory_->NewConnectingSocket(group_name, r, this);
206 connecting_socket_map_[handle] = connecting_socket;
207 int rv = connecting_socket->Connect();
208
209 CheckSocketCounts(group);
210 return rv;
211 }
212
213 void ClientSocketPoolBase::CancelRequest(const std::string& group_name,
214 const ClientSocketHandle* handle) {
215 CHECK(ContainsKey(group_map_, group_name));
216
217 Group& group = group_map_[group_name];
218
219 CheckSocketCounts(group);
220
221 // Search pending_requests for matching handle.
222 RequestQueue::iterator it = group.pending_requests.begin();
223 for (; it != group.pending_requests.end(); ++it) {
224 if (it->handle == handle) {
225 group.pending_requests.erase(it);
226 return;
227 }
228 }
229
230 // It's invalid to cancel a non-existent request.
231 CHECK(ContainsKey(group.connecting_requests, handle));
232
233 RequestMap::iterator map_it = group.connecting_requests.find(handle);
234 if (map_it != group.connecting_requests.end()) {
235 RemoveConnectingSocket(handle);
236
237 group.connecting_requests.erase(map_it);
238 group.active_socket_count--;
239
240 if (!group.pending_requests.empty()) {
241 ProcessPendingRequest(group_name, &group);
242 return; // |group| may be invalid after this, so return to be safe.
243 }
244
245 // Delete group if no longer needed.
246 if (group.active_socket_count == 0 && group.idle_sockets.empty()) {
247 CHECK(group.pending_requests.empty());
248 CHECK(group.connecting_requests.empty());
249 group_map_.erase(group_name);
250 return; // |group| is invalid after this, so return to be safe.
251 }
252 }
253
254 CheckSocketCounts(group);
255 }
256
257 void ClientSocketPoolBase::ReleaseSocket(const std::string& group_name,
258 ClientSocket* socket) {
259 // Run this asynchronously to allow the caller to finish before we let
260 // another to begin doing work. This also avoids nasty recursion issues.
261 // NOTE: We cannot refer to the handle argument after this method returns.
262 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
263 this, &ClientSocketPoolBase::DoReleaseSocket, group_name, socket));
264 }
265
266 void ClientSocketPoolBase::CloseIdleSockets() {
267 CleanupIdleSockets(true);
268 }
269
270 int ClientSocketPoolBase::IdleSocketCountInGroup(
271 const std::string& group_name) const {
272 GroupMap::const_iterator i = group_map_.find(group_name);
273 CHECK(i != group_map_.end());
274
275 return i->second.idle_sockets.size();
276 }
277
278 LoadState ClientSocketPoolBase::GetLoadState(
279 const std::string& group_name,
280 const ClientSocketHandle* handle) const {
281 if (!ContainsKey(group_map_, group_name)) {
282 NOTREACHED() << "ClientSocketPool does not contain group: " << group_name
283 << " for handle: " << handle;
284 return LOAD_STATE_IDLE;
285 }
286
287 // Can't use operator[] since it is non-const.
288 const Group& group = group_map_.find(group_name)->second;
289
290 // Search connecting_requests for matching handle.
291 RequestMap::const_iterator map_it = group.connecting_requests.find(handle);
292 if (map_it != group.connecting_requests.end()) {
293 const LoadState load_state = map_it->second.load_state;
294 CHECK(load_state == LOAD_STATE_RESOLVING_HOST ||
295 load_state == LOAD_STATE_CONNECTING);
296 return load_state;
297 }
298
299 // Search pending_requests for matching handle.
300 RequestQueue::const_iterator it = group.pending_requests.begin();
301 for (; it != group.pending_requests.end(); ++it) {
302 if (it->handle == handle) {
303 CHECK(LOAD_STATE_IDLE == it->load_state);
304 // TODO(wtc): Add a state for being on the wait list.
305 // See http://www.crbug.com/5077.
306 return LOAD_STATE_IDLE;
307 }
308 }
309
310 NOTREACHED();
311 return LOAD_STATE_IDLE;
312 }
313
314 bool ClientSocketPoolBase::IdleSocket::ShouldCleanup(
315 base::TimeTicks now) const {
316 bool timed_out = (now - start_time) >=
317 base::TimeDelta::FromSeconds(kIdleTimeout);
318 return timed_out || !socket->IsConnectedAndIdle();
319 }
320
321 void ClientSocketPoolBase::CleanupIdleSockets(bool force) {
322 if (idle_socket_count_ == 0)
323 return;
324
325 // Current time value. Retrieving it once at the function start rather than
326 // inside the inner loop, since it shouldn't change by any meaningful amount.
327 base::TimeTicks now = base::TimeTicks::Now();
328
329 GroupMap::iterator i = group_map_.begin();
330 while (i != group_map_.end()) {
331 Group& group = i->second;
332
333 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
334 while (j != group.idle_sockets.end()) {
335 if (force || j->ShouldCleanup(now)) {
336 delete j->socket;
337 j = group.idle_sockets.erase(j);
338 DecrementIdleCount();
339 } else {
340 ++j;
341 }
342 }
343
344 // Delete group if no longer needed.
345 if (group.active_socket_count == 0 && group.idle_sockets.empty()) {
346 CHECK(group.pending_requests.empty());
347 CHECK(group.connecting_requests.empty());
348 group_map_.erase(i++);
349 } else {
350 ++i;
351 }
352 }
353 }
354
355 void ClientSocketPoolBase::IncrementIdleCount() {
356 if (++idle_socket_count_ == 1)
357 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this,
358 &ClientSocketPoolBase::OnCleanupTimerFired);
359 }
360
361 void ClientSocketPoolBase::DecrementIdleCount() {
362 if (--idle_socket_count_ == 0)
363 timer_.Stop();
364 }
365
366 void ClientSocketPoolBase::DoReleaseSocket(const std::string& group_name,
367 ClientSocket* socket) {
368 GroupMap::iterator i = group_map_.find(group_name);
369 CHECK(i != group_map_.end());
370
371 Group& group = i->second;
372
373 CHECK(group.active_socket_count > 0);
374 CheckSocketCounts(group);
375
376 group.active_socket_count--;
377 group.sockets_handed_out_count--;
378
379 const bool can_reuse = socket->IsConnectedAndIdle();
380 if (can_reuse) {
381 IdleSocket idle_socket;
382 idle_socket.socket = socket;
383 idle_socket.start_time = base::TimeTicks::Now();
384
385 group.idle_sockets.push_back(idle_socket);
386 IncrementIdleCount();
387 } else {
388 delete socket;
389 }
390
391 // Process one pending request.
392 if (!group.pending_requests.empty()) {
393 ProcessPendingRequest(group_name, &group);
394 return;
395 }
396
397 // Delete group if no longer needed.
398 if (group.active_socket_count == 0 && group.idle_sockets.empty()) {
399 CHECK(group.pending_requests.empty());
400 CHECK(group.connecting_requests.empty());
401 group_map_.erase(i);
402 } else {
403 CheckSocketCounts(group);
404 }
405 }
406
407 ClientSocketPoolBase::Request* ClientSocketPoolBase::GetConnectingRequest(
408 const std::string& group_name, const ClientSocketHandle* handle) {
409 GroupMap::iterator group_it = group_map_.find(group_name);
410 if (group_it == group_map_.end())
411 return NULL;
412
413 Group& group = group_it->second;
414
415 RequestMap* request_map = &group.connecting_requests;
416 RequestMap::iterator it = request_map->find(handle);
417 if (it == request_map->end())
418 return NULL;
419
420 return &it->second;
421 }
422
423 CompletionCallback* ClientSocketPoolBase::OnConnectingRequestComplete(
424 const std::string& group_name,
425 const ClientSocketHandle* handle,
426 bool deactivate,
427 ClientSocket* socket) {
428 CHECK((deactivate && !socket) || (!deactivate && socket));
429 GroupMap::iterator group_it = group_map_.find(group_name);
430 CHECK(group_it != group_map_.end());
431 Group& group = group_it->second;
432
433 CheckSocketCounts(group);
434
435 RequestMap* request_map = &group.connecting_requests;
436
437 RequestMap::iterator it = request_map->find(handle);
438 CHECK(it != request_map->end());
439 Request request = it->second;
440 request_map->erase(it);
441 DCHECK_EQ(request.handle, handle);
442
443 if (deactivate) {
444 group.active_socket_count--;
445
446 // Delete group if no longer needed.
447 if (group.active_socket_count == 0 && group.idle_sockets.empty()) {
448 DCHECK(group.pending_requests.empty());
449 DCHECK(group.connecting_requests.empty());
450 group_map_.erase(group_name);
451 } else {
452 CheckSocketCounts(group);
453 }
454 } else {
455 request.handle->set_socket(socket);
456 request.handle->set_is_reused(false);
457 group.sockets_handed_out_count++;
458
459 CheckSocketCounts(group);
460 }
461
462 RemoveConnectingSocket(request.handle);
463
464 return request.callback;
465 }
466
467 // static
468 void ClientSocketPoolBase::CheckSocketCounts(const Group& group) {
469 CHECK(group.active_socket_count ==
470 group.sockets_handed_out_count +
471 static_cast<int>(group.connecting_requests.size()))
472 << "[active_socket_count: " << group.active_socket_count
473 << " ] [sockets_handed_out_count: " << group.sockets_handed_out_count
474 << " ] [connecting_requests size: " << group.connecting_requests.size();
475 }
476
477 void ClientSocketPoolBase::RemoveConnectingSocket(
478 const ClientSocketHandle* handle) {
479 ConnectingSocketMap::iterator it = connecting_socket_map_.find(handle);
480 CHECK(it != connecting_socket_map_.end());
481 delete it->second;
482 connecting_socket_map_.erase(it);
483 }
484
485 void ClientSocketPoolBase::ProcessPendingRequest(const std::string& group_name,
486 Group* group) {
487 Request r = group->pending_requests.front();
488 group->pending_requests.pop_front();
489
490 int rv = RequestSocket(
491 group_name, r.resolve_info, r.priority, r.handle, r.callback);
492
493 // |group| may be invalid after RequestSocket.
494
495 if (rv != ERR_IO_PENDING)
496 r.callback->Run(rv);
497 }
498
499 ConnectingSocket*
500 TCPClientSocketPool::TCPConnectingSocketFactory::NewConnectingSocket(
501 const std::string& group_name,
502 const ClientSocketPoolBase::Request& request,
503 ClientSocketPoolBase* pool) const {
504 return new TCPConnectingSocket(
505 group_name, request.resolve_info, request.handle,
506 client_socket_factory_, pool);
507 }
508
509 TCPClientSocketPool::TCPClientSocketPool(
510 int max_sockets_per_group,
511 HostResolver* host_resolver,
512 ClientSocketFactory* client_socket_factory)
513 : base_(new ClientSocketPoolBase(
514 max_sockets_per_group, host_resolver,
515 new TCPConnectingSocketFactory(client_socket_factory))) {}
516
517 TCPClientSocketPool::~TCPClientSocketPool() {}
518
519 int TCPClientSocketPool::RequestSocket(
520 const std::string& group_name,
521 const HostResolver::RequestInfo& resolve_info,
522 int priority,
523 ClientSocketHandle* handle,
524 CompletionCallback* callback) {
525 return base_->RequestSocket(
526 group_name, resolve_info, priority, handle, callback);
527 }
528
529 void TCPClientSocketPool::CancelRequest(
530 const std::string& group_name,
531 const ClientSocketHandle* handle) {
532 base_->CancelRequest(group_name, handle);
533 }
534
535 void TCPClientSocketPool::ReleaseSocket(
536 const std::string& group_name,
537 ClientSocket* socket) {
538 base_->ReleaseSocket(group_name, socket);
539 }
540
541 void TCPClientSocketPool::CloseIdleSockets() {
542 base_->CloseIdleSockets();
543 }
544
545 int TCPClientSocketPool::IdleSocketCountInGroup(
546 const std::string& group_name) const {
547 return base_->IdleSocketCountInGroup(group_name);
548 }
549
550 LoadState TCPClientSocketPool::GetLoadState(
551 const std::string& group_name, const ClientSocketHandle* handle) const {
552 return base_->GetLoadState(group_name, handle);
553 }
554
555 } // namespace net
OLDNEW
« no previous file with comments | « net/base/tcp_client_socket_pool.h ('k') | net/base/tcp_client_socket_pool_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698