Home | History | Annotate | Download | only in base
      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
      3 // found in the LICENSE file.
      4 
      5 #include "net/base/host_resolver_impl.h"
      6 
      7 #include <cmath>
      8 #include <deque>
      9 
     10 #include "base/basictypes.h"
     11 #include "base/compiler_specific.h"
     12 #include "base/debug_util.h"
     13 #include "base/lock.h"
     14 #include "base/message_loop.h"
     15 #include "base/stl_util-inl.h"
     16 #include "base/string_util.h"
     17 #include "base/time.h"
     18 #include "base/worker_pool.h"
     19 #include "net/base/address_list.h"
     20 #include "net/base/host_resolver_proc.h"
     21 #include "net/base/load_log.h"
     22 #include "net/base/net_errors.h"
     23 #include "net/base/network_change_notifier.h"
     24 
     25 #if defined(OS_WIN)
     26 #include "net/base/winsock_init.h"
     27 #endif
     28 
     29 namespace net {
     30 
     31 namespace {
     32 
     33 HostCache* CreateDefaultCache() {
     34   static const size_t kMaxHostCacheEntries = 100;
     35 
     36   HostCache* cache = new HostCache(
     37       kMaxHostCacheEntries,
     38       base::TimeDelta::FromMinutes(1),
     39       base::TimeDelta::FromSeconds(1));
     40 
     41   return cache;
     42 }
     43 
     44 }  // anonymous namespace
     45 
     46 HostResolver* CreateSystemHostResolver(
     47     NetworkChangeNotifier* network_change_notifier) {
     48   // Maximum of 50 concurrent threads.
     49   // TODO(eroman): Adjust this, do some A/B experiments.
     50   static const size_t kMaxJobs = 50u;
     51 
     52   // TODO(willchan): Pass in the NetworkChangeNotifier.
     53   HostResolverImpl* resolver = new HostResolverImpl(
     54       NULL, CreateDefaultCache(), network_change_notifier, kMaxJobs);
     55 
     56   return resolver;
     57 }
     58 
     59 static int ResolveAddrInfo(HostResolverProc* resolver_proc,
     60                            const std::string& host,
     61                            AddressFamily address_family,
     62                            AddressList* out) {
     63   if (resolver_proc) {
     64     // Use the custom procedure.
     65     return resolver_proc->Resolve(host, address_family, out);
     66   } else {
     67     // Use the system procedure (getaddrinfo).
     68     return SystemHostResolverProc(host, address_family, out);
     69   }
     70 }
     71 
     72 //-----------------------------------------------------------------------------
     73 
     74 class HostResolverImpl::Request {
     75  public:
     76   Request(LoadLog* load_log,
     77           int id,
     78           const RequestInfo& info,
     79           CompletionCallback* callback,
     80           AddressList* addresses)
     81       : load_log_(load_log),
     82         id_(id),
     83         info_(info),
     84         job_(NULL),
     85         callback_(callback),
     86         addresses_(addresses) {
     87   }
     88 
     89   // Mark the request as cancelled.
     90   void MarkAsCancelled() {
     91     job_ = NULL;
     92     callback_ = NULL;
     93     addresses_ = NULL;
     94   }
     95 
     96   bool was_cancelled() const {
     97     return callback_ == NULL;
     98   }
     99 
    100   void set_job(Job* job) {
    101     DCHECK(job != NULL);
    102     // Identify which job the request is waiting on.
    103     job_ = job;
    104   }
    105 
    106   void OnComplete(int error, const AddressList& addrlist) {
    107     if (error == OK)
    108       addresses_->SetFrom(addrlist, port());
    109     callback_->Run(error);
    110   }
    111 
    112   int port() const {
    113     return info_.port();
    114   }
    115 
    116   Job* job() const {
    117     return job_;
    118   }
    119 
    120   LoadLog* load_log() const {
    121     return load_log_;
    122   }
    123 
    124   int id() const {
    125     return id_;
    126   }
    127 
    128   const RequestInfo& info() const {
    129     return info_;
    130   }
    131 
    132  private:
    133   scoped_refptr<LoadLog> load_log_;
    134 
    135   // Unique ID for this request. Used by observers to identify requests.
    136   int id_;
    137 
    138   // The request info that started the request.
    139   RequestInfo info_;
    140 
    141   // The resolve job (running in worker pool) that this request is dependent on.
    142   Job* job_;
    143 
    144   // The user's callback to invoke when the request completes.
    145   CompletionCallback* callback_;
    146 
    147   // The address list to save result into.
    148   AddressList* addresses_;
    149 
    150   DISALLOW_COPY_AND_ASSIGN(Request);
    151 };
    152 
    153 //-----------------------------------------------------------------------------
    154 
    155 // Threadsafe log.
    156 class HostResolverImpl::RequestsTrace
    157     : public base::RefCountedThreadSafe<HostResolverImpl::RequestsTrace> {
    158  public:
    159   RequestsTrace() : log_(new LoadLog(LoadLog::kUnbounded)) {}
    160 
    161   void Add(const std::string& msg) {
    162     AutoLock l(lock_);
    163     LoadLog::AddString(log_, msg);
    164   }
    165 
    166   void Get(LoadLog* out) {
    167     AutoLock l(lock_);
    168     out->Append(log_);
    169   }
    170 
    171   void Clear() {
    172     AutoLock l(lock_);
    173     log_ = new LoadLog(LoadLog::kUnbounded);
    174   }
    175 
    176  private:
    177   Lock lock_;
    178   scoped_refptr<LoadLog> log_;
    179 };
    180 
    181 //-----------------------------------------------------------------------------
    182 
    183 // This class represents a request to the worker pool for a "getaddrinfo()"
    184 // call.
    185 class HostResolverImpl::Job
    186     : public base::RefCountedThreadSafe<HostResolverImpl::Job> {
    187  public:
    188   Job(int id, HostResolverImpl* resolver, const Key& key,
    189       RequestsTrace* requests_trace)
    190       : id_(id), key_(key),
    191         resolver_(resolver),
    192         origin_loop_(MessageLoop::current()),
    193         resolver_proc_(resolver->effective_resolver_proc()),
    194         requests_trace_(requests_trace),
    195         error_(OK) {
    196     if (requests_trace_) {
    197       requests_trace_->Add(StringPrintf(
    198           "Created job j%d for {hostname='%s', address_family=%d}",
    199           id_, key.hostname.c_str(),
    200           static_cast<int>(key.address_family)));
    201     }
    202   }
    203 
    204   // Attaches a request to this job. The job takes ownership of |req| and will
    205   // take care to delete it.
    206   void AddRequest(Request* req) {
    207     if (requests_trace_) {
    208       requests_trace_->Add(StringPrintf(
    209           "Attached request r%d to job j%d", req->id(), id_));
    210     }
    211 
    212     req->set_job(this);
    213     requests_.push_back(req);
    214   }
    215 
    216   // Called from origin loop.
    217   void Start() {
    218     if (requests_trace_)
    219       requests_trace_->Add(StringPrintf("Starting job j%d", id_));
    220 
    221     // Dispatch the job to a worker thread.
    222     if (!WorkerPool::PostTask(FROM_HERE,
    223             NewRunnableMethod(this, &Job::DoLookup), true)) {
    224       NOTREACHED();
    225 
    226       // Since we could be running within Resolve() right now, we can't just
    227       // call OnLookupComplete().  Instead we must wait until Resolve() has
    228       // returned (IO_PENDING).
    229       error_ = ERR_UNEXPECTED;
    230       MessageLoop::current()->PostTask(
    231           FROM_HERE, NewRunnableMethod(this, &Job::OnLookupComplete));
    232     }
    233   }
    234 
    235   // Cancels the current job. Callable from origin thread.
    236   void Cancel() {
    237     HostResolver* resolver = resolver_;
    238     resolver_ = NULL;
    239 
    240     if (requests_trace_)
    241       requests_trace_->Add(StringPrintf("Cancelled job j%d", id_));
    242 
    243     // Mark the job as cancelled, so when worker thread completes it will
    244     // not try to post completion to origin loop.
    245     {
    246       AutoLock locked(origin_loop_lock_);
    247       origin_loop_ = NULL;
    248     }
    249 
    250     // We will call HostResolverImpl::CancelRequest(Request*) on each one
    251     // in order to notify any observers.
    252     for (RequestsList::const_iterator it = requests_.begin();
    253          it != requests_.end(); ++it) {
    254       HostResolverImpl::Request* req = *it;
    255       if (!req->was_cancelled())
    256         resolver->CancelRequest(req);
    257     }
    258   }
    259 
    260   // Called from origin thread.
    261   bool was_cancelled() const {
    262     return resolver_ == NULL;
    263   }
    264 
    265   // Called from origin thread.
    266   const Key& key() const {
    267     return key_;
    268   }
    269 
    270   // Called from origin thread.
    271   const RequestsList& requests() const {
    272     return requests_;
    273   }
    274 
    275   // Returns the first request attached to the job.
    276   const Request* initial_request() const {
    277     DCHECK_EQ(origin_loop_, MessageLoop::current());
    278     DCHECK(!requests_.empty());
    279     return requests_[0];
    280   }
    281 
    282  private:
    283   friend class base::RefCountedThreadSafe<HostResolverImpl::Job>;
    284 
    285   ~Job() {
    286     // Free the requests attached to this job.
    287     STLDeleteElements(&requests_);
    288   }
    289 
    290   void DoLookup() {
    291     if (requests_trace_) {
    292       requests_trace_->Add(StringPrintf(
    293           "[resolver thread] Running job j%d", id_));
    294     }
    295 
    296     // Running on the worker thread
    297     error_ = ResolveAddrInfo(resolver_proc_,
    298                              key_.hostname,
    299                              key_.address_family,
    300                              &results_);
    301 
    302     if (requests_trace_) {
    303       requests_trace_->Add(StringPrintf(
    304           "[resolver thread] Completed job j%d", id_));
    305     }
    306 
    307     Task* reply = NewRunnableMethod(this, &Job::OnLookupComplete);
    308 
    309     // The origin loop could go away while we are trying to post to it, so we
    310     // need to call its PostTask method inside a lock.  See ~HostResolver.
    311     {
    312       AutoLock locked(origin_loop_lock_);
    313       if (origin_loop_) {
    314         origin_loop_->PostTask(FROM_HERE, reply);
    315         reply = NULL;
    316       }
    317     }
    318 
    319     // Does nothing if it got posted.
    320     delete reply;
    321   }
    322 
    323   // Callback for when DoLookup() completes (runs on origin thread).
    324   void OnLookupComplete() {
    325     // Should be running on origin loop.
    326     // TODO(eroman): this is being hit by URLRequestTest.CancelTest*,
    327     // because MessageLoop::current() == NULL.
    328     //DCHECK_EQ(origin_loop_, MessageLoop::current());
    329     DCHECK(error_ || results_.head());
    330 
    331     if (requests_trace_)
    332       requests_trace_->Add(StringPrintf("Completing job j%d", id_));
    333 
    334     if (was_cancelled())
    335       return;
    336 
    337     DCHECK(!requests_.empty());
    338 
    339      // Use the port number of the first request.
    340     if (error_ == OK)
    341       results_.SetPort(requests_[0]->port());
    342 
    343     resolver_->OnJobComplete(this, error_, results_);
    344   }
    345 
    346   // Immutable. Can be read from either thread,
    347   const int id_;
    348 
    349   // Set on the origin thread, read on the worker thread.
    350   Key key_;
    351 
    352   // Only used on the origin thread (where Resolve was called).
    353   HostResolverImpl* resolver_;
    354   RequestsList requests_;  // The requests waiting on this job.
    355 
    356   // Used to post ourselves onto the origin thread.
    357   Lock origin_loop_lock_;
    358   MessageLoop* origin_loop_;
    359 
    360   // Hold an owning reference to the HostResolverProc that we are going to use.
    361   // This may not be the current resolver procedure by the time we call
    362   // ResolveAddrInfo, but that's OK... we'll use it anyways, and the owning
    363   // reference ensures that it remains valid until we are done.
    364   scoped_refptr<HostResolverProc> resolver_proc_;
    365 
    366   // Thread safe log to write details into, or NULL.
    367   scoped_refptr<RequestsTrace> requests_trace_;
    368 
    369   // Assigned on the worker thread, read on the origin thread.
    370   int error_;
    371   AddressList results_;
    372 
    373   DISALLOW_COPY_AND_ASSIGN(Job);
    374 };
    375 
    376 //-----------------------------------------------------------------------------
    377 
    378 // We rely on the priority enum values being sequential having starting at 0,
    379 // and increasing for lower priorities.
    380 COMPILE_ASSERT(HIGHEST == 0u &&
    381                LOWEST > HIGHEST &&
    382                NUM_PRIORITIES > LOWEST,
    383                priority_indexes_incompatible);
    384 
    385 // JobPool contains all the information relating to queued requests, including
    386 // the limits on how many jobs are allowed to be used for this category of
    387 // requests.
    388 class HostResolverImpl::JobPool {
    389  public:
    390   JobPool(size_t max_outstanding_jobs, size_t max_pending_requests)
    391       : num_outstanding_jobs_(0u) {
    392     SetConstraints(max_outstanding_jobs, max_pending_requests);
    393   }
    394 
    395   ~JobPool() {
    396     // Free the pending requests.
    397     for (size_t i = 0; i < arraysize(pending_requests_); ++i)
    398       STLDeleteElements(&pending_requests_[i]);
    399   }
    400 
    401   // Sets the constraints for this pool. See SetPoolConstraints() for the
    402   // specific meaning of these parameters.
    403   void SetConstraints(size_t max_outstanding_jobs,
    404                       size_t max_pending_requests) {
    405     CHECK(max_outstanding_jobs != 0u);
    406     max_outstanding_jobs_ = max_outstanding_jobs;
    407     max_pending_requests_ = max_pending_requests;
    408   }
    409 
    410   // Returns the number of pending requests enqueued to this pool.
    411   // A pending request is one waiting to be attached to a job.
    412   size_t GetNumPendingRequests() const {
    413     size_t total = 0u;
    414     for (size_t i = 0u; i < arraysize(pending_requests_); ++i)
    415       total += pending_requests_[i].size();
    416     return total;
    417   }
    418 
    419   bool HasPendingRequests() const {
    420     return GetNumPendingRequests() > 0u;
    421   }
    422 
    423   // Enqueues a request to this pool. As a result of enqueing this request,
    424   // the queue may have reached its maximum size. In this case, a request is
    425   // evicted from the queue, and returned. Otherwise returns NULL. The caller
    426   // is responsible for freeing the evicted request.
    427   Request* InsertPendingRequest(Request* req) {
    428     PendingRequestsQueue& q = pending_requests_[req->info().priority()];
    429     q.push_back(req);
    430 
    431     // If the queue is too big, kick out the lowest priority oldest request.
    432     if (GetNumPendingRequests() > max_pending_requests_) {
    433       // Iterate over the queues from lowest priority to highest priority.
    434       for (int i = static_cast<int>(arraysize(pending_requests_)) - 1;
    435            i >= 0; --i) {
    436         PendingRequestsQueue& q = pending_requests_[i];
    437         if (!q.empty()) {
    438           Request* req = q.front();
    439           q.pop_front();
    440           return req;
    441         }
    442       }
    443     }
    444 
    445     return NULL;
    446   }
    447 
    448   // Erases |req| from this container. Caller is responsible for freeing
    449   // |req| afterwards.
    450   void RemovePendingRequest(Request* req) {
    451     PendingRequestsQueue& q = pending_requests_[req->info().priority()];
    452     PendingRequestsQueue::iterator it = std::find(q.begin(), q.end(), req);
    453     DCHECK(it != q.end());
    454     q.erase(it);
    455   }
    456 
    457   // Removes and returns the highest priority pending request.
    458   Request* RemoveTopPendingRequest() {
    459     DCHECK(HasPendingRequests());
    460 
    461     for (size_t i = 0u; i < arraysize(pending_requests_); ++i) {
    462       PendingRequestsQueue& q = pending_requests_[i];
    463       if (!q.empty()) {
    464         Request* req = q.front();
    465         q.pop_front();
    466         return req;
    467       }
    468     }
    469 
    470     NOTREACHED();
    471     return NULL;
    472   }
    473 
    474   // Keeps track of a job that was just added/removed, and belongs to this pool.
    475   void AdjustNumOutstandingJobs(int offset) {
    476     DCHECK(offset == 1 || (offset == -1 && num_outstanding_jobs_ > 0u));
    477     num_outstanding_jobs_ += offset;
    478   }
    479 
    480   // Returns true if a new job can be created for this pool.
    481   bool CanCreateJob() const {
    482     return num_outstanding_jobs_ + 1u <= max_outstanding_jobs_;
    483   }
    484 
    485   // Removes any pending requests from the queue which are for the
    486   // same hostname/address-family as |job|, and attaches them to |job|.
    487   void MoveRequestsToJob(Job* job) {
    488     for (size_t i = 0u; i < arraysize(pending_requests_); ++i) {
    489       PendingRequestsQueue& q = pending_requests_[i];
    490       PendingRequestsQueue::iterator req_it = q.begin();
    491       while (req_it != q.end()) {
    492         Request* req = *req_it;
    493         Key req_key(req->info().hostname(), req->info().address_family());
    494         if (req_key == job->key()) {
    495           // Job takes ownership of |req|.
    496           job->AddRequest(req);
    497           req_it = q.erase(req_it);
    498         } else {
    499           ++req_it;
    500         }
    501       }
    502     }
    503   }
    504 
    505  private:
    506   typedef std::deque<Request*> PendingRequestsQueue;
    507 
    508   // Maximum number of concurrent jobs allowed to be started for requests
    509   // belonging to this pool.
    510   size_t max_outstanding_jobs_;
    511 
    512   // The current number of running jobs that were started for requests
    513   // belonging to this pool.
    514   size_t num_outstanding_jobs_;
    515 
    516   // The maximum number of requests we allow to be waiting on a job,
    517   // for this pool.
    518   size_t max_pending_requests_;
    519 
    520   // The requests which are waiting to be started for this pool.
    521   PendingRequestsQueue pending_requests_[NUM_PRIORITIES];
    522 };
    523 
    524 //-----------------------------------------------------------------------------
    525 
    526 HostResolverImpl::HostResolverImpl(
    527     HostResolverProc* resolver_proc,
    528     HostCache* cache,
    529     NetworkChangeNotifier* network_change_notifier,
    530     size_t max_jobs)
    531     : cache_(cache),
    532       max_jobs_(max_jobs),
    533       next_request_id_(0),
    534       next_job_id_(0),
    535       resolver_proc_(resolver_proc),
    536       default_address_family_(ADDRESS_FAMILY_UNSPECIFIED),
    537       shutdown_(false),
    538       network_change_notifier_(network_change_notifier) {
    539   DCHECK_GT(max_jobs, 0u);
    540 
    541   // It is cumbersome to expose all of the constraints in the constructor,
    542   // so we choose some defaults, which users can override later.
    543   job_pools_[POOL_NORMAL] = new JobPool(max_jobs, 100u * max_jobs);
    544 
    545 #if defined(OS_WIN)
    546   EnsureWinsockInit();
    547 #endif
    548   if (network_change_notifier_)
    549     network_change_notifier_->AddObserver(this);
    550 }
    551 
    552 HostResolverImpl::~HostResolverImpl() {
    553   // Cancel the outstanding jobs. Those jobs may contain several attached
    554   // requests, which will also be cancelled.
    555   for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
    556     it->second->Cancel();
    557 
    558   // In case we are being deleted during the processing of a callback.
    559   if (cur_completing_job_)
    560     cur_completing_job_->Cancel();
    561 
    562   if (network_change_notifier_)
    563     network_change_notifier_->RemoveObserver(this);
    564 
    565   // Delete the job pools.
    566   for (size_t i = 0u; i < arraysize(job_pools_); ++i)
    567     delete job_pools_[i];
    568 }
    569 
    570 // TODO(eroman): Don't create cache entries for hostnames which are simply IP
    571 // address literals.
    572 int HostResolverImpl::Resolve(const RequestInfo& info,
    573                               AddressList* addresses,
    574                               CompletionCallback* callback,
    575                               RequestHandle* out_req,
    576                               LoadLog* load_log) {
    577   if (shutdown_)
    578     return ERR_UNEXPECTED;
    579 
    580   // Choose a unique ID number for observers to see.
    581   int request_id = next_request_id_++;
    582 
    583   // Update the load log and notify registered observers.
    584   OnStartRequest(load_log, request_id, info);
    585 
    586   // Build a key that identifies the request in the cache and in the
    587   // outstanding jobs map.
    588   Key key(info.hostname(), info.address_family());
    589   if (key.address_family == ADDRESS_FAMILY_UNSPECIFIED)
    590     key.address_family = default_address_family_;
    591 
    592   // If we have an unexpired cache entry, use it.
    593   if (info.allow_cached_response() && cache_.get()) {
    594     const HostCache::Entry* cache_entry = cache_->Lookup(
    595         key, base::TimeTicks::Now());
    596     if (cache_entry) {
    597       int error = cache_entry->error;
    598       if (error == OK)
    599         addresses->SetFrom(cache_entry->addrlist, info.port());
    600 
    601       // Update the load log and notify registered observers.
    602       OnFinishRequest(load_log, request_id, info, error);
    603 
    604       return error;
    605     }
    606   }
    607 
    608   // If no callback was specified, do a synchronous resolution.
    609   if (!callback) {
    610     AddressList addrlist;
    611     int error = ResolveAddrInfo(
    612         effective_resolver_proc(), key.hostname, key.address_family, &addrlist);
    613     if (error == OK) {
    614       addrlist.SetPort(info.port());
    615       *addresses = addrlist;
    616     }
    617 
    618     // Write to cache.
    619     if (cache_.get())
    620       cache_->Set(key, error, addrlist, base::TimeTicks::Now());
    621 
    622     // Update the load log and notify registered observers.
    623     OnFinishRequest(load_log, request_id, info, error);
    624 
    625     return error;
    626   }
    627 
    628   // Create a handle for this request, and pass it back to the user if they
    629   // asked for it (out_req != NULL).
    630   Request* req = new Request(load_log, request_id, info, callback, addresses);
    631   if (out_req)
    632     *out_req = reinterpret_cast<RequestHandle>(req);
    633 
    634   // Next we need to attach our request to a "job". This job is responsible for
    635   // calling "getaddrinfo(hostname)" on a worker thread.
    636   scoped_refptr<Job> job;
    637 
    638   // If there is already an outstanding job to resolve |key|, use
    639   // it. This prevents starting concurrent resolves for the same hostname.
    640   job = FindOutstandingJob(key);
    641   if (job) {
    642     job->AddRequest(req);
    643   } else {
    644     JobPool* pool = GetPoolForRequest(req);
    645     if (CanCreateJobForPool(*pool)) {
    646       CreateAndStartJob(req);
    647     } else {
    648       return EnqueueRequest(pool, req);
    649     }
    650   }
    651 
    652   // Completion happens during OnJobComplete(Job*).
    653   return ERR_IO_PENDING;
    654 }
    655 
    656 // See OnJobComplete(Job*) for why it is important not to clean out
    657 // cancelled requests from Job::requests_.
    658 void HostResolverImpl::CancelRequest(RequestHandle req_handle) {
    659   if (shutdown_) {
    660     // TODO(eroman): temp hack for: http://crbug.com/18373
    661     // Because we destroy outstanding requests during Shutdown(),
    662     // |req_handle| is already cancelled.
    663     LOG(ERROR) << "Called HostResolverImpl::CancelRequest() after Shutdown().";
    664     StackTrace().PrintBacktrace();
    665     return;
    666   }
    667   Request* req = reinterpret_cast<Request*>(req_handle);
    668   DCHECK(req);
    669 
    670   scoped_ptr<Request> request_deleter;  // Frees at end of function.
    671 
    672   if (!req->job()) {
    673     // If the request was not attached to a job yet, it must have been
    674     // enqueued into a pool. Remove it from that pool's queue.
    675     // Otherwise if it was attached to a job, the job is responsible for
    676     // deleting it.
    677     JobPool* pool = GetPoolForRequest(req);
    678     pool->RemovePendingRequest(req);
    679     request_deleter.reset(req);
    680   }
    681 
    682   // NULL out the fields of req, to mark it as cancelled.
    683   req->MarkAsCancelled();
    684   OnCancelRequest(req->load_log(), req->id(), req->info());
    685 }
    686 
    687 void HostResolverImpl::AddObserver(HostResolver::Observer* observer) {
    688   observers_.push_back(observer);
    689 }
    690 
    691 void HostResolverImpl::RemoveObserver(HostResolver::Observer* observer) {
    692   ObserversList::iterator it =
    693       std::find(observers_.begin(), observers_.end(), observer);
    694 
    695   // Observer must exist.
    696   DCHECK(it != observers_.end());
    697 
    698   observers_.erase(it);
    699 }
    700 
    701 void HostResolverImpl::Shutdown() {
    702   shutdown_ = true;
    703 
    704   // Cancel the outstanding jobs.
    705   for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
    706     it->second->Cancel();
    707   jobs_.clear();
    708 }
    709 
    710 void HostResolverImpl::ClearRequestsTrace() {
    711   if (requests_trace_)
    712     requests_trace_->Clear();
    713 }
    714 
    715 void HostResolverImpl::EnableRequestsTracing(bool enable) {
    716   requests_trace_ = enable ? new RequestsTrace : NULL;
    717   if (enable) {
    718     // Print the state of the world when logging was started.
    719     requests_trace_->Add("Enabled tracing");
    720     requests_trace_->Add(StringPrintf(
    721         "Current num outstanding jobs: %d",
    722         static_cast<int>(jobs_.size())));
    723 
    724     size_t total = 0u;
    725     for (size_t i = 0; i < arraysize(job_pools_); ++i)
    726       total += job_pools_[i]->GetNumPendingRequests();
    727 
    728     requests_trace_->Add(StringPrintf(
    729         "Number of queued requests: %d", static_cast<int>(total)));
    730   }
    731 }
    732 
    733 bool HostResolverImpl::IsRequestsTracingEnabled() const {
    734   return !!requests_trace_;  // Cast to bool.
    735 }
    736 
    737 scoped_refptr<LoadLog> HostResolverImpl::GetRequestsTrace() {
    738   if (!requests_trace_)
    739     return NULL;
    740 
    741   scoped_refptr<LoadLog> copy_of_log = new LoadLog(LoadLog::kUnbounded);
    742   requests_trace_->Get(copy_of_log);
    743   return copy_of_log;
    744 }
    745 
    746 void HostResolverImpl::SetPoolConstraints(JobPoolIndex pool_index,
    747                                           size_t max_outstanding_jobs,
    748                                           size_t max_pending_requests) {
    749   CHECK(pool_index >= 0);
    750   CHECK(pool_index < POOL_COUNT);
    751   CHECK(jobs_.empty()) << "Can only set constraints during setup";
    752   JobPool* pool = job_pools_[pool_index];
    753   pool->SetConstraints(max_outstanding_jobs, max_pending_requests);
    754 }
    755 
    756 void HostResolverImpl::AddOutstandingJob(Job* job) {
    757   scoped_refptr<Job>& found_job = jobs_[job->key()];
    758   DCHECK(!found_job);
    759   found_job = job;
    760 
    761   JobPool* pool = GetPoolForRequest(job->initial_request());
    762   pool->AdjustNumOutstandingJobs(1);
    763 }
    764 
    765 HostResolverImpl::Job* HostResolverImpl::FindOutstandingJob(const Key& key) {
    766   JobMap::iterator it = jobs_.find(key);
    767   if (it != jobs_.end())
    768     return it->second;
    769   return NULL;
    770 }
    771 
    772 void HostResolverImpl::RemoveOutstandingJob(Job* job) {
    773   JobMap::iterator it = jobs_.find(job->key());
    774   DCHECK(it != jobs_.end());
    775   DCHECK_EQ(it->second.get(), job);
    776   jobs_.erase(it);
    777 
    778   JobPool* pool = GetPoolForRequest(job->initial_request());
    779   pool->AdjustNumOutstandingJobs(-1);
    780 }
    781 
    782 void HostResolverImpl::OnJobComplete(Job* job,
    783                                      int error,
    784                                      const AddressList& addrlist) {
    785   RemoveOutstandingJob(job);
    786 
    787   // Write result to the cache.
    788   if (cache_.get())
    789     cache_->Set(job->key(), error, addrlist, base::TimeTicks::Now());
    790 
    791   // Make a note that we are executing within OnJobComplete() in case the
    792   // HostResolver is deleted by a callback invocation.
    793   DCHECK(!cur_completing_job_);
    794   cur_completing_job_ = job;
    795 
    796   // Try to start any queued requests now that a job-slot has freed up.
    797   ProcessQueuedRequests();
    798 
    799   // Complete all of the requests that were attached to the job.
    800   for (RequestsList::const_iterator it = job->requests().begin();
    801        it != job->requests().end(); ++it) {
    802     Request* req = *it;
    803     if (!req->was_cancelled()) {
    804       DCHECK_EQ(job, req->job());
    805 
    806       // Update the load log and notify registered observers.
    807       OnFinishRequest(req->load_log(), req->id(), req->info(), error);
    808 
    809       req->OnComplete(error, addrlist);
    810 
    811       // Check if the job was cancelled as a result of running the callback.
    812       // (Meaning that |this| was deleted).
    813       if (job->was_cancelled())
    814         return;
    815     }
    816   }
    817 
    818   cur_completing_job_ = NULL;
    819 }
    820 
    821 void HostResolverImpl::OnStartRequest(LoadLog* load_log,
    822                                       int request_id,
    823                                       const RequestInfo& info) {
    824   LoadLog::BeginEvent(load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL);
    825 
    826   if (requests_trace_) {
    827     requests_trace_->Add(StringPrintf(
    828         "Received request r%d for {hostname='%s', port=%d, priority=%d, "
    829         "speculative=%d, address_family=%d, allow_cached=%d, referrer='%s'}",
    830          request_id,
    831          info.hostname().c_str(),
    832          info.port(),
    833          static_cast<int>(info.priority()),
    834          static_cast<int>(info.is_speculative()),
    835          static_cast<int>(info.address_family()),
    836          static_cast<int>(info.allow_cached_response()),
    837          info.referrer().spec().c_str()));
    838   }
    839 
    840   // Notify the observers of the start.
    841   if (!observers_.empty()) {
    842     LoadLog::BeginEvent(
    843         load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART);
    844 
    845     for (ObserversList::iterator it = observers_.begin();
    846          it != observers_.end(); ++it) {
    847       (*it)->OnStartResolution(request_id, info);
    848     }
    849 
    850     LoadLog::EndEvent(
    851         load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART);
    852   }
    853 }
    854 
    855 void HostResolverImpl::OnFinishRequest(LoadLog* load_log,
    856                                        int request_id,
    857                                        const RequestInfo& info,
    858                                        int error) {
    859   if (requests_trace_) {
    860     requests_trace_->Add(StringPrintf(
    861         "Finished request r%d with error=%d", request_id, error));
    862   }
    863 
    864   // Notify the observers of the completion.
    865   if (!observers_.empty()) {
    866     LoadLog::BeginEvent(
    867         load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONFINISH);
    868 
    869     bool was_resolved = error == OK;
    870     for (ObserversList::iterator it = observers_.begin();
    871          it != observers_.end(); ++it) {
    872       (*it)->OnFinishResolutionWithStatus(request_id, was_resolved, info);
    873     }
    874 
    875     LoadLog::EndEvent(
    876         load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONFINISH);
    877   }
    878 
    879   LoadLog::EndEvent(load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL);
    880 }
    881 
    882 void HostResolverImpl::OnCancelRequest(LoadLog* load_log,
    883                                        int request_id,
    884                                        const RequestInfo& info) {
    885   LoadLog::AddEvent(load_log, LoadLog::TYPE_CANCELLED);
    886 
    887   if (requests_trace_)
    888     requests_trace_->Add(StringPrintf("Cancelled request r%d", request_id));
    889 
    890   // Notify the observers of the cancellation.
    891   if (!observers_.empty()) {
    892     LoadLog::BeginEvent(
    893         load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONCANCEL);
    894 
    895     for (ObserversList::iterator it = observers_.begin();
    896          it != observers_.end(); ++it) {
    897       (*it)->OnCancelResolution(request_id, info);
    898     }
    899 
    900     LoadLog::EndEvent(
    901         load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONCANCEL);
    902   }
    903 
    904   LoadLog::EndEvent(load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL);
    905 }
    906 
    907 void HostResolverImpl::OnIPAddressChanged() {
    908   if (cache_.get())
    909     cache_->clear();
    910 }
    911 
    912 // static
    913 HostResolverImpl::JobPoolIndex HostResolverImpl::GetJobPoolIndexForRequest(
    914     const Request* req) {
    915   return POOL_NORMAL;
    916 }
    917 
    918 bool HostResolverImpl::CanCreateJobForPool(const JobPool& pool) const {
    919   DCHECK_LE(jobs_.size(), max_jobs_);
    920 
    921   // We can't create another job if it would exceed the global total.
    922   if (jobs_.size() + 1 > max_jobs_)
    923     return false;
    924 
    925   // Check whether the pool's constraints are met.
    926   return pool.CanCreateJob();
    927 }
    928 
    929 void HostResolverImpl::ProcessQueuedRequests() {
    930   // Find the highest priority request that can be scheduled.
    931   Request* top_req = NULL;
    932   for (size_t i = 0; i < arraysize(job_pools_); ++i) {
    933     JobPool* pool = job_pools_[i];
    934     if (pool->HasPendingRequests() && CanCreateJobForPool(*pool)) {
    935       top_req = pool->RemoveTopPendingRequest();
    936       break;
    937     }
    938   }
    939 
    940   if (!top_req)
    941     return;
    942 
    943   scoped_refptr<Job> job = CreateAndStartJob(top_req);
    944 
    945   // Search for any other pending request which can piggy-back off this job.
    946   for (size_t pool_i = 0; pool_i < POOL_COUNT; ++pool_i) {
    947     JobPool* pool = job_pools_[pool_i];
    948     pool->MoveRequestsToJob(job);
    949   }
    950 }
    951 
    952 HostResolverImpl::Job* HostResolverImpl::CreateAndStartJob(Request* req) {
    953   DCHECK(CanCreateJobForPool(*GetPoolForRequest(req)));
    954   Key key(req->info().hostname(), req->info().address_family());
    955   scoped_refptr<Job> job = new Job(next_job_id_++, this, key, requests_trace_);
    956   job->AddRequest(req);
    957   AddOutstandingJob(job);
    958   job->Start();
    959   return job.get();
    960 }
    961 
    962 int HostResolverImpl::EnqueueRequest(JobPool* pool, Request* req) {
    963   if (requests_trace_)
    964     requests_trace_->Add(StringPrintf("Queued request r%d", req->id()));
    965 
    966   scoped_ptr<Request> req_evicted_from_queue(
    967       pool->InsertPendingRequest(req));
    968 
    969   // If the queue has become too large, we need to kick something out.
    970   if (req_evicted_from_queue.get()) {
    971     Request* r = req_evicted_from_queue.get();
    972     int error = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE;
    973 
    974     if (requests_trace_)
    975       requests_trace_->Add(StringPrintf("Evicted request r%d", r->id()));
    976 
    977     OnFinishRequest(r->load_log(), r->id(), r->info(), error);
    978 
    979     if (r == req)
    980       return error;
    981 
    982     r->OnComplete(error, AddressList());
    983   }
    984 
    985   return ERR_IO_PENDING;
    986 }
    987 
    988 }  // namespace net
    989