1 // Copyright (c) 2011 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/format_macros.h" 9 #include "base/message_loop.h" 10 #include "base/metrics/stats_counters.h" 11 #include "base/stl_util-inl.h" 12 #include "base/string_util.h" 13 #include "base/time.h" 14 #include "base/values.h" 15 #include "net/base/net_log.h" 16 #include "net/base/net_errors.h" 17 #include "net/socket/client_socket_handle.h" 18 19 using base::TimeDelta; 20 21 namespace { 22 23 // Indicate whether we should enable idle socket cleanup timer. When timer is 24 // disabled, sockets are closed next time a socket request is made. 25 #ifdef ANDROID 26 bool g_cleanup_timer_enabled = false; 27 #else 28 bool g_cleanup_timer_enabled = true; 29 #endif 30 31 // The timeout value, in seconds, used to clean up idle sockets that can't be 32 // reused. 33 // 34 // Note: It's important to close idle sockets that have received data as soon 35 // as possible because the received data may cause BSOD on Windows XP under 36 // some conditions. See http://crbug.com/4606. 37 const int kCleanupInterval = 10; // DO NOT INCREASE THIS TIMEOUT. 38 39 // Indicate whether or not we should establish a new transport layer connection 40 // after a certain timeout has passed without receiving an ACK. 41 bool g_connect_backup_jobs_enabled = true; 42 43 } // namespace 44 45 namespace net { 46 47 ConnectJob::ConnectJob(const std::string& group_name, 48 base::TimeDelta timeout_duration, 49 Delegate* delegate, 50 const BoundNetLog& net_log) 51 : group_name_(group_name), 52 timeout_duration_(timeout_duration), 53 delegate_(delegate), 54 net_log_(net_log), 55 idle_(true), 56 preconnect_state_(NOT_PRECONNECT) { 57 DCHECK(!group_name.empty()); 58 DCHECK(delegate); 59 net_log.BeginEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB, NULL); 60 } 61 62 ConnectJob::~ConnectJob() { 63 net_log().EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB, NULL); 64 } 65 66 void ConnectJob::Initialize(bool is_preconnect) { 67 if (is_preconnect) 68 preconnect_state_ = UNUSED_PRECONNECT; 69 else 70 preconnect_state_ = NOT_PRECONNECT; 71 } 72 73 int ConnectJob::Connect() { 74 if (timeout_duration_ != base::TimeDelta()) 75 timer_.Start(timeout_duration_, this, &ConnectJob::OnTimeout); 76 77 idle_ = false; 78 79 LogConnectStart(); 80 81 int rv = ConnectInternal(); 82 83 if (rv != ERR_IO_PENDING) { 84 LogConnectCompletion(rv); 85 delegate_ = NULL; 86 } 87 88 return rv; 89 } 90 91 void ConnectJob::UseForNormalRequest() { 92 DCHECK_EQ(UNUSED_PRECONNECT, preconnect_state_); 93 preconnect_state_ = USED_PRECONNECT; 94 } 95 96 void ConnectJob::set_socket(ClientSocket* socket) { 97 if (socket) { 98 net_log().AddEvent(NetLog::TYPE_CONNECT_JOB_SET_SOCKET, make_scoped_refptr( 99 new NetLogSourceParameter("source_dependency", 100 socket->NetLog().source()))); 101 } 102 socket_.reset(socket); 103 } 104 105 void ConnectJob::NotifyDelegateOfCompletion(int rv) { 106 // The delegate will delete |this|. 107 Delegate *delegate = delegate_; 108 delegate_ = NULL; 109 110 LogConnectCompletion(rv); 111 delegate->OnConnectJobComplete(rv, this); 112 } 113 114 void ConnectJob::ResetTimer(base::TimeDelta remaining_time) { 115 timer_.Stop(); 116 timer_.Start(remaining_time, this, &ConnectJob::OnTimeout); 117 } 118 119 void ConnectJob::LogConnectStart() { 120 net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_CONNECT, 121 make_scoped_refptr(new NetLogStringParameter("group_name", group_name_))); 122 } 123 124 void ConnectJob::LogConnectCompletion(int net_error) { 125 net_log().EndEventWithNetErrorCode( 126 NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_CONNECT, net_error); 127 } 128 129 void ConnectJob::OnTimeout() { 130 // Make sure the socket is NULL before calling into |delegate|. 131 set_socket(NULL); 132 133 net_log_.AddEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_TIMED_OUT, NULL); 134 135 NotifyDelegateOfCompletion(ERR_TIMED_OUT); 136 } 137 138 namespace internal { 139 140 ClientSocketPoolBaseHelper::Request::Request( 141 ClientSocketHandle* handle, 142 CompletionCallback* callback, 143 RequestPriority priority, 144 bool ignore_limits, 145 Flags flags, 146 const BoundNetLog& net_log 147 #ifdef ANDROID 148 , bool valid_uid, uid_t calling_uid 149 #endif 150 ) 151 : handle_(handle), 152 callback_(callback), 153 priority_(priority), 154 ignore_limits_(ignore_limits), 155 flags_(flags), 156 net_log_(net_log) 157 #ifdef ANDROID 158 , valid_uid_(valid_uid), calling_uid_(calling_uid) 159 #endif 160 {} 161 162 ClientSocketPoolBaseHelper::Request::~Request() {} 163 164 ClientSocketPoolBaseHelper::ClientSocketPoolBaseHelper( 165 int max_sockets, 166 int max_sockets_per_group, 167 base::TimeDelta unused_idle_socket_timeout, 168 base::TimeDelta used_idle_socket_timeout, 169 ConnectJobFactory* connect_job_factory) 170 : idle_socket_count_(0), 171 connecting_socket_count_(0), 172 handed_out_socket_count_(0), 173 max_sockets_(max_sockets), 174 max_sockets_per_group_(max_sockets_per_group), 175 use_cleanup_timer_(g_cleanup_timer_enabled), 176 unused_idle_socket_timeout_(unused_idle_socket_timeout), 177 used_idle_socket_timeout_(used_idle_socket_timeout), 178 connect_job_factory_(connect_job_factory), 179 connect_backup_jobs_enabled_(false), 180 pool_generation_number_(0), 181 method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { 182 DCHECK_LE(0, max_sockets_per_group); 183 DCHECK_LE(max_sockets_per_group, max_sockets); 184 185 NetworkChangeNotifier::AddIPAddressObserver(this); 186 } 187 188 ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() { 189 // Clean up any idle sockets and pending connect jobs. Assert that we have no 190 // remaining active sockets or pending requests. They should have all been 191 // cleaned up prior to |this| being destroyed. 192 Flush(); 193 DCHECK(group_map_.empty()); 194 DCHECK(pending_callback_map_.empty()); 195 DCHECK_EQ(0, connecting_socket_count_); 196 197 NetworkChangeNotifier::RemoveIPAddressObserver(this); 198 } 199 200 // InsertRequestIntoQueue inserts the request into the queue based on 201 // priority. Highest priorities are closest to the front. Older requests are 202 // prioritized over requests of equal priority. 203 // 204 // static 205 void ClientSocketPoolBaseHelper::InsertRequestIntoQueue( 206 const Request* r, RequestQueue* pending_requests) { 207 RequestQueue::iterator it = pending_requests->begin(); 208 while (it != pending_requests->end() && r->priority() >= (*it)->priority()) 209 ++it; 210 pending_requests->insert(it, r); 211 } 212 213 // static 214 const ClientSocketPoolBaseHelper::Request* 215 ClientSocketPoolBaseHelper::RemoveRequestFromQueue( 216 const RequestQueue::iterator& it, Group* group) { 217 const Request* req = *it; 218 group->mutable_pending_requests()->erase(it); 219 // If there are no more requests, we kill the backup timer. 220 if (group->pending_requests().empty()) 221 group->CleanupBackupJob(); 222 return req; 223 } 224 225 int ClientSocketPoolBaseHelper::RequestSocket( 226 const std::string& group_name, 227 const Request* request) { 228 CHECK(request->callback()); 229 CHECK(request->handle()); 230 231 // Cleanup any timed-out idle sockets if no timer is used. 232 if (!use_cleanup_timer_) 233 CleanupIdleSockets(false); 234 235 request->net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL, NULL); 236 Group* group = GetOrCreateGroup(group_name); 237 238 int rv = RequestSocketInternal(group_name, request); 239 if (rv != ERR_IO_PENDING) { 240 request->net_log().EndEventWithNetErrorCode(NetLog::TYPE_SOCKET_POOL, rv); 241 CHECK(!request->handle()->is_initialized()); 242 delete request; 243 } else { 244 InsertRequestIntoQueue(request, group->mutable_pending_requests()); 245 } 246 return rv; 247 } 248 249 void ClientSocketPoolBaseHelper::RequestSockets( 250 const std::string& group_name, 251 const Request& request, 252 int num_sockets) { 253 DCHECK(!request.callback()); 254 DCHECK(!request.handle()); 255 256 // Cleanup any timed out idle sockets if no timer is used. 257 if (!use_cleanup_timer_) 258 CleanupIdleSockets(false); 259 260 if (num_sockets > max_sockets_per_group_) { 261 num_sockets = max_sockets_per_group_; 262 } 263 264 request.net_log().BeginEvent( 265 NetLog::TYPE_SOCKET_POOL_CONNECTING_N_SOCKETS, 266 make_scoped_refptr(new NetLogIntegerParameter( 267 "num_sockets", num_sockets))); 268 269 Group* group = GetOrCreateGroup(group_name); 270 271 // RequestSocketsInternal() may delete the group. 272 bool deleted_group = false; 273 274 int rv = OK; 275 for (int num_iterations_left = num_sockets; 276 group->NumActiveSocketSlots() < num_sockets && 277 num_iterations_left > 0 ; num_iterations_left--) { 278 rv = RequestSocketInternal(group_name, &request); 279 if (rv < 0 && rv != ERR_IO_PENDING) { 280 // We're encountering a synchronous error. Give up. 281 if (!ContainsKey(group_map_, group_name)) 282 deleted_group = true; 283 break; 284 } 285 if (!ContainsKey(group_map_, group_name)) { 286 // Unexpected. The group should only be getting deleted on synchronous 287 // error. 288 NOTREACHED(); 289 deleted_group = true; 290 break; 291 } 292 } 293 294 if (!deleted_group && group->IsEmpty()) 295 RemoveGroup(group_name); 296 297 if (rv == ERR_IO_PENDING) 298 rv = OK; 299 request.net_log().EndEventWithNetErrorCode( 300 NetLog::TYPE_SOCKET_POOL_CONNECTING_N_SOCKETS, rv); 301 } 302 303 int ClientSocketPoolBaseHelper::RequestSocketInternal( 304 const std::string& group_name, 305 const Request* request) { 306 DCHECK_GE(request->priority(), 0); 307 ClientSocketHandle* const handle = request->handle(); 308 const bool preconnecting = !handle; 309 Group* group = GetOrCreateGroup(group_name); 310 311 if (!(request->flags() & NO_IDLE_SOCKETS)) { 312 // Try to reuse a socket. 313 if (AssignIdleSocketToGroup(request, group)) 314 return OK; 315 } 316 317 if (!preconnecting && group->TryToUsePreconnectConnectJob()) 318 return ERR_IO_PENDING; 319 320 // Can we make another active socket now? 321 if (!group->HasAvailableSocketSlot(max_sockets_per_group_) && 322 !request->ignore_limits()) { 323 request->net_log().AddEvent( 324 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP, NULL); 325 return ERR_IO_PENDING; 326 } 327 328 if (ReachedMaxSocketsLimit() && !request->ignore_limits()) { 329 if (idle_socket_count() > 0) { 330 bool closed = CloseOneIdleSocketExceptInGroup(group); 331 if (preconnecting && !closed) 332 return ERR_PRECONNECT_MAX_SOCKET_LIMIT; 333 } else { 334 // We could check if we really have a stalled group here, but it requires 335 // a scan of all groups, so just flip a flag here, and do the check later. 336 request->net_log().AddEvent( 337 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL); 338 return ERR_IO_PENDING; 339 } 340 } 341 342 // We couldn't find a socket to reuse, so allocate and connect a new one. 343 scoped_ptr<ConnectJob> connect_job( 344 connect_job_factory_->NewConnectJob(group_name, *request, this)); 345 346 connect_job->Initialize(preconnecting); 347 int rv = connect_job->Connect(); 348 if (rv == OK) { 349 LogBoundConnectJobToRequest(connect_job->net_log().source(), request); 350 if (!preconnecting) { 351 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */, 352 handle, base::TimeDelta(), group, request->net_log()); 353 } else { 354 AddIdleSocket(connect_job->ReleaseSocket(), group); 355 } 356 } else if (rv == ERR_IO_PENDING) { 357 // If we don't have any sockets in this group, set a timer for potentially 358 // creating a new one. If the SYN is lost, this backup socket may complete 359 // before the slow socket, improving end user latency. 360 if (connect_backup_jobs_enabled_ && 361 group->IsEmpty() && !group->HasBackupJob() && 362 handle) { 363 group->StartBackupSocketTimer(group_name, this); 364 } 365 366 connecting_socket_count_++; 367 368 group->AddJob(connect_job.release()); 369 } else { 370 LogBoundConnectJobToRequest(connect_job->net_log().source(), request); 371 ClientSocket* error_socket = NULL; 372 if (!preconnecting) { 373 DCHECK(handle); 374 connect_job->GetAdditionalErrorState(handle); 375 error_socket = connect_job->ReleaseSocket(); 376 } 377 if (error_socket) { 378 HandOutSocket(error_socket, false /* not reused */, handle, 379 base::TimeDelta(), group, request->net_log()); 380 } else if (group->IsEmpty()) { 381 RemoveGroup(group_name); 382 } 383 } 384 385 return rv; 386 } 387 388 bool ClientSocketPoolBaseHelper::AssignIdleSocketToGroup( 389 const Request* request, Group* group) { 390 std::list<IdleSocket>* idle_sockets = group->mutable_idle_sockets(); 391 std::list<IdleSocket>::iterator idle_socket_it = idle_sockets->end(); 392 393 // Iterate through the idle sockets forwards (oldest to newest) 394 // * Delete any disconnected ones. 395 // * If we find a used idle socket, assign to |idle_socket|. At the end, 396 // the |idle_socket_it| will be set to the newest used idle socket. 397 for (std::list<IdleSocket>::iterator it = idle_sockets->begin(); 398 it != idle_sockets->end();) { 399 if (!it->socket->IsConnectedAndIdle()) { 400 DecrementIdleCount(); 401 delete it->socket; 402 it = idle_sockets->erase(it); 403 continue; 404 } 405 406 if (it->socket->WasEverUsed()) { 407 // We found one we can reuse! 408 idle_socket_it = it; 409 } 410 411 ++it; 412 } 413 414 // If we haven't found an idle socket, that means there are no used idle 415 // sockets. Pick the oldest (first) idle socket (FIFO). 416 417 if (idle_socket_it == idle_sockets->end() && !idle_sockets->empty()) 418 idle_socket_it = idle_sockets->begin(); 419 420 if (idle_socket_it != idle_sockets->end()) { 421 DecrementIdleCount(); 422 base::TimeDelta idle_time = 423 base::TimeTicks::Now() - idle_socket_it->start_time; 424 IdleSocket idle_socket = *idle_socket_it; 425 idle_sockets->erase(idle_socket_it); 426 HandOutSocket( 427 idle_socket.socket, 428 idle_socket.socket->WasEverUsed(), 429 request->handle(), 430 idle_time, 431 group, 432 request->net_log()); 433 return true; 434 } 435 436 return false; 437 } 438 439 // static 440 void ClientSocketPoolBaseHelper::LogBoundConnectJobToRequest( 441 const NetLog::Source& connect_job_source, const Request* request) { 442 request->net_log().AddEvent( 443 NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB, 444 make_scoped_refptr(new NetLogSourceParameter( 445 "source_dependency", connect_job_source))); 446 } 447 448 void ClientSocketPoolBaseHelper::CancelRequest( 449 const std::string& group_name, ClientSocketHandle* handle) { 450 PendingCallbackMap::iterator callback_it = pending_callback_map_.find(handle); 451 if (callback_it != pending_callback_map_.end()) { 452 int result = callback_it->second.result; 453 pending_callback_map_.erase(callback_it); 454 ClientSocket* socket = handle->release_socket(); 455 if (socket) { 456 if (result != OK) 457 socket->Disconnect(); 458 ReleaseSocket(handle->group_name(), socket, handle->id()); 459 } 460 return; 461 } 462 463 CHECK(ContainsKey(group_map_, group_name)); 464 465 Group* group = GetOrCreateGroup(group_name); 466 467 // Search pending_requests for matching handle. 468 RequestQueue::iterator it = group->mutable_pending_requests()->begin(); 469 for (; it != group->pending_requests().end(); ++it) { 470 if ((*it)->handle() == handle) { 471 scoped_ptr<const Request> req(RemoveRequestFromQueue(it, group)); 472 req->net_log().AddEvent(NetLog::TYPE_CANCELLED, NULL); 473 req->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL); 474 475 // We let the job run, unless we're at the socket limit. 476 if (group->jobs().size() && ReachedMaxSocketsLimit()) { 477 RemoveConnectJob(*group->jobs().begin(), group); 478 CheckForStalledSocketGroups(); 479 } 480 break; 481 } 482 } 483 } 484 485 bool ClientSocketPoolBaseHelper::HasGroup(const std::string& group_name) const { 486 return ContainsKey(group_map_, group_name); 487 } 488 489 void ClientSocketPoolBaseHelper::CloseIdleSockets() { 490 CleanupIdleSockets(true); 491 DCHECK_EQ(0, idle_socket_count_); 492 } 493 494 int ClientSocketPoolBaseHelper::IdleSocketCountInGroup( 495 const std::string& group_name) const { 496 GroupMap::const_iterator i = group_map_.find(group_name); 497 CHECK(i != group_map_.end()); 498 499 return i->second->idle_sockets().size(); 500 } 501 502 LoadState ClientSocketPoolBaseHelper::GetLoadState( 503 const std::string& group_name, 504 const ClientSocketHandle* handle) const { 505 if (ContainsKey(pending_callback_map_, handle)) 506 return LOAD_STATE_CONNECTING; 507 508 if (!ContainsKey(group_map_, group_name)) { 509 NOTREACHED() << "ClientSocketPool does not contain group: " << group_name 510 << " for handle: " << handle; 511 return LOAD_STATE_IDLE; 512 } 513 514 // Can't use operator[] since it is non-const. 515 const Group& group = *group_map_.find(group_name)->second; 516 517 // Search pending_requests for matching handle. 518 RequestQueue::const_iterator it = group.pending_requests().begin(); 519 for (size_t i = 0; it != group.pending_requests().end(); ++it, ++i) { 520 if ((*it)->handle() == handle) { 521 if (i < group.jobs().size()) { 522 LoadState max_state = LOAD_STATE_IDLE; 523 for (ConnectJobSet::const_iterator job_it = group.jobs().begin(); 524 job_it != group.jobs().end(); ++job_it) { 525 max_state = std::max(max_state, (*job_it)->GetLoadState()); 526 } 527 return max_state; 528 } else { 529 // TODO(wtc): Add a state for being on the wait list. 530 // See http://www.crbug.com/5077. 531 return LOAD_STATE_IDLE; 532 } 533 } 534 } 535 536 NOTREACHED(); 537 return LOAD_STATE_IDLE; 538 } 539 540 DictionaryValue* ClientSocketPoolBaseHelper::GetInfoAsValue( 541 const std::string& name, const std::string& type) const { 542 DictionaryValue* dict = new DictionaryValue(); 543 dict->SetString("name", name); 544 dict->SetString("type", type); 545 dict->SetInteger("handed_out_socket_count", handed_out_socket_count_); 546 dict->SetInteger("connecting_socket_count", connecting_socket_count_); 547 dict->SetInteger("idle_socket_count", idle_socket_count_); 548 dict->SetInteger("max_socket_count", max_sockets_); 549 dict->SetInteger("max_sockets_per_group", max_sockets_per_group_); 550 dict->SetInteger("pool_generation_number", pool_generation_number_); 551 552 if (group_map_.empty()) 553 return dict; 554 555 DictionaryValue* all_groups_dict = new DictionaryValue(); 556 for (GroupMap::const_iterator it = group_map_.begin(); 557 it != group_map_.end(); it++) { 558 const Group* group = it->second; 559 DictionaryValue* group_dict = new DictionaryValue(); 560 561 group_dict->SetInteger("pending_request_count", 562 group->pending_requests().size()); 563 if (!group->pending_requests().empty()) { 564 group_dict->SetInteger("top_pending_priority", 565 group->TopPendingPriority()); 566 } 567 568 group_dict->SetInteger("active_socket_count", group->active_socket_count()); 569 570 ListValue* idle_socket_list = new ListValue(); 571 std::list<IdleSocket>::const_iterator idle_socket; 572 for (idle_socket = group->idle_sockets().begin(); 573 idle_socket != group->idle_sockets().end(); 574 idle_socket++) { 575 int source_id = idle_socket->socket->NetLog().source().id; 576 idle_socket_list->Append(Value::CreateIntegerValue(source_id)); 577 } 578 group_dict->Set("idle_sockets", idle_socket_list); 579 580 ListValue* connect_jobs_list = new ListValue(); 581 std::set<ConnectJob*>::const_iterator job = group->jobs().begin(); 582 for (job = group->jobs().begin(); job != group->jobs().end(); job++) { 583 int source_id = (*job)->net_log().source().id; 584 connect_jobs_list->Append(Value::CreateIntegerValue(source_id)); 585 } 586 group_dict->Set("connect_jobs", connect_jobs_list); 587 588 group_dict->SetBoolean("is_stalled", 589 group->IsStalled(max_sockets_per_group_)); 590 group_dict->SetBoolean("has_backup_job", group->HasBackupJob()); 591 592 all_groups_dict->SetWithoutPathExpansion(it->first, group_dict); 593 } 594 dict->Set("groups", all_groups_dict); 595 return dict; 596 } 597 598 bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup( 599 base::TimeTicks now, 600 base::TimeDelta timeout) const { 601 bool timed_out = (now - start_time) >= timeout; 602 if (timed_out) 603 return true; 604 if (socket->WasEverUsed()) 605 return !socket->IsConnectedAndIdle(); 606 return !socket->IsConnected(); 607 } 608 609 void ClientSocketPoolBaseHelper::CleanupIdleSockets(bool force) { 610 if (idle_socket_count_ == 0) 611 return; 612 613 // Current time value. Retrieving it once at the function start rather than 614 // inside the inner loop, since it shouldn't change by any meaningful amount. 615 base::TimeTicks now = base::TimeTicks::Now(); 616 617 GroupMap::iterator i = group_map_.begin(); 618 while (i != group_map_.end()) { 619 Group* group = i->second; 620 621 std::list<IdleSocket>::iterator j = group->mutable_idle_sockets()->begin(); 622 while (j != group->idle_sockets().end()) { 623 base::TimeDelta timeout = 624 j->socket->WasEverUsed() ? 625 used_idle_socket_timeout_ : unused_idle_socket_timeout_; 626 if (force || j->ShouldCleanup(now, timeout)) { 627 delete j->socket; 628 j = group->mutable_idle_sockets()->erase(j); 629 DecrementIdleCount(); 630 } else { 631 ++j; 632 } 633 } 634 635 // Delete group if no longer needed. 636 if (group->IsEmpty()) { 637 RemoveGroup(i++); 638 } else { 639 ++i; 640 } 641 } 642 } 643 644 ClientSocketPoolBaseHelper::Group* ClientSocketPoolBaseHelper::GetOrCreateGroup( 645 const std::string& group_name) { 646 GroupMap::iterator it = group_map_.find(group_name); 647 if (it != group_map_.end()) 648 return it->second; 649 Group* group = new Group; 650 group_map_[group_name] = group; 651 return group; 652 } 653 654 void ClientSocketPoolBaseHelper::RemoveGroup(const std::string& group_name) { 655 GroupMap::iterator it = group_map_.find(group_name); 656 CHECK(it != group_map_.end()); 657 658 RemoveGroup(it); 659 } 660 661 void ClientSocketPoolBaseHelper::RemoveGroup(GroupMap::iterator it) { 662 delete it->second; 663 group_map_.erase(it); 664 } 665 666 // static 667 bool ClientSocketPoolBaseHelper::connect_backup_jobs_enabled() { 668 return g_connect_backup_jobs_enabled; 669 } 670 671 // static 672 bool ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(bool enabled) { 673 bool old_value = g_connect_backup_jobs_enabled; 674 g_connect_backup_jobs_enabled = enabled; 675 return old_value; 676 } 677 678 void ClientSocketPoolBaseHelper::EnableConnectBackupJobs() { 679 connect_backup_jobs_enabled_ = g_connect_backup_jobs_enabled; 680 } 681 682 void ClientSocketPoolBaseHelper::IncrementIdleCount() { 683 if (++idle_socket_count_ == 1) 684 StartIdleSocketTimer(); 685 } 686 687 void ClientSocketPoolBaseHelper::DecrementIdleCount() { 688 if (--idle_socket_count_ == 0) 689 timer_.Stop(); 690 } 691 692 // static 693 bool ClientSocketPoolBaseHelper::cleanup_timer_enabled() { 694 return g_cleanup_timer_enabled; 695 } 696 697 // static 698 bool ClientSocketPoolBaseHelper::set_cleanup_timer_enabled(bool enabled) { 699 bool old_value = g_cleanup_timer_enabled; 700 g_cleanup_timer_enabled = enabled; 701 return old_value; 702 } 703 704 void ClientSocketPoolBaseHelper::StartIdleSocketTimer() { 705 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this, 706 &ClientSocketPoolBaseHelper::OnCleanupTimerFired); 707 } 708 709 void ClientSocketPoolBaseHelper::ReleaseSocket(const std::string& group_name, 710 ClientSocket* socket, 711 int id) { 712 GroupMap::iterator i = group_map_.find(group_name); 713 CHECK(i != group_map_.end()); 714 715 Group* group = i->second; 716 717 CHECK_GT(handed_out_socket_count_, 0); 718 handed_out_socket_count_--; 719 720 CHECK_GT(group->active_socket_count(), 0); 721 group->DecrementActiveSocketCount(); 722 723 const bool can_reuse = socket->IsConnectedAndIdle() && 724 id == pool_generation_number_; 725 if (can_reuse) { 726 // Add it to the idle list. 727 AddIdleSocket(socket, group); 728 OnAvailableSocketSlot(group_name, group); 729 } else { 730 delete socket; 731 } 732 733 CheckForStalledSocketGroups(); 734 } 735 736 void ClientSocketPoolBaseHelper::CheckForStalledSocketGroups() { 737 // If we have idle sockets, see if we can give one to the top-stalled group. 738 std::string top_group_name; 739 Group* top_group = NULL; 740 if (!FindTopStalledGroup(&top_group, &top_group_name)) 741 return; 742 743 if (ReachedMaxSocketsLimit()) { 744 if (idle_socket_count() > 0) { 745 CloseOneIdleSocket(); 746 } else { 747 // We can't activate more sockets since we're already at our global 748 // limit. 749 return; 750 } 751 } 752 753 // Note: we don't loop on waking stalled groups. If the stalled group is at 754 // its limit, may be left with other stalled groups that could be 755 // woken. This isn't optimal, but there is no starvation, so to avoid 756 // the looping we leave it at this. 757 OnAvailableSocketSlot(top_group_name, top_group); 758 } 759 760 // Search for the highest priority pending request, amongst the groups that 761 // are not at the |max_sockets_per_group_| limit. Note: for requests with 762 // the same priority, the winner is based on group hash ordering (and not 763 // insertion order). 764 bool ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group, 765 std::string* group_name) { 766 Group* top_group = NULL; 767 const std::string* top_group_name = NULL; 768 bool has_stalled_group = false; 769 for (GroupMap::iterator i = group_map_.begin(); 770 i != group_map_.end(); ++i) { 771 Group* curr_group = i->second; 772 const RequestQueue& queue = curr_group->pending_requests(); 773 if (queue.empty()) 774 continue; 775 if (curr_group->IsStalled(max_sockets_per_group_)) { 776 has_stalled_group = true; 777 bool has_higher_priority = !top_group || 778 curr_group->TopPendingPriority() < top_group->TopPendingPriority(); 779 if (has_higher_priority) { 780 top_group = curr_group; 781 top_group_name = &i->first; 782 } 783 } 784 } 785 786 if (top_group) { 787 *group = top_group; 788 *group_name = *top_group_name; 789 } 790 return has_stalled_group; 791 } 792 793 void ClientSocketPoolBaseHelper::OnConnectJobComplete( 794 int result, ConnectJob* job) { 795 DCHECK_NE(ERR_IO_PENDING, result); 796 const std::string group_name = job->group_name(); 797 GroupMap::iterator group_it = group_map_.find(group_name); 798 CHECK(group_it != group_map_.end()); 799 Group* group = group_it->second; 800 801 scoped_ptr<ClientSocket> socket(job->ReleaseSocket()); 802 803 BoundNetLog job_log = job->net_log(); 804 805 if (result == OK) { 806 DCHECK(socket.get()); 807 RemoveConnectJob(job, group); 808 if (!group->pending_requests().empty()) { 809 scoped_ptr<const Request> r(RemoveRequestFromQueue( 810 group->mutable_pending_requests()->begin(), group)); 811 LogBoundConnectJobToRequest(job_log.source(), r.get()); 812 HandOutSocket( 813 socket.release(), false /* unused socket */, r->handle(), 814 base::TimeDelta(), group, r->net_log()); 815 r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL); 816 InvokeUserCallbackLater(r->handle(), r->callback(), result); 817 } else { 818 AddIdleSocket(socket.release(), group); 819 OnAvailableSocketSlot(group_name, group); 820 CheckForStalledSocketGroups(); 821 } 822 } else { 823 // If we got a socket, it must contain error information so pass that 824 // up so that the caller can retrieve it. 825 bool handed_out_socket = false; 826 if (!group->pending_requests().empty()) { 827 scoped_ptr<const Request> r(RemoveRequestFromQueue( 828 group->mutable_pending_requests()->begin(), group)); 829 LogBoundConnectJobToRequest(job_log.source(), r.get()); 830 job->GetAdditionalErrorState(r->handle()); 831 RemoveConnectJob(job, group); 832 if (socket.get()) { 833 handed_out_socket = true; 834 HandOutSocket(socket.release(), false /* unused socket */, r->handle(), 835 base::TimeDelta(), group, r->net_log()); 836 } 837 r->net_log().EndEventWithNetErrorCode(NetLog::TYPE_SOCKET_POOL, 838 result); 839 InvokeUserCallbackLater(r->handle(), r->callback(), result); 840 } else { 841 RemoveConnectJob(job, group); 842 } 843 if (!handed_out_socket) { 844 OnAvailableSocketSlot(group_name, group); 845 CheckForStalledSocketGroups(); 846 } 847 } 848 } 849 850 void ClientSocketPoolBaseHelper::OnIPAddressChanged() { 851 Flush(); 852 } 853 854 void ClientSocketPoolBaseHelper::Flush() { 855 pool_generation_number_++; 856 CancelAllConnectJobs(); 857 CloseIdleSockets(); 858 AbortAllRequests(); 859 } 860 861 void ClientSocketPoolBaseHelper::RemoveConnectJob(ConnectJob* job, 862 Group* group) { 863 CHECK_GT(connecting_socket_count_, 0); 864 connecting_socket_count_--; 865 866 DCHECK(group); 867 DCHECK(ContainsKey(group->jobs(), job)); 868 group->RemoveJob(job); 869 870 // If we've got no more jobs for this group, then we no longer need a 871 // backup job either. 872 if (group->jobs().empty()) 873 group->CleanupBackupJob(); 874 875 DCHECK(job); 876 delete job; 877 } 878 879 void ClientSocketPoolBaseHelper::OnAvailableSocketSlot( 880 const std::string& group_name, Group* group) { 881 DCHECK(ContainsKey(group_map_, group_name)); 882 if (group->IsEmpty()) 883 RemoveGroup(group_name); 884 else if (!group->pending_requests().empty()) 885 ProcessPendingRequest(group_name, group); 886 } 887 888 void ClientSocketPoolBaseHelper::ProcessPendingRequest( 889 const std::string& group_name, Group* group) { 890 int rv = RequestSocketInternal(group_name, 891 *group->pending_requests().begin()); 892 if (rv != ERR_IO_PENDING) { 893 scoped_ptr<const Request> request(RemoveRequestFromQueue( 894 group->mutable_pending_requests()->begin(), group)); 895 if (group->IsEmpty()) 896 RemoveGroup(group_name); 897 898 request->net_log().EndEventWithNetErrorCode(NetLog::TYPE_SOCKET_POOL, rv); 899 InvokeUserCallbackLater(request->handle(), request->callback(), rv); 900 } 901 } 902 903 void ClientSocketPoolBaseHelper::HandOutSocket( 904 ClientSocket* socket, 905 bool reused, 906 ClientSocketHandle* handle, 907 base::TimeDelta idle_time, 908 Group* group, 909 const BoundNetLog& net_log) { 910 DCHECK(socket); 911 handle->set_socket(socket); 912 handle->set_is_reused(reused); 913 handle->set_idle_time(idle_time); 914 handle->set_pool_id(pool_generation_number_); 915 916 if (reused) { 917 net_log.AddEvent( 918 NetLog::TYPE_SOCKET_POOL_REUSED_AN_EXISTING_SOCKET, 919 make_scoped_refptr(new NetLogIntegerParameter( 920 "idle_ms", static_cast<int>(idle_time.InMilliseconds())))); 921 } 922 923 net_log.AddEvent(NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET, 924 make_scoped_refptr(new NetLogSourceParameter( 925 "source_dependency", socket->NetLog().source()))); 926 927 handed_out_socket_count_++; 928 group->IncrementActiveSocketCount(); 929 } 930 931 void ClientSocketPoolBaseHelper::AddIdleSocket( 932 ClientSocket* socket, Group* group) { 933 DCHECK(socket); 934 IdleSocket idle_socket; 935 idle_socket.socket = socket; 936 idle_socket.start_time = base::TimeTicks::Now(); 937 938 group->mutable_idle_sockets()->push_back(idle_socket); 939 IncrementIdleCount(); 940 } 941 942 void ClientSocketPoolBaseHelper::CancelAllConnectJobs() { 943 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end();) { 944 Group* group = i->second; 945 connecting_socket_count_ -= group->jobs().size(); 946 group->RemoveAllJobs(); 947 948 // Delete group if no longer needed. 949 if (group->IsEmpty()) { 950 // RemoveGroup() will call .erase() which will invalidate the iterator, 951 // but i will already have been incremented to a valid iterator before 952 // RemoveGroup() is called. 953 RemoveGroup(i++); 954 } else { 955 ++i; 956 } 957 } 958 DCHECK_EQ(0, connecting_socket_count_); 959 } 960 961 void ClientSocketPoolBaseHelper::AbortAllRequests() { 962 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end();) { 963 Group* group = i->second; 964 965 RequestQueue pending_requests; 966 pending_requests.swap(*group->mutable_pending_requests()); 967 for (RequestQueue::iterator it2 = pending_requests.begin(); 968 it2 != pending_requests.end(); ++it2) { 969 scoped_ptr<const Request> request(*it2); 970 InvokeUserCallbackLater( 971 request->handle(), request->callback(), ERR_ABORTED); 972 } 973 974 // Delete group if no longer needed. 975 if (group->IsEmpty()) { 976 // RemoveGroup() will call .erase() which will invalidate the iterator, 977 // but i will already have been incremented to a valid iterator before 978 // RemoveGroup() is called. 979 RemoveGroup(i++); 980 } else { 981 ++i; 982 } 983 } 984 } 985 986 bool ClientSocketPoolBaseHelper::ReachedMaxSocketsLimit() const { 987 // Each connecting socket will eventually connect and be handed out. 988 int total = handed_out_socket_count_ + connecting_socket_count_ + 989 idle_socket_count(); 990 // There can be more sockets than the limit since some requests can ignore 991 // the limit 992 if (total < max_sockets_) 993 return false; 994 return true; 995 } 996 997 void ClientSocketPoolBaseHelper::CloseOneIdleSocket() { 998 CloseOneIdleSocketExceptInGroup(NULL); 999 } 1000 1001 bool ClientSocketPoolBaseHelper::CloseOneIdleSocketExceptInGroup( 1002 const Group* exception_group) { 1003 CHECK_GT(idle_socket_count(), 0); 1004 1005 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end(); ++i) { 1006 Group* group = i->second; 1007 if (exception_group == group) 1008 continue; 1009 std::list<IdleSocket>* idle_sockets = group->mutable_idle_sockets(); 1010 1011 if (!idle_sockets->empty()) { 1012 delete idle_sockets->front().socket; 1013 idle_sockets->pop_front(); 1014 DecrementIdleCount(); 1015 if (group->IsEmpty()) 1016 RemoveGroup(i); 1017 1018 return true; 1019 } 1020 } 1021 1022 if (!exception_group) 1023 LOG(DFATAL) << "No idle socket found to close!."; 1024 1025 return false; 1026 } 1027 1028 void ClientSocketPoolBaseHelper::InvokeUserCallbackLater( 1029 ClientSocketHandle* handle, CompletionCallback* callback, int rv) { 1030 CHECK(!ContainsKey(pending_callback_map_, handle)); 1031 pending_callback_map_[handle] = CallbackResultPair(callback, rv); 1032 MessageLoop::current()->PostTask( 1033 FROM_HERE, 1034 method_factory_.NewRunnableMethod( 1035 &ClientSocketPoolBaseHelper::InvokeUserCallback, 1036 handle)); 1037 } 1038 1039 void ClientSocketPoolBaseHelper::InvokeUserCallback( 1040 ClientSocketHandle* handle) { 1041 PendingCallbackMap::iterator it = pending_callback_map_.find(handle); 1042 1043 // Exit if the request has already been cancelled. 1044 if (it == pending_callback_map_.end()) 1045 return; 1046 1047 CHECK(!handle->is_initialized()); 1048 CompletionCallback* callback = it->second.callback; 1049 int result = it->second.result; 1050 pending_callback_map_.erase(it); 1051 callback->Run(result); 1052 } 1053 1054 ClientSocketPoolBaseHelper::Group::Group() 1055 : active_socket_count_(0), 1056 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {} 1057 1058 ClientSocketPoolBaseHelper::Group::~Group() { 1059 CleanupBackupJob(); 1060 } 1061 1062 void ClientSocketPoolBaseHelper::Group::StartBackupSocketTimer( 1063 const std::string& group_name, 1064 ClientSocketPoolBaseHelper* pool) { 1065 // Only allow one timer pending to create a backup socket. 1066 if (!method_factory_.empty()) 1067 return; 1068 1069 MessageLoop::current()->PostDelayedTask( 1070 FROM_HERE, 1071 method_factory_.NewRunnableMethod( 1072 &Group::OnBackupSocketTimerFired, group_name, pool), 1073 pool->ConnectRetryIntervalMs()); 1074 } 1075 1076 bool ClientSocketPoolBaseHelper::Group::TryToUsePreconnectConnectJob() { 1077 for (std::set<ConnectJob*>::iterator it = jobs_.begin(); 1078 it != jobs_.end(); ++it) { 1079 ConnectJob* job = *it; 1080 if (job->is_unused_preconnect()) { 1081 job->UseForNormalRequest(); 1082 return true; 1083 } 1084 } 1085 return false; 1086 } 1087 1088 void ClientSocketPoolBaseHelper::Group::OnBackupSocketTimerFired( 1089 std::string group_name, 1090 ClientSocketPoolBaseHelper* pool) { 1091 // If there are no more jobs pending, there is no work to do. 1092 // If we've done our cleanups correctly, this should not happen. 1093 if (jobs_.empty()) { 1094 NOTREACHED(); 1095 return; 1096 } 1097 1098 // If our backup job is waiting on DNS, or if we can't create any sockets 1099 // right now due to limits, just reset the timer. 1100 if (pool->ReachedMaxSocketsLimit() || 1101 !HasAvailableSocketSlot(pool->max_sockets_per_group_) || 1102 (*jobs_.begin())->GetLoadState() == LOAD_STATE_RESOLVING_HOST) { 1103 StartBackupSocketTimer(group_name, pool); 1104 return; 1105 } 1106 1107 if (pending_requests_.empty()) { 1108 LOG(DFATAL) << "No pending request for backup job."; 1109 return; 1110 } 1111 1112 ConnectJob* backup_job = pool->connect_job_factory_->NewConnectJob( 1113 group_name, **pending_requests_.begin(), pool); 1114 backup_job->net_log().AddEvent(NetLog::TYPE_SOCKET_BACKUP_CREATED, NULL); 1115 SIMPLE_STATS_COUNTER("socket.backup_created"); 1116 int rv = backup_job->Connect(); 1117 pool->connecting_socket_count_++; 1118 AddJob(backup_job); 1119 if (rv != ERR_IO_PENDING) 1120 pool->OnConnectJobComplete(rv, backup_job); 1121 } 1122 1123 void ClientSocketPoolBaseHelper::Group::RemoveAllJobs() { 1124 // Delete active jobs. 1125 STLDeleteElements(&jobs_); 1126 1127 // Cancel pending backup job. 1128 method_factory_.RevokeAll(); 1129 } 1130 1131 } // namespace internal 1132 1133 } // namespace net 1134