| 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 12 matching lines...) Expand all Loading... |
| 23 // some conditions. See http://crbug.com/4606. | 23 // some conditions. See http://crbug.com/4606. |
| 24 const int kCleanupInterval = 10; // DO NOT INCREASE THIS TIMEOUT. | 24 const int kCleanupInterval = 10; // DO NOT INCREASE THIS TIMEOUT. |
| 25 | 25 |
| 26 // The maximum duration, in seconds, to keep idle persistent sockets alive. | 26 // The maximum duration, in seconds, to keep idle persistent sockets alive. |
| 27 const int kIdleTimeout = 300; // 5 minutes. | 27 const int kIdleTimeout = 300; // 5 minutes. |
| 28 | 28 |
| 29 } // namespace | 29 } // namespace |
| 30 | 30 |
| 31 namespace net { | 31 namespace net { |
| 32 | 32 |
| 33 ConnectJob::ConnectJob(const std::string& group_name, |
| 34 const ClientSocketHandle* key_handle, |
| 35 Delegate* delegate) |
| 36 : group_name_(group_name), |
| 37 key_handle_(key_handle), |
| 38 delegate_(delegate), |
| 39 load_state_(LOAD_STATE_IDLE) { |
| 40 DCHECK(!group_name.empty()); |
| 41 DCHECK(key_handle); |
| 42 DCHECK(delegate); |
| 43 } |
| 44 |
| 45 ConnectJob::~ConnectJob() {} |
| 46 |
| 33 ClientSocketPoolBase::ClientSocketPoolBase( | 47 ClientSocketPoolBase::ClientSocketPoolBase( |
| 34 int max_sockets_per_group, | 48 int max_sockets_per_group, |
| 35 ConnectJobFactory* connect_job_factory) | 49 ConnectJobFactory* connect_job_factory) |
| 36 : idle_socket_count_(0), | 50 : idle_socket_count_(0), |
| 37 max_sockets_per_group_(max_sockets_per_group), | 51 max_sockets_per_group_(max_sockets_per_group), |
| 38 connect_job_factory_(connect_job_factory) {} | 52 connect_job_factory_(connect_job_factory) {} |
| 39 | 53 |
| 40 ClientSocketPoolBase::~ClientSocketPoolBase() { | 54 ClientSocketPoolBase::~ClientSocketPoolBase() { |
| 41 // Clean up any idle sockets. Assert that we have no remaining active | 55 // Clean up any idle sockets. Assert that we have no remaining active |
| 42 // sockets or pending requests. They should have all been cleaned up prior | 56 // sockets or pending requests. They should have all been cleaned up prior |
| (...skipping 20 matching lines...) Expand all Loading... |
| 63 const std::string& group_name, | 77 const std::string& group_name, |
| 64 const HostResolver::RequestInfo& resolve_info, | 78 const HostResolver::RequestInfo& resolve_info, |
| 65 int priority, | 79 int priority, |
| 66 ClientSocketHandle* handle, | 80 ClientSocketHandle* handle, |
| 67 CompletionCallback* callback) { | 81 CompletionCallback* callback) { |
| 68 DCHECK(!resolve_info.hostname().empty()); | 82 DCHECK(!resolve_info.hostname().empty()); |
| 69 DCHECK_GE(priority, 0); | 83 DCHECK_GE(priority, 0); |
| 70 DCHECK(callback); | 84 DCHECK(callback); |
| 71 Group& group = group_map_[group_name]; | 85 Group& group = group_map_[group_name]; |
| 72 | 86 |
| 73 CheckSocketCounts(group); | |
| 74 | |
| 75 // Can we make another active socket now? | 87 // Can we make another active socket now? |
| 76 if (group.active_socket_count == max_sockets_per_group_) { | 88 if (!group.HasAvailableSocketSlot(max_sockets_per_group_)) { |
| 77 CHECK(callback); | 89 CHECK(callback); |
| 78 Request r(handle, callback, priority, resolve_info); | 90 Request r(handle, callback, priority, resolve_info); |
| 79 InsertRequestIntoQueue(r, &group.pending_requests); | 91 InsertRequestIntoQueue(r, &group.pending_requests); |
| 80 return ERR_IO_PENDING; | 92 return ERR_IO_PENDING; |
| 81 } | 93 } |
| 82 | 94 |
| 83 // OK, we are going to activate one. | |
| 84 group.active_socket_count++; | |
| 85 | |
| 86 while (!group.idle_sockets.empty()) { | 95 while (!group.idle_sockets.empty()) { |
| 87 IdleSocket idle_socket = group.idle_sockets.back(); | 96 IdleSocket idle_socket = group.idle_sockets.back(); |
| 88 group.idle_sockets.pop_back(); | 97 group.idle_sockets.pop_back(); |
| 89 DecrementIdleCount(); | 98 DecrementIdleCount(); |
| 90 if (idle_socket.socket->IsConnectedAndIdle()) { | 99 if (idle_socket.socket->IsConnectedAndIdle()) { |
| 91 // We found one we can reuse! | 100 // We found one we can reuse! |
| 92 handle->set_socket(idle_socket.socket); | 101 HandOutSocket(idle_socket.socket, true /* reuse */, handle, &group); |
| 93 handle->set_is_reused(true); | |
| 94 group.sockets_handed_out_count++; | |
| 95 CheckSocketCounts(group); | |
| 96 return OK; | 102 return OK; |
| 97 } | 103 } |
| 98 delete idle_socket.socket; | 104 delete idle_socket.socket; |
| 99 } | 105 } |
| 100 | 106 |
| 101 // We couldn't find a socket to reuse, so allocate and connect a new one. | 107 // We couldn't find a socket to reuse, so allocate and connect a new one. |
| 102 | 108 |
| 103 CHECK(callback); | 109 CHECK(callback); |
| 104 Request r(handle, callback, priority, resolve_info); | 110 Request r(handle, callback, priority, resolve_info); |
| 105 group.connecting_requests[handle] = r; | 111 scoped_ptr<ConnectJob> connect_job( |
| 112 connect_job_factory_->NewConnectJob(group_name, r, this)); |
| 106 | 113 |
| 107 CHECK(!ContainsKey(connect_job_map_, handle)); | 114 int rv = connect_job->Connect(); |
| 115 if (rv == OK) { |
| 116 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */, |
| 117 handle, &group); |
| 118 } else if (rv == ERR_IO_PENDING) { |
| 119 group.connecting_requests[handle] = r; |
| 120 CHECK(!ContainsKey(connect_job_map_, handle)); |
| 121 connect_job_map_[handle] = connect_job.release(); |
| 122 } else { |
| 123 if (group.IsEmpty()) |
| 124 group_map_.erase(group_name); |
| 125 } |
| 108 | 126 |
| 109 ConnectJob* connect_job = | 127 return rv; |
| 110 connect_job_factory_->NewConnectJob(group_name, r, this); | |
| 111 connect_job_map_[handle] = connect_job; | |
| 112 return connect_job->Connect(); | |
| 113 } | 128 } |
| 114 | 129 |
| 115 void ClientSocketPoolBase::CancelRequest(const std::string& group_name, | 130 void ClientSocketPoolBase::CancelRequest(const std::string& group_name, |
| 116 const ClientSocketHandle* handle) { | 131 const ClientSocketHandle* handle) { |
| 117 CHECK(ContainsKey(group_map_, group_name)); | 132 CHECK(ContainsKey(group_map_, group_name)); |
| 118 | 133 |
| 119 Group& group = group_map_[group_name]; | 134 Group& group = group_map_[group_name]; |
| 120 | 135 |
| 121 CheckSocketCounts(group); | |
| 122 | |
| 123 // Search pending_requests for matching handle. | 136 // Search pending_requests for matching handle. |
| 124 RequestQueue::iterator it = group.pending_requests.begin(); | 137 RequestQueue::iterator it = group.pending_requests.begin(); |
| 125 for (; it != group.pending_requests.end(); ++it) { | 138 for (; it != group.pending_requests.end(); ++it) { |
| 126 if (it->handle == handle) { | 139 if (it->handle == handle) { |
| 127 group.pending_requests.erase(it); | 140 group.pending_requests.erase(it); |
| 128 return; | 141 return; |
| 129 } | 142 } |
| 130 } | 143 } |
| 131 | 144 |
| 132 // It's invalid to cancel a non-existent request. | 145 // It's invalid to cancel a non-existent request. |
| 133 CHECK(ContainsKey(group.connecting_requests, handle)); | 146 CHECK(ContainsKey(group.connecting_requests, handle)); |
| 134 | 147 |
| 135 RequestMap::iterator map_it = group.connecting_requests.find(handle); | 148 RequestMap::iterator map_it = group.connecting_requests.find(handle); |
| 136 if (map_it != group.connecting_requests.end()) { | 149 if (map_it != group.connecting_requests.end()) { |
| 137 RemoveConnectJob(handle); | 150 RemoveConnectJob(handle); |
| 138 group.connecting_requests.erase(map_it); | 151 group.connecting_requests.erase(map_it); |
| 139 RemoveActiveSocket(group_name, &group); | 152 OnAvailableSocketSlot(group_name, &group); |
| 140 } | 153 } |
| 141 } | 154 } |
| 142 | 155 |
| 143 void ClientSocketPoolBase::ReleaseSocket(const std::string& group_name, | 156 void ClientSocketPoolBase::ReleaseSocket(const std::string& group_name, |
| 144 ClientSocket* socket) { | 157 ClientSocket* socket) { |
| 145 // Run this asynchronously to allow the caller to finish before we let | 158 // Run this asynchronously to allow the caller to finish before we let |
| 146 // another to begin doing work. This also avoids nasty recursion issues. | 159 // another to begin doing work. This also avoids nasty recursion issues. |
| 147 // NOTE: We cannot refer to the handle argument after this method returns. | 160 // NOTE: We cannot refer to the handle argument after this method returns. |
| 148 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( | 161 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( |
| 149 this, &ClientSocketPoolBase::DoReleaseSocket, group_name, socket)); | 162 this, &ClientSocketPoolBase::DoReleaseSocket, group_name, socket)); |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 222 if (force || j->ShouldCleanup(now)) { | 235 if (force || j->ShouldCleanup(now)) { |
| 223 delete j->socket; | 236 delete j->socket; |
| 224 j = group.idle_sockets.erase(j); | 237 j = group.idle_sockets.erase(j); |
| 225 DecrementIdleCount(); | 238 DecrementIdleCount(); |
| 226 } else { | 239 } else { |
| 227 ++j; | 240 ++j; |
| 228 } | 241 } |
| 229 } | 242 } |
| 230 | 243 |
| 231 // Delete group if no longer needed. | 244 // Delete group if no longer needed. |
| 232 if (group.active_socket_count == 0 && group.idle_sockets.empty()) { | 245 if (group.IsEmpty()) { |
| 233 CHECK(group.pending_requests.empty()); | 246 CHECK(group.pending_requests.empty()); |
| 234 CHECK(group.connecting_requests.empty()); | |
| 235 group_map_.erase(i++); | 247 group_map_.erase(i++); |
| 236 } else { | 248 } else { |
| 237 ++i; | 249 ++i; |
| 238 } | 250 } |
| 239 } | 251 } |
| 240 } | 252 } |
| 241 | 253 |
| 242 void ClientSocketPoolBase::IncrementIdleCount() { | 254 void ClientSocketPoolBase::IncrementIdleCount() { |
| 243 if (++idle_socket_count_ == 1) | 255 if (++idle_socket_count_ == 1) |
| 244 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this, | 256 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this, |
| 245 &ClientSocketPoolBase::OnCleanupTimerFired); | 257 &ClientSocketPoolBase::OnCleanupTimerFired); |
| 246 } | 258 } |
| 247 | 259 |
| 248 void ClientSocketPoolBase::DecrementIdleCount() { | 260 void ClientSocketPoolBase::DecrementIdleCount() { |
| 249 if (--idle_socket_count_ == 0) | 261 if (--idle_socket_count_ == 0) |
| 250 timer_.Stop(); | 262 timer_.Stop(); |
| 251 } | 263 } |
| 252 | 264 |
| 253 void ClientSocketPoolBase::DoReleaseSocket(const std::string& group_name, | 265 void ClientSocketPoolBase::DoReleaseSocket(const std::string& group_name, |
| 254 ClientSocket* socket) { | 266 ClientSocket* socket) { |
| 255 GroupMap::iterator i = group_map_.find(group_name); | 267 GroupMap::iterator i = group_map_.find(group_name); |
| 256 CHECK(i != group_map_.end()); | 268 CHECK(i != group_map_.end()); |
| 257 | 269 |
| 258 Group& group = i->second; | 270 Group& group = i->second; |
| 259 | 271 |
| 260 CHECK(group.active_socket_count > 0); | 272 CHECK(group.active_socket_count > 0); |
| 261 CheckSocketCounts(group); | 273 group.active_socket_count--; |
| 262 | |
| 263 group.sockets_handed_out_count--; | |
| 264 | 274 |
| 265 const bool can_reuse = socket->IsConnectedAndIdle(); | 275 const bool can_reuse = socket->IsConnectedAndIdle(); |
| 266 if (can_reuse) { | 276 if (can_reuse) { |
| 267 IdleSocket idle_socket; | 277 IdleSocket idle_socket; |
| 268 idle_socket.socket = socket; | 278 idle_socket.socket = socket; |
| 269 idle_socket.start_time = base::TimeTicks::Now(); | 279 idle_socket.start_time = base::TimeTicks::Now(); |
| 270 | 280 |
| 271 group.idle_sockets.push_back(idle_socket); | 281 group.idle_sockets.push_back(idle_socket); |
| 272 IncrementIdleCount(); | 282 IncrementIdleCount(); |
| 273 } else { | 283 } else { |
| 274 delete socket; | 284 delete socket; |
| 275 } | 285 } |
| 276 | 286 |
| 277 RemoveActiveSocket(group_name, &group); | 287 OnAvailableSocketSlot(group_name, &group); |
| 278 } | 288 } |
| 279 | 289 |
| 280 void ClientSocketPoolBase::OnConnectJobComplete( | 290 void ClientSocketPoolBase::OnConnectJobComplete(int result, ConnectJob* job) { |
| 281 const std::string& group_name, | 291 DCHECK_NE(ERR_IO_PENDING, result); |
| 282 const ClientSocketHandle* key_handle, | 292 const std::string group_name = job->group_name(); |
| 283 ClientSocket* socket, | |
| 284 int result, | |
| 285 bool was_async) { | |
| 286 GroupMap::iterator group_it = group_map_.find(group_name); | 293 GroupMap::iterator group_it = group_map_.find(group_name); |
| 287 CHECK(group_it != group_map_.end()); | 294 CHECK(group_it != group_map_.end()); |
| 288 Group& group = group_it->second; | 295 Group& group = group_it->second; |
| 289 | 296 |
| 290 CheckSocketCounts(group); | |
| 291 | |
| 292 RequestMap* request_map = &group.connecting_requests; | 297 RequestMap* request_map = &group.connecting_requests; |
| 293 | 298 |
| 294 RequestMap::iterator it = request_map->find(key_handle); | 299 RequestMap::iterator it = request_map->find(job->key_handle()); |
| 295 CHECK(it != request_map->end()); | 300 CHECK(it != request_map->end()); |
| 296 Request request = it->second; | 301 ClientSocketHandle* const handle = it->second.handle; |
| 302 CompletionCallback* const callback = it->second.callback; |
| 297 request_map->erase(it); | 303 request_map->erase(it); |
| 298 DCHECK_EQ(request.handle, key_handle); | 304 DCHECK_EQ(handle, job->key_handle()); |
| 299 | 305 |
| 300 if (!socket) { | 306 ClientSocket* const socket = job->ReleaseSocket(); |
| 301 RemoveActiveSocket(group_name, &group); | 307 RemoveConnectJob(job->key_handle()); |
| 308 |
| 309 if (result != OK) { |
| 310 callback->Run(result); // |group| is not necessarily valid after this. |
| 311 // |group| may be invalid after the callback, we need to search |
| 312 // |group_map_| again. |
| 313 MaybeOnAvailableSocketSlot(group_name); |
| 302 } else { | 314 } else { |
| 303 request.handle->set_socket(socket); | 315 HandOutSocket(socket, false /* not reused */, handle, &group); |
| 304 request.handle->set_is_reused(false); | 316 callback->Run(result); |
| 305 group.sockets_handed_out_count++; | |
| 306 | |
| 307 CheckSocketCounts(group); | |
| 308 } | 317 } |
| 309 | |
| 310 RemoveConnectJob(request.handle); | |
| 311 | |
| 312 if (was_async) | |
| 313 request.callback->Run(result); | |
| 314 } | |
| 315 | |
| 316 // static | |
| 317 void ClientSocketPoolBase::CheckSocketCounts(const Group& group) { | |
| 318 CHECK(group.active_socket_count == | |
| 319 group.sockets_handed_out_count + | |
| 320 static_cast<int>(group.connecting_requests.size())) | |
| 321 << "[active_socket_count: " << group.active_socket_count | |
| 322 << " ] [sockets_handed_out_count: " << group.sockets_handed_out_count | |
| 323 << " ] [connecting_requests size: " << group.connecting_requests.size(); | |
| 324 } | 318 } |
| 325 | 319 |
| 326 void ClientSocketPoolBase::RemoveConnectJob( | 320 void ClientSocketPoolBase::RemoveConnectJob( |
| 327 const ClientSocketHandle* handle) { | 321 const ClientSocketHandle* handle) { |
| 328 ConnectJobMap::iterator it = connect_job_map_.find(handle); | 322 ConnectJobMap::iterator it = connect_job_map_.find(handle); |
| 329 CHECK(it != connect_job_map_.end()); | 323 CHECK(it != connect_job_map_.end()); |
| 330 delete it->second; | 324 delete it->second; |
| 331 connect_job_map_.erase(it); | 325 connect_job_map_.erase(it); |
| 332 } | 326 } |
| 333 | 327 |
| 334 void ClientSocketPoolBase::RemoveActiveSocket(const std::string& group_name, | 328 void ClientSocketPoolBase::MaybeOnAvailableSocketSlot( |
| 335 Group* group) { | 329 const std::string& group_name) { |
| 336 group->active_socket_count--; | 330 GroupMap::iterator it = group_map_.find(group_name); |
| 331 if (it != group_map_.end()) { |
| 332 Group& group = it->second; |
| 333 if (group.HasAvailableSocketSlot(max_sockets_per_group_)) |
| 334 OnAvailableSocketSlot(group_name, &group); |
| 335 } |
| 336 } |
| 337 | 337 |
| 338 void ClientSocketPoolBase::OnAvailableSocketSlot(const std::string& group_name, |
| 339 Group* group) { |
| 338 if (!group->pending_requests.empty()) { | 340 if (!group->pending_requests.empty()) { |
| 339 ProcessPendingRequest(group_name, group); | 341 ProcessPendingRequest(group_name, group); |
| 340 // |group| may no longer be valid after this point. Be careful not to | 342 // |group| may no longer be valid after this point. Be careful not to |
| 341 // access it again. | 343 // access it again. |
| 342 } else if (group->active_socket_count == 0 && group->idle_sockets.empty()) { | 344 } else if (group->IsEmpty()) { |
| 343 // Delete |group| if no longer needed. |group| will no longer be valid. | 345 // Delete |group| if no longer needed. |group| will no longer be valid. |
| 344 DCHECK(group->connecting_requests.empty()); | |
| 345 group_map_.erase(group_name); | 346 group_map_.erase(group_name); |
| 346 } else { | |
| 347 CheckSocketCounts(*group); | |
| 348 } | 347 } |
| 349 } | 348 } |
| 350 | 349 |
| 351 void ClientSocketPoolBase::ProcessPendingRequest(const std::string& group_name, | 350 void ClientSocketPoolBase::ProcessPendingRequest(const std::string& group_name, |
| 352 Group* group) { | 351 Group* group) { |
| 353 Request r = group->pending_requests.front(); | 352 Request r = group->pending_requests.front(); |
| 354 group->pending_requests.pop_front(); | 353 group->pending_requests.pop_front(); |
| 355 | 354 |
| 356 int rv = RequestSocket( | 355 int rv = RequestSocket( |
| 357 group_name, r.resolve_info, r.priority, r.handle, r.callback); | 356 group_name, r.resolve_info, r.priority, r.handle, r.callback); |
| 358 | 357 |
| 359 // |group| may be invalid after RequestSocket. | 358 if (rv != ERR_IO_PENDING) { |
| 359 r.callback->Run(rv); |
| 360 if (rv != OK) { |
| 361 // |group| may be invalid after the callback, we need to search |
| 362 // |group_map_| again. |
| 363 MaybeOnAvailableSocketSlot(group_name); |
| 364 } |
| 365 } |
| 366 } |
| 360 | 367 |
| 361 if (rv != ERR_IO_PENDING) | 368 void ClientSocketPoolBase::HandOutSocket( |
| 362 r.callback->Run(rv); | 369 ClientSocket* socket, |
| 370 bool reused, |
| 371 ClientSocketHandle* handle, |
| 372 Group* group) { |
| 373 DCHECK(socket); |
| 374 handle->set_socket(socket); |
| 375 handle->set_is_reused(reused); |
| 376 group->active_socket_count++; |
| 363 } | 377 } |
| 364 | 378 |
| 365 } // namespace net | 379 } // namespace net |
| OLD | NEW |