1// Copyright (c) 2009 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_cache.h" 6 7#include "base/logging.h" 8#include "net/base/net_errors.h" 9 10namespace net { 11 12//----------------------------------------------------------------------------- 13 14HostCache::Entry::Entry(int error, 15 const AddressList& addrlist, 16 base::TimeTicks expiration) 17 : error(error), addrlist(addrlist), expiration(expiration) { 18} 19 20HostCache::Entry::~Entry() { 21} 22 23//----------------------------------------------------------------------------- 24 25HostCache::HostCache(size_t max_entries, 26 base::TimeDelta success_entry_ttl, 27 base::TimeDelta failure_entry_ttl) 28 : max_entries_(max_entries), 29 success_entry_ttl_(success_entry_ttl), 30 failure_entry_ttl_(failure_entry_ttl) { 31} 32 33HostCache::~HostCache() { 34} 35 36const HostCache::Entry* HostCache::Lookup(const Key& key, 37 base::TimeTicks now) const { 38 DCHECK(CalledOnValidThread()); 39 if (caching_is_disabled()) 40 return NULL; 41 42 EntryMap::const_iterator it = entries_.find(key); 43 if (it == entries_.end()) 44 return NULL; // Not found. 45 46 Entry* entry = it->second.get(); 47 if (CanUseEntry(entry, now)) 48 return entry; 49 50 return NULL; 51} 52 53HostCache::Entry* HostCache::Set(const Key& key, 54 int error, 55 const AddressList& addrlist, 56 base::TimeTicks now) { 57 DCHECK(CalledOnValidThread()); 58 if (caching_is_disabled()) 59 return NULL; 60 61 base::TimeTicks expiration = now + 62 (error == OK ? success_entry_ttl_ : failure_entry_ttl_); 63 64 scoped_refptr<Entry>& entry = entries_[key]; 65 if (!entry) { 66 // Entry didn't exist, creating one now. 67 Entry* ptr = new Entry(error, addrlist, expiration); 68 entry = ptr; 69 70 // Compact the cache if we grew it beyond limit -- exclude |entry| from 71 // being pruned though! 72 if (entries_.size() > max_entries_) 73 Compact(now, ptr); 74 return ptr; 75 } else { 76 // Update an existing cache entry. 77 entry->error = error; 78 entry->addrlist = addrlist; 79 entry->expiration = expiration; 80 return entry.get(); 81 } 82} 83 84void HostCache::clear() { 85 DCHECK(CalledOnValidThread()); 86 entries_.clear(); 87} 88 89size_t HostCache::size() const { 90 DCHECK(CalledOnValidThread()); 91 return entries_.size(); 92} 93 94size_t HostCache::max_entries() const { 95 DCHECK(CalledOnValidThread()); 96 return max_entries_; 97} 98 99base::TimeDelta HostCache::success_entry_ttl() const { 100 DCHECK(CalledOnValidThread()); 101 return success_entry_ttl_; 102} 103 104base::TimeDelta HostCache::failure_entry_ttl() const { 105 DCHECK(CalledOnValidThread()); 106 return failure_entry_ttl_; 107} 108 109// Note that this map may contain expired entries. 110const HostCache::EntryMap& HostCache::entries() const { 111 DCHECK(CalledOnValidThread()); 112 return entries_; 113} 114 115// static 116bool HostCache::CanUseEntry(const Entry* entry, const base::TimeTicks now) { 117 return entry->expiration > now; 118} 119 120void HostCache::Compact(base::TimeTicks now, const Entry* pinned_entry) { 121 // Clear out expired entries. 122 for (EntryMap::iterator it = entries_.begin(); it != entries_.end(); ) { 123 Entry* entry = (it->second).get(); 124 if (entry != pinned_entry && !CanUseEntry(entry, now)) { 125 entries_.erase(it++); 126 } else { 127 ++it; 128 } 129 } 130 131 if (entries_.size() <= max_entries_) 132 return; 133 134 // If we still have too many entries, start removing unexpired entries 135 // at random. 136 // TODO(eroman): this eviction policy could be better (access count FIFO 137 // or whatever). 138 for (EntryMap::iterator it = entries_.begin(); 139 it != entries_.end() && entries_.size() > max_entries_; ) { 140 Entry* entry = (it->second).get(); 141 if (entry != pinned_entry) { 142 entries_.erase(it++); 143 } else { 144 ++it; 145 } 146 } 147 148 if (entries_.size() > max_entries_) 149 DLOG(WARNING) << "Still above max entries limit"; 150} 151 152} // namespace net 153