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

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

Issue 118100: Avoid doing concurrent DNS resolves of the same hostname (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Get compiling on mac Created 11 years, 6 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 | Annotate | Revision Log
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/host_resolver.h" 5 #include "net/base/host_resolver.h"
6 6
7 #if defined(OS_WIN) 7 #if defined(OS_WIN)
8 #include <ws2tcpip.h> 8 #include <ws2tcpip.h>
9 #include <wspiapi.h> // Needed for Win2k compat. 9 #include <wspiapi.h> // Needed for Win2k compat.
10 #elif defined(OS_POSIX) 10 #elif defined(OS_POSIX)
11 #include <netdb.h> 11 #include <netdb.h>
12 #include <sys/socket.h> 12 #include <sys/socket.h>
13 #endif 13 #endif
14 #if defined(OS_LINUX) 14 #if defined(OS_LINUX)
15 #include <resolv.h> 15 #include <resolv.h>
16 #endif 16 #endif
17 17
18 #include "base/compiler_specific.h"
18 #include "base/message_loop.h" 19 #include "base/message_loop.h"
20 #include "base/stl_util-inl.h"
19 #include "base/string_util.h" 21 #include "base/string_util.h"
22 #include "base/time.h"
20 #include "base/worker_pool.h" 23 #include "base/worker_pool.h"
21 #include "net/base/address_list.h" 24 #include "net/base/address_list.h"
22 #include "net/base/net_errors.h" 25 #include "net/base/net_errors.h"
23 26
24 #if defined(OS_LINUX) 27 #if defined(OS_LINUX)
25 #include "base/singleton.h" 28 #include "base/singleton.h"
26 #include "base/thread_local_storage.h" 29 #include "base/thread_local_storage.h"
27 #include "base/time.h"
28 #endif 30 #endif
29 31
30 #if defined(OS_WIN) 32 #if defined(OS_WIN)
31 #include "net/base/winsock_init.h" 33 #include "net/base/winsock_init.h"
32 #endif 34 #endif
33 35
34 namespace net { 36 namespace net {
35 37
36 //----------------------------------------------------------------------------- 38 //-----------------------------------------------------------------------------
37 39
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
103 105
104 DISALLOW_COPY_AND_ASSIGN(DnsReloadTimer); 106 DISALLOW_COPY_AND_ASSIGN(DnsReloadTimer);
105 }; 107 };
106 108
107 // A TLS slot to the TimeTicks for the current thread. 109 // A TLS slot to the TimeTicks for the current thread.
108 // static 110 // static
109 ThreadLocalStorage::Slot DnsReloadTimer::tls_index_(base::LINKER_INITIALIZED); 111 ThreadLocalStorage::Slot DnsReloadTimer::tls_index_(base::LINKER_INITIALIZED);
110 112
111 #endif // defined(OS_LINUX) 113 #endif // defined(OS_LINUX)
112 114
113 static int HostResolverProc( 115 static int HostResolverProc(const std::string& host, struct addrinfo** out) {
114 const std::string& host, const std::string& port, struct addrinfo** out) {
115 struct addrinfo hints = {0}; 116 struct addrinfo hints = {0};
116 hints.ai_family = AF_UNSPEC; 117 hints.ai_family = AF_UNSPEC;
117 118
118 #if defined(OS_WIN) 119 #if defined(OS_WIN)
119 // DO NOT USE AI_ADDRCONFIG ON WINDOWS. 120 // DO NOT USE AI_ADDRCONFIG ON WINDOWS.
120 // 121 //
121 // The following comment in <winsock2.h> is the best documentation I found 122 // The following comment in <winsock2.h> is the best documentation I found
122 // on AI_ADDRCONFIG for Windows: 123 // on AI_ADDRCONFIG for Windows:
123 // Flags used in "hints" argument to getaddrinfo() 124 // Flags used in "hints" argument to getaddrinfo()
124 // - AI_ADDRCONFIG is supported starting with Vista 125 // - AI_ADDRCONFIG is supported starting with Vista
(...skipping 12 matching lines...) Expand all
137 // address. 138 // address.
138 // See http://crbug.com/5234. 139 // See http://crbug.com/5234.
139 hints.ai_flags = 0; 140 hints.ai_flags = 0;
140 #else 141 #else
141 hints.ai_flags = AI_ADDRCONFIG; 142 hints.ai_flags = AI_ADDRCONFIG;
142 #endif 143 #endif
143 144
144 // Restrict result set to only this socket type to avoid duplicates. 145 // Restrict result set to only this socket type to avoid duplicates.
145 hints.ai_socktype = SOCK_STREAM; 146 hints.ai_socktype = SOCK_STREAM;
146 147
147 int err = getaddrinfo(host.c_str(), port.c_str(), &hints, out); 148 int err = getaddrinfo(host.c_str(), NULL, &hints, out);
148 #if defined(OS_LINUX) 149 #if defined(OS_LINUX)
149 net::DnsReloadTimer* dns_timer = Singleton<net::DnsReloadTimer>::get(); 150 net::DnsReloadTimer* dns_timer = Singleton<net::DnsReloadTimer>::get();
150 // If we fail, re-initialise the resolver just in case there have been any 151 // If we fail, re-initialise the resolver just in case there have been any
151 // changes to /etc/resolv.conf and retry. See http://crbug.com/11380 for info. 152 // changes to /etc/resolv.conf and retry. See http://crbug.com/11380 for info.
152 if (err && dns_timer->Expired()) { 153 if (err && dns_timer->Expired()) {
153 res_nclose(&_res); 154 res_nclose(&_res);
154 if (!res_ninit(&_res)) 155 if (!res_ninit(&_res))
155 err = getaddrinfo(host.c_str(), port.c_str(), &hints, out); 156 err = getaddrinfo(host.c_str(), NULL, &hints, out);
156 } 157 }
157 #endif 158 #endif
158 159
159 return err ? ERR_NAME_NOT_RESOLVED : OK; 160 return err ? ERR_NAME_NOT_RESOLVED : OK;
160 } 161 }
161 162
162 static int ResolveAddrInfo(HostMapper* mapper, const std::string& host, 163 static int ResolveAddrInfo(HostMapper* mapper, const std::string& host,
163 const std::string& port, struct addrinfo** out) { 164 struct addrinfo** out) {
164 if (mapper) { 165 if (mapper) {
165 std::string mapped_host = mapper->Map(host); 166 std::string mapped_host = mapper->Map(host);
166 167
167 if (mapped_host.empty()) 168 if (mapped_host.empty())
168 return ERR_NAME_NOT_RESOLVED; 169 return ERR_NAME_NOT_RESOLVED;
169 170
170 return HostResolverProc(mapped_host, port, out); 171 return HostResolverProc(mapped_host, out);
171 } else { 172 } else {
172 return HostResolverProc(host, port, out); 173 return HostResolverProc(host, out);
173 } 174 }
174 } 175 }
175 176
176 //----------------------------------------------------------------------------- 177 //-----------------------------------------------------------------------------
177 178
178 class HostResolver::Request : 179 class HostResolver::Request {
179 public base::RefCountedThreadSafe<HostResolver::Request> {
180 public: 180 public:
181 Request(HostResolver* resolver, 181 Request(CompletionCallback* callback, AddressList* addresses, int port)
182 const std::string& host, 182 : job_(NULL), callback_(callback), addresses_(addresses), port_(port) {}
183 const std::string& port, 183
184 AddressList* addresses, 184 // Mark the request as cancelled.
185 CompletionCallback* callback) 185 void Cancel() {
186 job_ = NULL;
187 callback_ = NULL;
188 addresses_ = NULL;
189 }
190
191 bool was_cancelled() const {
192 return callback_ == NULL;
193 }
194
195 void set_job(Job* job) {
196 DCHECK(job != NULL);
197 // Identify which job the request is waiting on.
198 job_ = job;
199 }
200
201 void OnComplete(int error, const AddressList& addrlist) {
202 if (error == OK)
203 addresses_->SetFrom(addrlist, port_);
204 callback_->Run(error);
205 }
206
207 int port() const {
208 return port_;
209 }
210
211 Job* job() const {
212 return job_;
213 }
214
215 private:
216 // The resolve job (running in worker pool) that this request is dependent on.
217 Job* job_;
218
219 // The user's callback to invoke when the request completes.
220 CompletionCallback* callback_;
221
222 // The address list to save result into.
223 AddressList* addresses_;
224
225 // The desired port number for the socket addresses.
226 int port_;
227
228 DISALLOW_COPY_AND_ASSIGN(Request);
229 };
230
231 //-----------------------------------------------------------------------------
232
233 // This class represents a request to the worker pool for a "getaddrinfo()"
234 // call.
235 class HostResolver::Job : public base::RefCountedThreadSafe<HostResolver::Job> {
236 public:
237 Job(HostResolver* resolver, const std::string& host)
186 : host_(host), 238 : host_(host),
187 port_(port),
188 resolver_(resolver), 239 resolver_(resolver),
189 addresses_(addresses),
190 callback_(callback),
191 origin_loop_(MessageLoop::current()), 240 origin_loop_(MessageLoop::current()),
192 host_mapper_(host_mapper), 241 host_mapper_(host_mapper),
193 error_(OK), 242 error_(OK),
194 results_(NULL) { 243 results_(NULL) {
195 } 244 }
196 245
197 ~Request() { 246 ~Job() {
198 if (results_) 247 if (results_)
199 freeaddrinfo(results_); 248 freeaddrinfo(results_);
249
250 // Free the requests attached to this job.
251 STLDeleteElements(&requests_);
200 } 252 }
201 253
254 // Attaches a request to this job. The job takes ownership of |req| and will
255 // take care to delete it.
256 void AddRequest(HostResolver::Request* req) {
257 req->set_job(this);
258 requests_.push_back(req);
259 }
260
261 // Called from origin loop.
262 void Start() {
263 // Dispatch the job to a worker thread.
264 if (!WorkerPool::PostTask(FROM_HERE,
265 NewRunnableMethod(this, &Job::DoLookup), true)) {
266 NOTREACHED();
267
268 // Since we could be running within Resolve() right now, we can't just
269 // call OnLookupComplete(). Instead we must wait until Resolve() has
270 // returned (IO_PENDING).
271 error_ = ERR_UNEXPECTED;
272 MessageLoop::current()->PostTask(
273 FROM_HERE, NewRunnableMethod(this, &Job::OnLookupComplete));
274 }
275 }
276
277 // Cancels the current job. Callable from origin thread.
278 void Cancel() {
279 resolver_ = NULL;
280
281 AutoLock locked(origin_loop_lock_);
282 origin_loop_ = NULL;
283 }
284
285 // Called from origin thread.
286 bool was_cancelled() const {
287 return resolver_ == NULL;
288 }
289
290 // Called from origin thread.
291 const std::string& host() const {
292 return host_;
293 }
294
295 // Called from origin thread.
296 const RequestsList& requests() const {
297 return requests_;
298 }
299
300 private:
202 void DoLookup() { 301 void DoLookup() {
203 // Running on the worker thread 302 // Running on the worker thread
204 error_ = ResolveAddrInfo(host_mapper_, host_, port_, &results_); 303 error_ = ResolveAddrInfo(host_mapper_, host_, &results_);
205 304
206 Task* reply = NewRunnableMethod(this, &Request::DoCallback); 305 Task* reply = NewRunnableMethod(this, &Job::OnLookupComplete);
207 306
208 // The origin loop could go away while we are trying to post to it, so we 307 // The origin loop could go away while we are trying to post to it, so we
209 // need to call its PostTask method inside a lock. See ~HostResolver. 308 // need to call its PostTask method inside a lock. See ~HostResolver.
210 { 309 {
211 AutoLock locked(origin_loop_lock_); 310 AutoLock locked(origin_loop_lock_);
212 if (origin_loop_) { 311 if (origin_loop_) {
213 origin_loop_->PostTask(FROM_HERE, reply); 312 origin_loop_->PostTask(FROM_HERE, reply);
214 reply = NULL; 313 reply = NULL;
215 } 314 }
216 } 315 }
217 316
218 // Does nothing if it got posted. 317 // Does nothing if it got posted.
219 delete reply; 318 delete reply;
220 } 319 }
221 320
222 void DoCallback() { 321 // Callback for when DoLookup() completes (runs on origin thread).
223 // Running on the origin thread. 322 void OnLookupComplete() {
323 DCHECK_EQ(origin_loop_, MessageLoop::current());
224 DCHECK(error_ || results_); 324 DCHECK(error_ || results_);
225 325
226 // We may have been cancelled! 326 if (was_cancelled())
227 if (!resolver_)
228 return; 327 return;
229 328
230 if (!error_) { 329 DCHECK(!requests_.empty());
231 addresses_->Adopt(results_); 330
331 // Adopt the address list using the port number of the first request.
332 AddressList addrlist;
333 if (error_ == OK) {
334 addrlist.Adopt(results_);
335 addrlist.SetPort(requests_[0]->port());
232 results_ = NULL; 336 results_ = NULL;
233 } 337 }
234 338
235 // Drop the resolver's reference to us. Do this before running the 339 resolver_->OnJobComplete(this, error_, addrlist);
236 // callback since the callback might result in the resolver being
237 // destroyed.
238 resolver_->request_ = NULL;
239
240 callback_->Run(error_);
241 } 340 }
242 341
243 void Cancel() {
244 resolver_ = NULL;
245
246 AutoLock locked(origin_loop_lock_);
247 origin_loop_ = NULL;
248 }
249
250 private:
251 // Set on the origin thread, read on the worker thread. 342 // Set on the origin thread, read on the worker thread.
252 std::string host_; 343 std::string host_;
253 std::string port_;
254 344
255 // Only used on the origin thread (where Resolve was called). 345 // Only used on the origin thread (where Resolve was called).
256 HostResolver* resolver_; 346 HostResolver* resolver_;
257 AddressList* addresses_; 347 RequestsList requests_; // The requests waiting on this job.
258 CompletionCallback* callback_;
259 348
260 // Used to post ourselves onto the origin thread. 349 // Used to post ourselves onto the origin thread.
261 Lock origin_loop_lock_; 350 Lock origin_loop_lock_;
262 MessageLoop* origin_loop_; 351 MessageLoop* origin_loop_;
263 352
264 // Hold an owning reference to the host mapper that we are going to use. 353 // Hold an owning reference to the host mapper that we are going to use.
265 // This may not be the current host mapper by the time we call 354 // This may not be the current host mapper by the time we call
266 // ResolveAddrInfo, but that's OK... we'll use it anyways, and the owning 355 // ResolveAddrInfo, but that's OK... we'll use it anyways, and the owning
267 // reference ensures that it remains valid until we are done. 356 // reference ensures that it remains valid until we are done.
268 scoped_refptr<HostMapper> host_mapper_; 357 scoped_refptr<HostMapper> host_mapper_;
269 358
270 // Assigned on the worker thread, read on the origin thread. 359 // Assigned on the worker thread, read on the origin thread.
271 int error_; 360 int error_;
272 struct addrinfo* results_; 361 struct addrinfo* results_;
362
363 DISALLOW_COPY_AND_ASSIGN(Job);
273 }; 364 };
274 365
275 //----------------------------------------------------------------------------- 366 //-----------------------------------------------------------------------------
276 367
277 HostResolver::HostResolver() { 368 HostResolver::HostResolver(int max_cache_entries, int cache_duration_ms)
369 : cache_(max_cache_entries, cache_duration_ms) {
278 #if defined(OS_WIN) 370 #if defined(OS_WIN)
279 EnsureWinsockInit(); 371 EnsureWinsockInit();
280 #endif 372 #endif
281 } 373 }
282 374
283 HostResolver::~HostResolver() { 375 HostResolver::~HostResolver() {
284 if (request_) 376 // Cancel the outstanding jobs. Those jobs may contain several attached
285 request_->Cancel(); 377 // requests, which will now never be completed.
286 } 378 for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
287 379 it->second->Cancel();
380
381 // In case we are being deleted during the processing of a callback.
382 if (cur_completing_job_)
383 cur_completing_job_->Cancel();
384 }
385
386 // TODO(eroman): Don't create cache entries for hostnames which are simply IP
387 // address literals.
288 int HostResolver::Resolve(const std::string& hostname, int port, 388 int HostResolver::Resolve(const std::string& hostname, int port,
289 AddressList* addresses, 389 AddressList* addresses,
290 CompletionCallback* callback) { 390 CompletionCallback* callback,
291 DCHECK(!request_) << "resolver already in use"; 391 Request** out_req) {
292 392 // If we have an unexpired cache entry, use it.
293 const std::string& port_str = IntToString(port); 393 const HostCache::Entry* cache_entry = cache_.Lookup(
294 394 hostname, base::TimeTicks::Now());
295 // Do a synchronous resolution. 395 if (cache_entry) {
396 addresses->SetFrom(cache_entry->addrlist, port);
397 return OK;
398 }
399
400 // If no callback was specified, do a synchronous resolution.
296 if (!callback) { 401 if (!callback) {
297 struct addrinfo* results; 402 struct addrinfo* results;
298 int rv = ResolveAddrInfo(host_mapper, hostname, port_str, &results); 403 int error = ResolveAddrInfo(host_mapper, hostname, &results);
299 if (rv == OK) 404
300 addresses->Adopt(results); 405 // Adopt the address list.
301 return rv; 406 AddressList addrlist;
302 } 407 if (error == OK) {
303 408 addrlist.Adopt(results);
304 request_ = new Request(this, hostname, port_str, addresses, callback); 409 addrlist.SetPort(port);
305 410 *addresses = addrlist;
306 // Dispatch to worker thread... 411 }
307 if (!WorkerPool::PostTask(FROM_HERE, 412
308 NewRunnableMethod(request_.get(), &Request::DoLookup), true)) { 413 // Write to cache.
309 NOTREACHED(); 414 cache_.Set(hostname, error, addrlist, base::TimeTicks::Now());
310 request_ = NULL; 415
311 return ERR_FAILED; 416 return error;
312 } 417 }
313 418
419 // Create a handle for this request, and pass it back to the user if they
420 // asked for it (out_req != NULL).
421 Request* req = new Request(callback, addresses, port);
422 if (out_req)
423 *out_req = req;
424
425 // Next we need to attach our request to a "job". This job is responsible for
426 // calling "getaddrinfo(hostname)" on a worker thread.
427 scoped_refptr<Job> job;
428
429 // If there is already an outstanding job to resolve |hostname|, use it.
430 // This prevents starting concurrent resolves for the same hostname.
431 job = FindOutstandingJob(hostname);
432 if (job) {
433 job->AddRequest(req);
434 } else {
435 // Create a new job for this request.
436 job = new Job(this, hostname);
437 job->AddRequest(req);
438 AddOutstandingJob(job);
439 // TODO(eroman): Bound the total number of concurrent jobs.
440 // http://crbug.com/9598
441 job->Start();
442 }
443
444 // Completion happens during OnJobComplete(Job*).
314 return ERR_IO_PENDING; 445 return ERR_IO_PENDING;
315 } 446 }
316 447
448 // See OnJobComplete(Job*) for why it is important not to clean out
449 // cancelled requests from Job::requests_.
450 void HostResolver::CancelRequest(Request* req) {
451 DCHECK(req);
452 DCHECK(req->job());
453 // NULL out the fields of req, to mark it as cancelled.
454 req->Cancel();
455 }
456
457 void HostResolver::AddOutstandingJob(Job* job) {
458 scoped_refptr<Job>& found_job = jobs_[job->host()];
459 DCHECK(!found_job);
460 found_job = job;
461 }
462
463 HostResolver::Job* HostResolver::FindOutstandingJob(
464 const std::string& hostname) {
465 JobMap::iterator it = jobs_.find(hostname);
466 if (it != jobs_.end())
467 return it->second;
468 return NULL;
469 }
470
471 void HostResolver::RemoveOutstandingJob(Job* job) {
472 JobMap::iterator it = jobs_.find(job->host());
473 DCHECK(it != jobs_.end());
474 DCHECK_EQ(it->second.get(), job);
475 jobs_.erase(it);
476 }
477
478 void HostResolver::OnJobComplete(Job* job,
479 int error,
480 const AddressList& addrlist) {
481 RemoveOutstandingJob(job);
482
483 // Write result to the cache.
484 cache_.Set(job->host(), error, addrlist, base::TimeTicks::Now());
485
486 // Make a note that we are executing within OnJobComplete() in case the
487 // HostResolver is deleted by a callback invocation.
488 DCHECK(!cur_completing_job_);
489 cur_completing_job_ = job;
490
491 // Complete all of the requests that were attached to the job.
492 for (RequestsList::const_iterator it = job->requests().begin();
493 it != job->requests().end(); ++it) {
494 Request* req = *it;
495 if (!req->was_cancelled()) {
496 DCHECK_EQ(job, req->job());
497 req->OnComplete(error, addrlist);
498
499 // Check if the job was cancelled as a result of running the callback.
500 // (Meaning that |this| was deleted).
501 if (job->was_cancelled())
502 return;
503 }
504 }
505
506 cur_completing_job_ = NULL;
507 }
508
509 //-----------------------------------------------------------------------------
510
511 SingleRequestHostResolver::SingleRequestHostResolver(HostResolver* resolver)
512 : resolver_(resolver),
513 cur_request_(NULL),
514 cur_request_callback_(NULL),
515 ALLOW_THIS_IN_INITIALIZER_LIST(
516 callback_(this, &SingleRequestHostResolver::OnResolveCompletion)) {
517 DCHECK(resolver_ != NULL);
518 }
519
520 SingleRequestHostResolver::~SingleRequestHostResolver() {
521 if (cur_request_) {
522 resolver_->CancelRequest(cur_request_);
523 }
524 }
525
526 int SingleRequestHostResolver::Resolve(
527 const std::string& hostname, int port,
528 AddressList* addresses,
529 CompletionCallback* callback) {
530 DCHECK(!cur_request_ && !cur_request_callback_) << "resolver already in use";
531
532 HostResolver::Request* request = NULL;
533
534 // We need to be notified of completion before |callback| is called, so that
535 // we can clear out |cur_request_*|.
536 CompletionCallback* transient_callback = callback ? &callback_ : NULL;
537
538 int rv = resolver_->Resolve(
539 hostname, port, addresses, transient_callback, &request);
540
541 if (rv == ERR_IO_PENDING) {
542 // Cleared in OnResolveCompletion().
543 cur_request_ = request;
544 cur_request_callback_ = callback;
545 }
546
547 return rv;
548 }
549
550 void SingleRequestHostResolver::OnResolveCompletion(int result) {
551 DCHECK(cur_request_ && cur_request_callback_);
552
553 CompletionCallback* callback = cur_request_callback_;
554
555 // Clear the outstanding request information.
556 cur_request_ = NULL;
557 cur_request_callback_ = NULL;
558
559 // Call the user's original callback.
560 callback->Run(result);
561 }
562
317 } // namespace net 563 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698