| 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/base/client_socket_pool.h" | 5 #include "net/base/client_socket_pool.h" |
| 6 | 6 |
| 7 #include "base/compiler_specific.h" |
| 8 #include "base/field_trial.h" |
| 7 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
| 8 #include "net/base/client_socket.h" | 10 #include "base/time.h" |
| 11 #include "base/stl_util-inl.h" |
| 12 #include "net/base/client_socket_factory.h" |
| 9 #include "net/base/client_socket_handle.h" | 13 #include "net/base/client_socket_handle.h" |
| 14 #include "net/base/dns_resolution_observer.h" |
| 10 #include "net/base/net_errors.h" | 15 #include "net/base/net_errors.h" |
| 16 #include "net/base/tcp_client_socket.h" |
| 11 | 17 |
| 12 using base::TimeDelta; | 18 using base::TimeDelta; |
| 13 | 19 |
| 14 namespace { | 20 namespace { |
| 15 | 21 |
| 16 // The timeout value, in seconds, used to clean up idle sockets that can't be | 22 // The timeout value, in seconds, used to clean up idle sockets that can't be |
| 17 // reused. | 23 // reused. |
| 18 // | 24 // |
| 19 // Note: It's important to close idle sockets that have received data as soon | 25 // Note: It's important to close idle sockets that have received data as soon |
| 20 // as possible because the received data may cause BSOD on Windows XP under | 26 // as possible because the received data may cause BSOD on Windows XP under |
| 21 // some conditions. See http://crbug.com/4606. | 27 // some conditions. See http://crbug.com/4606. |
| 22 const int kCleanupInterval = 10; // DO NOT INCREASE THIS TIMEOUT. | 28 const int kCleanupInterval = 10; // DO NOT INCREASE THIS TIMEOUT. |
| 23 | 29 |
| 24 // The maximum duration, in seconds, to keep idle persistent sockets alive. | 30 // The maximum duration, in seconds, to keep idle persistent sockets alive. |
| 25 const int kIdleTimeout = 300; // 5 minutes. | 31 const int kIdleTimeout = 300; // 5 minutes. |
| 26 | 32 |
| 27 } // namespace | 33 } // namespace |
| 28 | 34 |
| 29 namespace net { | 35 namespace net { |
| 30 | 36 |
| 31 ClientSocketPool::ClientSocketPool(int max_sockets_per_group) | 37 ClientSocketPool::ConnectingSocket::ConnectingSocket( |
| 32 : idle_socket_count_(0), | 38 const std::string& group_name, |
| 39 const ClientSocketHandle* handle, |
| 40 ClientSocketFactory* client_socket_factory, |
| 41 ClientSocketPool* pool) |
| 42 : group_name_(group_name), |
| 43 handle_(handle), |
| 44 client_socket_factory_(client_socket_factory), |
| 45 ALLOW_THIS_IN_INITIALIZER_LIST( |
| 46 callback_(this, |
| 47 &ClientSocketPool::ConnectingSocket::OnIOComplete)), |
| 48 pool_(pool), |
| 49 canceled_(false) { |
| 50 DCHECK(!ContainsKey(pool_->connecting_socket_map_, handle)); |
| 51 pool_->connecting_socket_map_[handle] = this; |
| 52 } |
| 53 |
| 54 ClientSocketPool::ConnectingSocket::~ConnectingSocket() { |
| 55 if (!canceled_) |
| 56 pool_->connecting_socket_map_.erase(handle_); |
| 57 } |
| 58 |
| 59 int ClientSocketPool::ConnectingSocket::Connect( |
| 60 const std::string& host, |
| 61 int port, |
| 62 CompletionCallback* callback) { |
| 63 DCHECK(!canceled_); |
| 64 DidStartDnsResolution(host, this); |
| 65 int rv = resolver_.Resolve(host, port, &addresses_, &callback_); |
| 66 if (rv == OK) { |
| 67 // TODO(willchan): This code is broken. It should be fixed, but the code |
| 68 // path is impossible in the current implementation since the host resolver |
| 69 // always dumps the request to a worker pool, so it cannot complete |
| 70 // synchronously. |
| 71 NOTREACHED(); |
| 72 connect_start_time_ = base::Time::Now(); |
| 73 rv = socket_->Connect(&callback_); |
| 74 } |
| 75 return rv; |
| 76 } |
| 77 |
| 78 ClientSocket* ClientSocketPool::ConnectingSocket::ReleaseSocket() { |
| 79 return socket_.release(); |
| 80 } |
| 81 |
| 82 void ClientSocketPool::ConnectingSocket::OnIOComplete(int result) { |
| 83 DCHECK_NE(result, ERR_IO_PENDING); |
| 84 |
| 85 if (canceled_) { |
| 86 // We got canceled, so bail out. |
| 87 delete this; |
| 88 return; |
| 89 } |
| 90 |
| 91 GroupMap::iterator group_it = pool_->group_map_.find(group_name_); |
| 92 if (group_it == pool_->group_map_.end()) { |
| 93 // The request corresponding to this ConnectingSocket has been canceled. |
| 94 // Stop bothering with it. |
| 95 delete this; |
| 96 return; |
| 97 } |
| 98 |
| 99 Group& group = group_it->second; |
| 100 |
| 101 RequestMap* request_map = &group.connecting_requests; |
| 102 RequestMap::iterator it = request_map->find(handle_); |
| 103 if (it == request_map->end()) { |
| 104 // The request corresponding to this ConnectingSocket has been canceled. |
| 105 // Stop bothering with it. |
| 106 delete this; |
| 107 return; |
| 108 } |
| 109 |
| 110 if (result == OK) { |
| 111 if (it->second.load_state == LOAD_STATE_RESOLVING_HOST) { |
| 112 it->second.load_state = LOAD_STATE_CONNECTING; |
| 113 socket_.reset(client_socket_factory_->CreateTCPClientSocket(addresses_)); |
| 114 connect_start_time_ = base::Time::Now(); |
| 115 result = socket_->Connect(&callback_); |
| 116 if (result == ERR_IO_PENDING) |
| 117 return; |
| 118 } else { |
| 119 DCHECK(connect_start_time_ != base::Time()); |
| 120 base::TimeDelta connect_duration = |
| 121 base::Time::Now() - connect_start_time_; |
| 122 |
| 123 UMA_HISTOGRAM_CLIPPED_TIMES( |
| 124 FieldTrial::MakeName( |
| 125 "Net.TCP_Connection_Latency", "DnsImpact").data(), |
| 126 connect_duration, |
| 127 base::TimeDelta::FromMilliseconds(1), |
| 128 base::TimeDelta::FromMinutes(10), |
| 129 100); |
| 130 } |
| 131 } |
| 132 |
| 133 // Now, we either succeeded at Connect()'ing, or we failed at host resolution |
| 134 // or Connect()'ing. Either way, we'll run the callback to alert the client. |
| 135 |
| 136 Request request = it->second; |
| 137 request_map->erase(it); |
| 138 |
| 139 if (result == OK) { |
| 140 request.handle->set_socket(socket_.release()); |
| 141 request.handle->set_is_reused(false); |
| 142 } else { |
| 143 group.active_socket_count--; |
| 144 |
| 145 // Delete group if no longer needed. |
| 146 if (group.active_socket_count == 0 && group.idle_sockets.empty()) { |
| 147 DCHECK(group.pending_requests.empty()); |
| 148 DCHECK(group.connecting_requests.empty()); |
| 149 pool_->group_map_.erase(group_it); |
| 150 } |
| 151 } |
| 152 |
| 153 request.callback->Run(result); |
| 154 delete this; |
| 155 } |
| 156 |
| 157 void ClientSocketPool::ConnectingSocket::Cancel() { |
| 158 DCHECK(!canceled_); |
| 159 DCHECK(ContainsKey(pool_->connecting_socket_map_, handle_)); |
| 160 pool_->connecting_socket_map_.erase(handle_); |
| 161 canceled_ = true; |
| 162 } |
| 163 |
| 164 ClientSocketPool::ClientSocketPool(int max_sockets_per_group, |
| 165 ClientSocketFactory* client_socket_factory) |
| 166 : client_socket_factory_(client_socket_factory), |
| 167 idle_socket_count_(0), |
| 33 max_sockets_per_group_(max_sockets_per_group) { | 168 max_sockets_per_group_(max_sockets_per_group) { |
| 34 } | 169 } |
| 35 | 170 |
| 36 ClientSocketPool::~ClientSocketPool() { | 171 ClientSocketPool::~ClientSocketPool() { |
| 37 // Clean up any idle sockets. Assert that we have no remaining active | 172 // Clean up any idle sockets. Assert that we have no remaining active |
| 38 // sockets or pending requests. They should have all been cleaned up prior | 173 // sockets or pending requests. They should have all been cleaned up prior |
| 39 // to the manager being destroyed. | 174 // to the manager being destroyed. |
| 40 CloseIdleSockets(); | 175 CloseIdleSockets(); |
| 41 DCHECK(group_map_.empty()); | 176 DCHECK(group_map_.empty()); |
| 42 } | 177 } |
| 43 | 178 |
| 44 // InsertRequestIntoQueue inserts the request into the queue based on | 179 // InsertRequestIntoQueue inserts the request into the queue based on |
| 45 // priority. Highest priorities are closest to the front. Older requests are | 180 // priority. Highest priorities are closest to the front. Older requests are |
| 46 // prioritized over requests of equal priority. | 181 // prioritized over requests of equal priority. |
| 47 // | 182 // |
| 48 // static | 183 // static |
| 49 void ClientSocketPool::InsertRequestIntoQueue(const Request& r, | 184 void ClientSocketPool::InsertRequestIntoQueue(const Request& r, |
| 50 RequestQueue* pending_requests) { | 185 RequestQueue* pending_requests) { |
| 51 RequestQueue::iterator it = pending_requests->begin(); | 186 RequestQueue::iterator it = pending_requests->begin(); |
| 52 while (it != pending_requests->end() && r.priority <= it->priority) | 187 while (it != pending_requests->end() && r.priority <= it->priority) |
| 53 ++it; | 188 ++it; |
| 54 pending_requests->insert(it, r); | 189 pending_requests->insert(it, r); |
| 55 } | 190 } |
| 56 | 191 |
| 57 int ClientSocketPool::RequestSocket(ClientSocketHandle* handle, | 192 int ClientSocketPool::RequestSocket(const std::string& group_name, |
| 193 const std::string& host, |
| 194 int port, |
| 58 int priority, | 195 int priority, |
| 196 ClientSocketHandle* handle, |
| 59 CompletionCallback* callback) { | 197 CompletionCallback* callback) { |
| 60 Group& group = group_map_[handle->group_name_]; | 198 DCHECK(!host.empty()); |
| 199 DCHECK_GE(priority, 0); |
| 200 Group& group = group_map_[group_name]; |
| 61 | 201 |
| 62 // Can we make another active socket now? | 202 // Can we make another active socket now? |
| 63 if (group.active_socket_count == max_sockets_per_group_) { | 203 if (group.active_socket_count == max_sockets_per_group_) { |
| 64 Request r; | 204 Request r; |
| 65 r.handle = handle; | 205 r.handle = handle; |
| 66 DCHECK(callback); | 206 DCHECK(callback); |
| 67 r.callback = callback; | 207 r.callback = callback; |
| 68 r.priority = priority; | 208 r.priority = priority; |
| 209 r.host = host; |
| 210 r.port = port; |
| 211 r.load_state = LOAD_STATE_IDLE; |
| 69 InsertRequestIntoQueue(r, &group.pending_requests); | 212 InsertRequestIntoQueue(r, &group.pending_requests); |
| 70 return ERR_IO_PENDING; | 213 return ERR_IO_PENDING; |
| 71 } | 214 } |
| 72 | 215 |
| 73 // OK, we are going to activate one. | 216 // OK, we are going to activate one. |
| 74 group.active_socket_count++; | 217 group.active_socket_count++; |
| 75 | 218 |
| 76 // Use idle sockets in LIFO order because they're more likely to be | |
| 77 // still reusable. | |
| 78 while (!group.idle_sockets.empty()) { | 219 while (!group.idle_sockets.empty()) { |
| 79 IdleSocket idle_socket = group.idle_sockets.back(); | 220 IdleSocket idle_socket = group.idle_sockets.back(); |
| 80 group.idle_sockets.pop_back(); | 221 group.idle_sockets.pop_back(); |
| 81 DecrementIdleCount(); | 222 DecrementIdleCount(); |
| 82 if ((*idle_socket.ptr)->IsConnectedAndIdle()) { | 223 if (idle_socket.socket->IsConnectedAndIdle()) { |
| 83 // We found one we can reuse! | 224 // We found one we can reuse! |
| 84 handle->socket_ = idle_socket.ptr; | 225 handle->set_socket(idle_socket.socket); |
| 226 handle->set_is_reused(true); |
| 85 return OK; | 227 return OK; |
| 86 } | 228 } |
| 87 delete idle_socket.ptr; | 229 delete idle_socket.socket; |
| 88 } | 230 } |
| 89 | 231 |
| 90 handle->socket_ = new ClientSocketPtr(); | 232 // We couldn't find a socket to reuse, so allocate and connect a new one. |
| 91 return OK; | 233 |
| 234 // First, we need to make sure we aren't already servicing a request for this |
| 235 // handle (which could happen if we requested, canceled, and then requested |
| 236 // with the same handle). |
| 237 if (ContainsKey(connecting_socket_map_, handle)) |
| 238 connecting_socket_map_[handle]->Cancel(); |
| 239 |
| 240 scoped_ptr<ConnectingSocket> connecting_socket( |
| 241 new ConnectingSocket(group_name, handle, client_socket_factory_, this)); |
| 242 int rv = connecting_socket->Connect(host, port, callback); |
| 243 if (rv == OK) { |
| 244 NOTREACHED(); |
| 245 handle->set_socket(connecting_socket->ReleaseSocket()); |
| 246 handle->set_is_reused(false); |
| 247 } else if (rv == ERR_IO_PENDING) { |
| 248 // The ConnectingSocket will delete itself. |
| 249 connecting_socket.release(); |
| 250 Request r; |
| 251 r.handle = handle; |
| 252 DCHECK(callback); |
| 253 r.callback = callback; |
| 254 r.priority = priority; |
| 255 r.host = host; |
| 256 r.port = port; |
| 257 r.load_state = LOAD_STATE_RESOLVING_HOST; |
| 258 group_map_[group_name].connecting_requests[handle] = r; |
| 259 } else { |
| 260 group.active_socket_count--; |
| 261 |
| 262 // Delete group if no longer needed. |
| 263 if (group.active_socket_count == 0 && group.idle_sockets.empty()) { |
| 264 DCHECK(group.pending_requests.empty()); |
| 265 DCHECK(group.connecting_requests.empty()); |
| 266 group_map_.erase(group_name); |
| 267 } |
| 268 } |
| 269 |
| 270 return rv; |
| 92 } | 271 } |
| 93 | 272 |
| 94 void ClientSocketPool::CancelRequest(ClientSocketHandle* handle) { | 273 void ClientSocketPool::CancelRequest(const std::string& group_name, |
| 95 Group& group = group_map_[handle->group_name_]; | 274 const ClientSocketHandle* handle) { |
| 275 DCHECK(ContainsKey(group_map_, group_name)); |
| 96 | 276 |
| 97 // In order for us to be canceling a pending request, we must have active | 277 Group& group = group_map_[group_name]; |
| 98 // sockets equaling the limit. NOTE: The correctness of the code doesn't | |
| 99 // require this assertion. | |
| 100 DCHECK(group.active_socket_count == max_sockets_per_group_); | |
| 101 | 278 |
| 102 // Search pending_requests for matching handle. | 279 // Search pending_requests for matching handle. |
| 103 std::deque<Request>::iterator it = group.pending_requests.begin(); | 280 RequestQueue::iterator it = group.pending_requests.begin(); |
| 104 for (; it != group.pending_requests.end(); ++it) { | 281 for (; it != group.pending_requests.end(); ++it) { |
| 105 if (it->handle == handle) { | 282 if (it->handle == handle) { |
| 106 group.pending_requests.erase(it); | 283 group.pending_requests.erase(it); |
| 107 break; | 284 return; |
| 285 } |
| 286 } |
| 287 |
| 288 // It's invalid to cancel a non-existent request. |
| 289 DCHECK(ContainsKey(group.connecting_requests, handle)); |
| 290 |
| 291 RequestMap::iterator map_it = group.connecting_requests.find(handle); |
| 292 if (map_it != group.connecting_requests.end()) { |
| 293 group.connecting_requests.erase(map_it); |
| 294 group.active_socket_count--; |
| 295 |
| 296 // Delete group if no longer needed. |
| 297 if (group.active_socket_count == 0 && group.idle_sockets.empty()) { |
| 298 DCHECK(group.pending_requests.empty()); |
| 299 DCHECK(group.connecting_requests.empty()); |
| 300 group_map_.erase(group_name); |
| 108 } | 301 } |
| 109 } | 302 } |
| 110 } | 303 } |
| 111 | 304 |
| 112 void ClientSocketPool::ReleaseSocket(ClientSocketHandle* handle) { | 305 void ClientSocketPool::ReleaseSocket(const std::string& group_name, |
| 306 ClientSocket* socket) { |
| 113 // Run this asynchronously to allow the caller to finish before we let | 307 // Run this asynchronously to allow the caller to finish before we let |
| 114 // another to begin doing work. This also avoids nasty recursion issues. | 308 // another to begin doing work. This also avoids nasty recursion issues. |
| 115 // NOTE: We cannot refer to the handle argument after this method returns. | 309 // NOTE: We cannot refer to the handle argument after this method returns. |
| 116 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( | 310 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( |
| 117 this, &ClientSocketPool::DoReleaseSocket, handle->group_name_, | 311 this, &ClientSocketPool::DoReleaseSocket, group_name, socket)); |
| 118 handle->socket_)); | |
| 119 } | 312 } |
| 120 | 313 |
| 121 void ClientSocketPool::CloseIdleSockets() { | 314 void ClientSocketPool::CloseIdleSockets() { |
| 122 CleanupIdleSockets(true); | 315 CleanupIdleSockets(true); |
| 123 } | 316 } |
| 124 | 317 |
| 125 int ClientSocketPool::IdleSocketCountInGroup( | 318 int ClientSocketPool::IdleSocketCountInGroup( |
| 126 const std::string& group_name) const { | 319 const std::string& group_name) const { |
| 127 GroupMap::const_iterator i = group_map_.find(group_name); | 320 GroupMap::const_iterator i = group_map_.find(group_name); |
| 128 DCHECK(i != group_map_.end()); | 321 DCHECK(i != group_map_.end()); |
| 129 | 322 |
| 130 return i->second.idle_sockets.size(); | 323 return i->second.idle_sockets.size(); |
| 131 } | 324 } |
| 132 | 325 |
| 326 LoadState ClientSocketPool::GetLoadState( |
| 327 const std::string& group_name, |
| 328 const ClientSocketHandle* handle) const { |
| 329 DCHECK(ContainsKey(group_map_, group_name)) << group_name; |
| 330 |
| 331 // Can't use operator[] since it is non-const. |
| 332 const Group& group = group_map_.find(group_name)->second; |
| 333 |
| 334 // Search connecting_requests for matching handle. |
| 335 RequestMap::const_iterator map_it = group.connecting_requests.find(handle); |
| 336 if (map_it != group.connecting_requests.end()) { |
| 337 const LoadState load_state = map_it->second.load_state; |
| 338 DCHECK(load_state == LOAD_STATE_RESOLVING_HOST || |
| 339 load_state == LOAD_STATE_CONNECTING); |
| 340 return load_state; |
| 341 } |
| 342 |
| 343 // Search pending_requests for matching handle. |
| 344 RequestQueue::const_iterator it = group.pending_requests.begin(); |
| 345 for (; it != group.pending_requests.end(); ++it) { |
| 346 if (it->handle == handle) { |
| 347 DCHECK_EQ(LOAD_STATE_IDLE, it->load_state); |
| 348 // TODO(wtc): Add a state for being on the wait list. |
| 349 // See http://www.crbug.com/5077. |
| 350 return LOAD_STATE_IDLE; |
| 351 } |
| 352 } |
| 353 |
| 354 NOTREACHED(); |
| 355 return LOAD_STATE_IDLE; |
| 356 } |
| 357 |
| 133 bool ClientSocketPool::IdleSocket::ShouldCleanup(base::TimeTicks now) const { | 358 bool ClientSocketPool::IdleSocket::ShouldCleanup(base::TimeTicks now) const { |
| 134 bool timed_out = (now - start_time) >= | 359 bool timed_out = (now - start_time) >= |
| 135 base::TimeDelta::FromSeconds(kIdleTimeout); | 360 base::TimeDelta::FromSeconds(kIdleTimeout); |
| 136 return timed_out || !(*ptr)->IsConnectedAndIdle(); | 361 return timed_out || !socket->IsConnectedAndIdle(); |
| 137 } | 362 } |
| 138 | 363 |
| 139 void ClientSocketPool::CleanupIdleSockets(bool force) { | 364 void ClientSocketPool::CleanupIdleSockets(bool force) { |
| 140 if (idle_socket_count_ == 0) | 365 if (idle_socket_count_ == 0) |
| 141 return; | 366 return; |
| 142 | 367 |
| 143 // Current time value. Retrieving it once at the function start rather than | 368 // Current time value. Retrieving it once at the function start rather than |
| 144 // inside the inner loop, since it shouldn't change by any meaningful amount. | 369 // inside the inner loop, since it shouldn't change by any meaningful amount. |
| 145 base::TimeTicks now = base::TimeTicks::Now(); | 370 base::TimeTicks now = base::TimeTicks::Now(); |
| 146 | 371 |
| 147 GroupMap::iterator i = group_map_.begin(); | 372 GroupMap::iterator i = group_map_.begin(); |
| 148 while (i != group_map_.end()) { | 373 while (i != group_map_.end()) { |
| 149 Group& group = i->second; | 374 Group& group = i->second; |
| 150 | 375 |
| 151 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin(); | 376 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin(); |
| 152 while (j != group.idle_sockets.end()) { | 377 while (j != group.idle_sockets.end()) { |
| 153 if (force || j->ShouldCleanup(now)) { | 378 if (force || j->ShouldCleanup(now)) { |
| 154 delete j->ptr; | 379 delete j->socket; |
| 155 j = group.idle_sockets.erase(j); | 380 j = group.idle_sockets.erase(j); |
| 156 DecrementIdleCount(); | 381 DecrementIdleCount(); |
| 157 } else { | 382 } else { |
| 158 ++j; | 383 ++j; |
| 159 } | 384 } |
| 160 } | 385 } |
| 161 | 386 |
| 162 // Delete group if no longer needed. | 387 // Delete group if no longer needed. |
| 163 if (group.active_socket_count == 0 && group.idle_sockets.empty()) { | 388 if (group.active_socket_count == 0 && group.idle_sockets.empty()) { |
| 164 DCHECK(group.pending_requests.empty()); | 389 DCHECK(group.pending_requests.empty()); |
| 390 DCHECK(group.connecting_requests.empty()); |
| 165 group_map_.erase(i++); | 391 group_map_.erase(i++); |
| 166 } else { | 392 } else { |
| 167 ++i; | 393 ++i; |
| 168 } | 394 } |
| 169 } | 395 } |
| 170 } | 396 } |
| 171 | 397 |
| 172 void ClientSocketPool::IncrementIdleCount() { | 398 void ClientSocketPool::IncrementIdleCount() { |
| 173 if (++idle_socket_count_ == 1) | 399 if (++idle_socket_count_ == 1) |
| 174 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this, | 400 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this, |
| 175 &ClientSocketPool::OnCleanupTimerFired); | 401 &ClientSocketPool::OnCleanupTimerFired); |
| 176 } | 402 } |
| 177 | 403 |
| 178 void ClientSocketPool::DecrementIdleCount() { | 404 void ClientSocketPool::DecrementIdleCount() { |
| 179 if (--idle_socket_count_ == 0) | 405 if (--idle_socket_count_ == 0) |
| 180 timer_.Stop(); | 406 timer_.Stop(); |
| 181 } | 407 } |
| 182 | 408 |
| 183 void ClientSocketPool::DoReleaseSocket(const std::string& group_name, | 409 void ClientSocketPool::DoReleaseSocket(const std::string& group_name, |
| 184 ClientSocketPtr* ptr) { | 410 ClientSocket* socket) { |
| 185 GroupMap::iterator i = group_map_.find(group_name); | 411 GroupMap::iterator i = group_map_.find(group_name); |
| 186 DCHECK(i != group_map_.end()); | 412 DCHECK(i != group_map_.end()); |
| 187 | 413 |
| 188 Group& group = i->second; | 414 Group& group = i->second; |
| 189 | 415 |
| 190 DCHECK(group.active_socket_count > 0); | 416 DCHECK_GT(group.active_socket_count, 0); |
| 191 group.active_socket_count--; | 417 group.active_socket_count--; |
| 192 | 418 |
| 193 bool can_reuse = ptr->get() && (*ptr)->IsConnectedAndIdle(); | 419 const bool can_reuse = socket->IsConnectedAndIdle(); |
| 194 if (can_reuse) { | 420 if (can_reuse) { |
| 195 IdleSocket idle_socket; | 421 IdleSocket idle_socket; |
| 196 idle_socket.ptr = ptr; | 422 idle_socket.socket = socket; |
| 197 idle_socket.start_time = base::TimeTicks::Now(); | 423 idle_socket.start_time = base::TimeTicks::Now(); |
| 198 | 424 |
| 199 group.idle_sockets.push_back(idle_socket); | 425 group.idle_sockets.push_back(idle_socket); |
| 200 IncrementIdleCount(); | 426 IncrementIdleCount(); |
| 201 } else { | 427 } else { |
| 202 delete ptr; | 428 delete socket; |
| 203 } | 429 } |
| 204 | 430 |
| 205 // Process one pending request. | 431 // Process one pending request. |
| 206 if (!group.pending_requests.empty()) { | 432 if (!group.pending_requests.empty()) { |
| 207 Request r = group.pending_requests.front(); | 433 Request r = group.pending_requests.front(); |
| 208 group.pending_requests.pop_front(); | 434 group.pending_requests.pop_front(); |
| 209 int rv = RequestSocket(r.handle, r.priority, NULL); | 435 |
| 210 DCHECK(rv == OK); | 436 int rv = RequestSocket( |
| 211 r.callback->Run(rv); | 437 group_name, r.host, r.port, r.priority, r.handle, r.callback); |
| 438 if (rv != ERR_IO_PENDING) |
| 439 r.callback->Run(rv); |
| 212 return; | 440 return; |
| 213 } | 441 } |
| 214 | 442 |
| 215 // Delete group if no longer needed. | 443 // Delete group if no longer needed. |
| 216 if (group.active_socket_count == 0 && group.idle_sockets.empty()) { | 444 if (group.active_socket_count == 0 && group.idle_sockets.empty()) { |
| 217 DCHECK(group.pending_requests.empty()); | 445 DCHECK(group.pending_requests.empty()); |
| 446 DCHECK(group.connecting_requests.empty()); |
| 218 group_map_.erase(i); | 447 group_map_.erase(i); |
| 219 } | 448 } |
| 220 } | 449 } |
| 221 | 450 |
| 222 } // namespace net | 451 } // namespace net |
| OLD | NEW |