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 |