Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(553)

Side by Side Diff: net/base/client_socket_pool.cc

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

Powered by Google App Engine
This is Rietveld 408576698