| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/socket/client_socket_pool_base.h" | 5 #include "net/socket/client_socket_pool_base.h" |
| 6 | 6 |
| 7 #include "base/compiler_specific.h" | 7 #include "base/compiler_specific.h" |
| 8 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
| 9 #include "base/stl_util-inl.h" | 9 #include "base/stl_util-inl.h" |
| 10 #include "base/time.h" | 10 #include "base/time.h" |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 delegate_(delegate), | 40 delegate_(delegate), |
| 41 load_state_(LOAD_STATE_IDLE) { | 41 load_state_(LOAD_STATE_IDLE) { |
| 42 DCHECK(!group_name.empty()); | 42 DCHECK(!group_name.empty()); |
| 43 DCHECK(key_handle); | 43 DCHECK(key_handle); |
| 44 DCHECK(delegate); | 44 DCHECK(delegate); |
| 45 } | 45 } |
| 46 | 46 |
| 47 ConnectJob::~ConnectJob() {} | 47 ConnectJob::~ConnectJob() {} |
| 48 | 48 |
| 49 ClientSocketPoolBase::ClientSocketPoolBase( | 49 ClientSocketPoolBase::ClientSocketPoolBase( |
| 50 int max_sockets, |
| 50 int max_sockets_per_group, | 51 int max_sockets_per_group, |
| 51 ConnectJobFactory* connect_job_factory) | 52 ConnectJobFactory* connect_job_factory) |
| 52 : idle_socket_count_(0), | 53 : idle_socket_count_(0), |
| 54 connecting_socket_count_(0), |
| 55 handed_out_socket_count_(0), |
| 56 max_sockets_(max_sockets), |
| 53 max_sockets_per_group_(max_sockets_per_group), | 57 max_sockets_per_group_(max_sockets_per_group), |
| 54 connect_job_factory_(connect_job_factory) {} | 58 may_have_stalled_group_(false), |
| 59 connect_job_factory_(connect_job_factory) { |
| 60 DCHECK_LE(0, max_sockets_per_group); |
| 61 DCHECK_LE(max_sockets_per_group, max_sockets); |
| 62 } |
| 55 | 63 |
| 56 ClientSocketPoolBase::~ClientSocketPoolBase() { | 64 ClientSocketPoolBase::~ClientSocketPoolBase() { |
| 57 if (g_late_binding) | 65 if (g_late_binding) |
| 58 CancelAllConnectJobs(); | 66 CancelAllConnectJobs(); |
| 59 // Clean up any idle sockets. Assert that we have no remaining active | 67 // Clean up any idle sockets. Assert that we have no remaining active |
| 60 // sockets or pending requests. They should have all been cleaned up prior | 68 // sockets or pending requests. They should have all been cleaned up prior |
| 61 // to the manager being destroyed. | 69 // to the manager being destroyed. |
| 62 CloseIdleSockets(); | 70 CloseIdleSockets(); |
| 63 DCHECK(group_map_.empty()); | 71 DCHECK(group_map_.empty()); |
| 64 DCHECK(connect_job_map_.empty()); | 72 DCHECK(connect_job_map_.empty()); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 82 const HostResolver::RequestInfo& resolve_info, | 90 const HostResolver::RequestInfo& resolve_info, |
| 83 int priority, | 91 int priority, |
| 84 ClientSocketHandle* handle, | 92 ClientSocketHandle* handle, |
| 85 CompletionCallback* callback) { | 93 CompletionCallback* callback) { |
| 86 DCHECK(!resolve_info.hostname().empty()); | 94 DCHECK(!resolve_info.hostname().empty()); |
| 87 DCHECK_GE(priority, 0); | 95 DCHECK_GE(priority, 0); |
| 88 DCHECK(callback); | 96 DCHECK(callback); |
| 89 Group& group = group_map_[group_name]; | 97 Group& group = group_map_[group_name]; |
| 90 | 98 |
| 91 // Can we make another active socket now? | 99 // Can we make another active socket now? |
| 92 if (!group.HasAvailableSocketSlot(max_sockets_per_group_)) { | 100 if (ReachedMaxSocketsLimit() || |
| 101 !group.HasAvailableSocketSlot(max_sockets_per_group_)) { |
| 102 if (ReachedMaxSocketsLimit()) { |
| 103 // We could check if we really have a stalled group here, but it requires |
| 104 // a scan of all groups, so just flip a flag here, and do the check later. |
| 105 may_have_stalled_group_ = true; |
| 106 } |
| 93 CHECK(callback); | 107 CHECK(callback); |
| 94 Request r(handle, callback, priority, resolve_info); | 108 Request r(handle, callback, priority, resolve_info); |
| 95 InsertRequestIntoQueue(r, &group.pending_requests); | 109 InsertRequestIntoQueue(r, &group.pending_requests); |
| 96 return ERR_IO_PENDING; | 110 return ERR_IO_PENDING; |
| 97 } | 111 } |
| 98 | 112 |
| 99 while (!group.idle_sockets.empty()) { | 113 while (!group.idle_sockets.empty()) { |
| 100 IdleSocket idle_socket = group.idle_sockets.back(); | 114 IdleSocket idle_socket = group.idle_sockets.back(); |
| 101 group.idle_sockets.pop_back(); | 115 group.idle_sockets.pop_back(); |
| 102 DecrementIdleCount(); | 116 DecrementIdleCount(); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 113 CHECK(callback); | 127 CHECK(callback); |
| 114 Request r(handle, callback, priority, resolve_info); | 128 Request r(handle, callback, priority, resolve_info); |
| 115 scoped_ptr<ConnectJob> connect_job( | 129 scoped_ptr<ConnectJob> connect_job( |
| 116 connect_job_factory_->NewConnectJob(group_name, r, this)); | 130 connect_job_factory_->NewConnectJob(group_name, r, this)); |
| 117 | 131 |
| 118 int rv = connect_job->Connect(); | 132 int rv = connect_job->Connect(); |
| 119 if (rv == OK) { | 133 if (rv == OK) { |
| 120 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */, | 134 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */, |
| 121 handle, &group); | 135 handle, &group); |
| 122 } else if (rv == ERR_IO_PENDING) { | 136 } else if (rv == ERR_IO_PENDING) { |
| 137 connecting_socket_count_++; |
| 138 |
| 123 ConnectJob* job = connect_job.release(); | 139 ConnectJob* job = connect_job.release(); |
| 124 if (g_late_binding) { | 140 if (g_late_binding) { |
| 125 CHECK(!ContainsKey(connect_job_map_, handle)); | 141 CHECK(!ContainsKey(connect_job_map_, handle)); |
| 126 InsertRequestIntoQueue(r, &group.pending_requests); | 142 InsertRequestIntoQueue(r, &group.pending_requests); |
| 127 } else { | 143 } else { |
| 128 group.connecting_requests[handle] = r; | 144 group.connecting_requests[handle] = r; |
| 129 CHECK(!ContainsKey(connect_job_map_, handle)); | 145 CHECK(!ContainsKey(connect_job_map_, handle)); |
| 130 connect_job_map_[handle] = job; | 146 connect_job_map_[handle] = job; |
| 131 } | 147 } |
| 132 group.jobs.insert(job); | 148 group.jobs.insert(job); |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 284 timer_.Stop(); | 300 timer_.Stop(); |
| 285 } | 301 } |
| 286 | 302 |
| 287 void ClientSocketPoolBase::DoReleaseSocket(const std::string& group_name, | 303 void ClientSocketPoolBase::DoReleaseSocket(const std::string& group_name, |
| 288 ClientSocket* socket) { | 304 ClientSocket* socket) { |
| 289 GroupMap::iterator i = group_map_.find(group_name); | 305 GroupMap::iterator i = group_map_.find(group_name); |
| 290 CHECK(i != group_map_.end()); | 306 CHECK(i != group_map_.end()); |
| 291 | 307 |
| 292 Group& group = i->second; | 308 Group& group = i->second; |
| 293 | 309 |
| 310 CHECK(handed_out_socket_count_ > 0); |
| 311 handed_out_socket_count_--; |
| 312 |
| 294 CHECK(group.active_socket_count > 0); | 313 CHECK(group.active_socket_count > 0); |
| 295 group.active_socket_count--; | 314 group.active_socket_count--; |
| 296 | 315 |
| 297 const bool can_reuse = socket->IsConnectedAndIdle(); | 316 const bool can_reuse = socket->IsConnectedAndIdle(); |
| 298 if (can_reuse) { | 317 if (can_reuse) { |
| 299 AddIdleSocket(socket, true /* used socket */, &group); | 318 AddIdleSocket(socket, true /* used socket */, &group); |
| 300 } else { | 319 } else { |
| 301 delete socket; | 320 delete socket; |
| 302 } | 321 } |
| 303 | 322 |
| 304 OnAvailableSocketSlot(group_name, &group); | 323 OnAvailableSocketSlot(group_name, &group); |
| 305 } | 324 } |
| 306 | 325 |
| 326 // Search for the highest priority pending request, amongst the groups that |
| 327 // are not at the |max_sockets_per_group_| limit. Note: for requests with |
| 328 // the same priority, the winner is based on group hash ordering (and not |
| 329 // insertion order). |
| 330 int ClientSocketPoolBase::FindTopStalledGroup(Group** group, |
| 331 std::string* group_name) { |
| 332 Group* top_group = NULL; |
| 333 const std::string* top_group_name = NULL; |
| 334 int stalled_group_count = 0; |
| 335 for (GroupMap::iterator i = group_map_.begin(); |
| 336 i != group_map_.end(); ++i) { |
| 337 Group& group = i->second; |
| 338 const RequestQueue& queue = group.pending_requests; |
| 339 if (queue.empty()) |
| 340 continue; |
| 341 bool has_slot = group.HasAvailableSocketSlot(max_sockets_per_group_); |
| 342 if (has_slot) |
| 343 stalled_group_count++; |
| 344 bool has_higher_priority = !top_group || |
| 345 group.TopPendingPriority() > top_group->TopPendingPriority(); |
| 346 if (has_slot && has_higher_priority) { |
| 347 top_group = &group; |
| 348 top_group_name = &i->first; |
| 349 } |
| 350 } |
| 351 if (top_group) { |
| 352 *group = top_group; |
| 353 *group_name = *top_group_name; |
| 354 } |
| 355 return stalled_group_count; |
| 356 } |
| 357 |
| 307 void ClientSocketPoolBase::OnConnectJobComplete(int result, ConnectJob* job) { | 358 void ClientSocketPoolBase::OnConnectJobComplete(int result, ConnectJob* job) { |
| 308 DCHECK_NE(ERR_IO_PENDING, result); | 359 DCHECK_NE(ERR_IO_PENDING, result); |
| 309 const std::string group_name = job->group_name(); | 360 const std::string group_name = job->group_name(); |
| 310 GroupMap::iterator group_it = group_map_.find(group_name); | 361 GroupMap::iterator group_it = group_map_.find(group_name); |
| 311 CHECK(group_it != group_map_.end()); | 362 CHECK(group_it != group_map_.end()); |
| 312 Group& group = group_it->second; | 363 Group& group = group_it->second; |
| 313 | 364 |
| 314 const ClientSocketHandle* const key_handle = job->key_handle(); | 365 const ClientSocketHandle* const key_handle = job->key_handle(); |
| 315 scoped_ptr<ClientSocket> socket(job->ReleaseSocket()); | 366 scoped_ptr<ClientSocket> socket(job->ReleaseSocket()); |
| 316 | 367 |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 362 callback->Run(result); | 413 callback->Run(result); |
| 363 } | 414 } |
| 364 } | 415 } |
| 365 | 416 |
| 366 void ClientSocketPoolBase::EnableLateBindingOfSockets(bool enabled) { | 417 void ClientSocketPoolBase::EnableLateBindingOfSockets(bool enabled) { |
| 367 g_late_binding = enabled; | 418 g_late_binding = enabled; |
| 368 } | 419 } |
| 369 | 420 |
| 370 void ClientSocketPoolBase::RemoveConnectJob( | 421 void ClientSocketPoolBase::RemoveConnectJob( |
| 371 const ClientSocketHandle* handle, ConnectJob *job, Group* group) { | 422 const ClientSocketHandle* handle, ConnectJob *job, Group* group) { |
| 423 CHECK(connecting_socket_count_ > 0); |
| 424 connecting_socket_count_--; |
| 425 |
| 372 if (g_late_binding) { | 426 if (g_late_binding) { |
| 373 DCHECK(job); | 427 DCHECK(job); |
| 374 delete job; | 428 delete job; |
| 375 } else { | 429 } else { |
| 376 ConnectJobMap::iterator it = connect_job_map_.find(handle); | 430 ConnectJobMap::iterator it = connect_job_map_.find(handle); |
| 377 CHECK(it != connect_job_map_.end()); | 431 CHECK(it != connect_job_map_.end()); |
| 378 job = it->second; | 432 job = it->second; |
| 379 delete job; | 433 delete job; |
| 380 connect_job_map_.erase(it); | 434 connect_job_map_.erase(it); |
| 381 group->connecting_requests.erase(handle); | 435 group->connecting_requests.erase(handle); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 392 GroupMap::iterator it = group_map_.find(group_name); | 446 GroupMap::iterator it = group_map_.find(group_name); |
| 393 if (it != group_map_.end()) { | 447 if (it != group_map_.end()) { |
| 394 Group& group = it->second; | 448 Group& group = it->second; |
| 395 if (group.HasAvailableSocketSlot(max_sockets_per_group_)) | 449 if (group.HasAvailableSocketSlot(max_sockets_per_group_)) |
| 396 OnAvailableSocketSlot(group_name, &group); | 450 OnAvailableSocketSlot(group_name, &group); |
| 397 } | 451 } |
| 398 } | 452 } |
| 399 | 453 |
| 400 void ClientSocketPoolBase::OnAvailableSocketSlot(const std::string& group_name, | 454 void ClientSocketPoolBase::OnAvailableSocketSlot(const std::string& group_name, |
| 401 Group* group) { | 455 Group* group) { |
| 402 if (!group->pending_requests.empty()) { | 456 if (may_have_stalled_group_) { |
| 457 std::string top_group_name; |
| 458 Group* top_group; |
| 459 int stalled_group_count = FindTopStalledGroup(&top_group, &top_group_name); |
| 460 if (stalled_group_count <= 1) |
| 461 may_have_stalled_group_ = false; |
| 462 if (stalled_group_count >= 1) |
| 463 ProcessPendingRequest(top_group_name, top_group); |
| 464 } else if (!group->pending_requests.empty()) { |
| 403 ProcessPendingRequest(group_name, group); | 465 ProcessPendingRequest(group_name, group); |
| 404 // |group| may no longer be valid after this point. Be careful not to | 466 // |group| may no longer be valid after this point. Be careful not to |
| 405 // access it again. | 467 // access it again. |
| 406 } else if (group->IsEmpty()) { | 468 } else if (group->IsEmpty()) { |
| 407 // Delete |group| if no longer needed. |group| will no longer be valid. | 469 // Delete |group| if no longer needed. |group| will no longer be valid. |
| 408 group_map_.erase(group_name); | 470 group_map_.erase(group_name); |
| 409 } | 471 } |
| 410 } | 472 } |
| 411 | 473 |
| 412 void ClientSocketPoolBase::ProcessPendingRequest(const std::string& group_name, | 474 void ClientSocketPoolBase::ProcessPendingRequest(const std::string& group_name, |
| (...skipping 15 matching lines...) Expand all Loading... |
| 428 } | 490 } |
| 429 | 491 |
| 430 void ClientSocketPoolBase::HandOutSocket( | 492 void ClientSocketPoolBase::HandOutSocket( |
| 431 ClientSocket* socket, | 493 ClientSocket* socket, |
| 432 bool reused, | 494 bool reused, |
| 433 ClientSocketHandle* handle, | 495 ClientSocketHandle* handle, |
| 434 Group* group) { | 496 Group* group) { |
| 435 DCHECK(socket); | 497 DCHECK(socket); |
| 436 handle->set_socket(socket); | 498 handle->set_socket(socket); |
| 437 handle->set_is_reused(reused); | 499 handle->set_is_reused(reused); |
| 500 |
| 501 handed_out_socket_count_++; |
| 438 group->active_socket_count++; | 502 group->active_socket_count++; |
| 439 } | 503 } |
| 440 | 504 |
| 441 void ClientSocketPoolBase::AddIdleSocket( | 505 void ClientSocketPoolBase::AddIdleSocket( |
| 442 ClientSocket* socket, bool used, Group* group) { | 506 ClientSocket* socket, bool used, Group* group) { |
| 443 DCHECK(socket); | 507 DCHECK(socket); |
| 444 IdleSocket idle_socket; | 508 IdleSocket idle_socket; |
| 445 idle_socket.socket = socket; | 509 idle_socket.socket = socket; |
| 446 idle_socket.start_time = base::TimeTicks::Now(); | 510 idle_socket.start_time = base::TimeTicks::Now(); |
| 447 idle_socket.used = used; | 511 idle_socket.used = used; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 458 // Delete group if no longer needed. | 522 // Delete group if no longer needed. |
| 459 if (group.IsEmpty()) { | 523 if (group.IsEmpty()) { |
| 460 CHECK(group.pending_requests.empty()); | 524 CHECK(group.pending_requests.empty()); |
| 461 group_map_.erase(i++); | 525 group_map_.erase(i++); |
| 462 } else { | 526 } else { |
| 463 ++i; | 527 ++i; |
| 464 } | 528 } |
| 465 } | 529 } |
| 466 } | 530 } |
| 467 | 531 |
| 532 bool ClientSocketPoolBase::ReachedMaxSocketsLimit() const { |
| 533 // Each connecting socket will eventually connect and be handed out. |
| 534 int total = handed_out_socket_count_ + connecting_socket_count_; |
| 535 DCHECK_LE(total, max_sockets_); |
| 536 return total == max_sockets_; |
| 537 } |
| 538 |
| 468 } // namespace net | 539 } // namespace net |
| OLD | NEW |