summaryrefslogtreecommitdiffstats
path: root/net/base/host_cache.cc
blob: 28173d273f164c58e71de244011ea4e1a0887bb8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/host_cache.h"

#include "base/logging.h"
#include "net/base/net_errors.h"

namespace net {

//-----------------------------------------------------------------------------

HostCache::Entry::Entry(int error,
                        const AddressList& addrlist,
                        base::TimeTicks expiration)
    : error(error), addrlist(addrlist), expiration(expiration) {
}

HostCache::Entry::~Entry() {
}

//-----------------------------------------------------------------------------

HostCache::HostCache(size_t max_entries,
                     base::TimeDelta success_entry_ttl,
                     base::TimeDelta failure_entry_ttl)
    : max_entries_(max_entries),
      success_entry_ttl_(success_entry_ttl),
      failure_entry_ttl_(failure_entry_ttl) {
}

HostCache::~HostCache() {
}

const HostCache::Entry* HostCache::Lookup(const Key& key,
                                          base::TimeTicks now) const {
  DCHECK(CalledOnValidThread());
  if (caching_is_disabled())
    return NULL;

  EntryMap::const_iterator it = entries_.find(key);
  if (it == entries_.end())
    return NULL;  // Not found.

  Entry* entry = it->second.get();
  if (CanUseEntry(entry, now))
    return entry;

  return NULL;
}

HostCache::Entry* HostCache::Set(const Key& key,
                                 int error,
                                 const AddressList& addrlist,
                                 base::TimeTicks now) {
  DCHECK(CalledOnValidThread());
  if (caching_is_disabled())
    return NULL;

  base::TimeTicks expiration = now +
      (error == OK ? success_entry_ttl_ : failure_entry_ttl_);

  scoped_refptr<Entry>& entry = entries_[key];
  if (!entry) {
    // Entry didn't exist, creating one now.
    Entry* ptr = new Entry(error, addrlist, expiration);
    entry = ptr;

    // Compact the cache if we grew it beyond limit -- exclude |entry| from
    // being pruned though!
    if (entries_.size() > max_entries_)
      Compact(now, ptr);
    return ptr;
  } else {
    // Update an existing cache entry.
    entry->error = error;
    entry->addrlist = addrlist;
    entry->expiration = expiration;
    return entry.get();
  }
}

void HostCache::clear() {
  DCHECK(CalledOnValidThread());
  entries_.clear();
}

size_t HostCache::size() const {
  DCHECK(CalledOnValidThread());
  return entries_.size();
}

size_t HostCache::max_entries() const {
  DCHECK(CalledOnValidThread());
  return max_entries_;
}

base::TimeDelta HostCache::success_entry_ttl() const {
  DCHECK(CalledOnValidThread());
  return success_entry_ttl_;
}

base::TimeDelta HostCache::failure_entry_ttl() const {
  DCHECK(CalledOnValidThread());
  return failure_entry_ttl_;
}

// Note that this map may contain expired entries.
const HostCache::EntryMap& HostCache::entries() const {
  DCHECK(CalledOnValidThread());
  return entries_;
}

// static
bool HostCache::CanUseEntry(const Entry* entry, const base::TimeTicks now) {
  return entry->expiration > now;
}

void HostCache::Compact(base::TimeTicks now, const Entry* pinned_entry) {
  // Clear out expired entries.
  for (EntryMap::iterator it = entries_.begin(); it != entries_.end(); ) {
    Entry* entry = (it->second).get();
    if (entry != pinned_entry && !CanUseEntry(entry, now)) {
      entries_.erase(it++);
    } else {
      ++it;
    }
  }

  if (entries_.size() <= max_entries_)
    return;

  // If we still have too many entries, start removing unexpired entries
  // at random.
  // TODO(eroman): this eviction policy could be better (access count FIFO
  // or whatever).
  for (EntryMap::iterator it = entries_.begin();
       it != entries_.end() && entries_.size() > max_entries_; ) {
    Entry* entry = (it->second).get();
    if (entry != pinned_entry) {
      entries_.erase(it++);
    } else {
      ++it;
    }
  }

  if (entries_.size() > max_entries_)
    DLOG(WARNING) << "Still above max entries limit";
}

}  // namespace net