ClpMap.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 #ifndef SQUID_SRC_BASE_CLPMAP_H
10 #define SQUID_SRC_BASE_CLPMAP_H
11 
12 #include "mem/PoolingAllocator.h"
13 #include "SquidMath.h"
14 #include "time/gadgets.h"
15 
16 #include <functional>
17 #include <limits>
18 #include <list>
19 #include <optional>
20 #include <unordered_map>
21 
22 template<class Value>
23 uint64_t
24 DefaultMemoryUsage(const Value &e)
25 {
26  return sizeof(e);
27 }
28 
39 template <class Key, class Value, uint64_t MemoryUsedBy(const Value &) = DefaultMemoryUsage>
40 class ClpMap
41 {
42 public:
43  using key_type = Key;
44  using mapped_type = Value;
45 
47  using Ttl = int;
48 
50  class Entry
51  {
52  public:
53  Entry(const Key &, const Value &, const Ttl);
54 
56  bool expired() const { return expires < squid_curtime; }
57 
58  public:
59  Key key;
60  Value value;
61  time_t expires = 0;
62  uint64_t memCounted = 0;
63  };
64 
66  using Entries = std::list<Entry, PoolingAllocator<Entry> >;
67  using EntriesIterator = typename Entries::iterator;
68  using ConstEntriesIterator = typename Entries::const_iterator;
69 
70  explicit ClpMap(const uint64_t capacity) { setMemLimit(capacity); }
71  ClpMap(uint64_t capacity, Ttl defaultTtl);
72  ~ClpMap() = default;
73 
74  // copying disabled because it may be expensive for large maps
75  // moving (implicitly) disabled for simplicity sake
76  ClpMap(const ClpMap &) = delete;
77  ClpMap &operator =(const ClpMap &) = delete;
78 
83  const Value *get(const Key &);
84 
88  bool add(const Key &, const Value &, Ttl);
89 
91  bool add(const Key &key, const Value &v) { return add(key, v, defaultTtl_); }
92 
94  void del(const Key &);
95 
97  void setMemLimit(uint64_t newLimit);
98 
100  uint64_t memLimit() const { return memLimit_; }
101 
103  uint64_t freeMem() const { return memLimit() - memoryUsed(); }
104 
106  uint64_t memoryUsed() const { return memUsed_; }
107 
109  size_t entries() const { return entries_.size(); }
110 
114  ConstEntriesIterator cbegin() const { return entries_.cbegin(); }
115  ConstEntriesIterator cend() const { return entries_.cend(); }
117  ConstEntriesIterator begin() const { return cbegin(); }
118  ConstEntriesIterator end() const { return cend(); }
119 
120 private:
121  using IndexItem = std::pair<const Key, EntriesIterator>;
123  using Index = std::unordered_map<Key, EntriesIterator, std::hash<Key>, std::equal_to<Key>, PoolingAllocator<IndexItem> >;
124  using IndexIterator = typename Index::iterator;
125 
126  static std::optional<uint64_t> MemoryCountedFor(const Key &, const Value &);
127 
128  void trim(uint64_t wantSpace);
129  void erase(const IndexIterator &);
130  IndexIterator find(const Key &);
131 
134 
137 
140 
142  uint64_t memLimit_ = 0;
143 
145  uint64_t memUsed_ = 0;
146 };
147 
148 template <class Key, class Value, uint64_t MemoryUsedBy(const Value &)>
149 ClpMap<Key, Value, MemoryUsedBy>::ClpMap(const uint64_t capacity, const Ttl defaultTtl):
150  defaultTtl_(defaultTtl)
151 {
152  assert(defaultTtl >= 0);
153  setMemLimit(capacity);
154 }
155 
156 template <class Key, class Value, uint64_t MemoryUsedBy(const Value &)>
157 void
159 {
160  if (memUsed_ > newLimit)
161  trim(memLimit_ - newLimit);
162  memLimit_ = newLimit;
163 }
164 
166 template <class Key, class Value, uint64_t MemoryUsedBy(const Value &)>
169 {
170  const auto i = index_.find(key);
171  if (i == index_.end())
172  return i;
173 
174  const auto entryPosition = i->second;
175  if (!entryPosition->expired()) {
176  if (entryPosition != entries_.begin())
177  entries_.splice(entries_.begin(), entries_, entryPosition);
178  return i;
179  }
180  // else fall through to cleanup
181 
182  erase(i);
183  return index_.end();
184 }
185 
186 template <class Key, class Value, uint64_t MemoryUsedBy(const Value &)>
187 const Value *
189 {
190  const auto i = find(key);
191  if (i != index_.end()) {
192  const auto &entry = *(i->second);
193  return &entry.value;
194  }
195  return nullptr;
196 }
197 
198 template <class Key, class Value, uint64_t MemoryUsedBy(const Value &)>
199 std::optional<uint64_t>
201 {
202  // Both storage and index store keys, but we count keySz once, assuming that
203  // copying a Key does not consume more memory. This assumption holds for
204  // Key=SBuf, but, ideally, we should be outsourcing this decision to another
205  // configurable function, storing each key once, or hard-coding Key=SBuf.
206  const auto keySz = k.length();
207 
208  // approximate calculation (e.g., containers store wrappers not value_types)
209  return NaturalSum<uint64_t>(
210  keySz,
211  // storage
212  sizeof(typename Entries::value_type),
213  MemoryUsedBy(v),
214  // index
215  sizeof(typename Index::value_type));
216 }
217 
218 template <class Key, class Value, uint64_t MemoryUsedBy(const Value &)>
219 bool
220 ClpMap<Key, Value, MemoryUsedBy>::add(const Key &key, const Value &v, const Ttl ttl)
221 {
222  // optimization: avoid del() search, MemoryCountedFor() in always-empty maps
223  if (memLimit() == 0)
224  return false;
225 
226  del(key);
227 
228  if (ttl < 0)
229  return false; // already expired; will never be returned by get()
230 
231  const auto memoryRequirements = MemoryCountedFor(key, v);
232  if (!memoryRequirements)
233  return false; // cannot even compute memory requirements
234 
235  const auto wantSpace = memoryRequirements.value();
236  if (wantSpace > memLimit() || wantSpace == 0) // 0 is 64-bit integer overflow
237  return false; // will never fit
238  trim(wantSpace);
239 
240  auto &addedEntry = entries_.emplace_front(key, v, ttl);
241  index_.emplace(key, entries_.begin());
242 
243  addedEntry.memCounted = wantSpace;
244  memUsed_ += wantSpace;
245  assert(memUsed_ >= wantSpace); // no overflows
246  return true;
247 }
248 
250 template <class Key, class Value, uint64_t MemoryUsedBy(const Value &)>
251 void
253 {
254  assert(i != index_.end());
255  const auto entryPosition = i->second;
256 
257  assert(entryPosition != entries_.end());
258  const auto sz = entryPosition->memCounted;
259  assert(memUsed_ >= sz);
260  memUsed_ -= sz;
261 
262  index_.erase(i); // destroys a "pointer" to our Entry
263  entries_.erase(entryPosition); // destroys our Entry
264 }
265 
266 template <class Key, class Value, uint64_t MemoryUsedBy(const Value &)>
267 void
269 {
270  const auto i = find(key);
271  if (i != index_.end())
272  erase(i);
273 }
274 
276 template <class Key, class Value, uint64_t MemoryUsedBy(const Value &)>
277 void
278 ClpMap<Key, Value, MemoryUsedBy>::trim(const uint64_t wantSpace)
279 {
280  assert(wantSpace <= memLimit()); // no infinite loops and in-vain trimming
281  while (freeMem() < wantSpace) {
282  assert(!entries_.empty());
283  // TODO: Purge expired entries first. They are useless, but their
284  // presence may lead to purging potentially useful fresh entries here.
285  del(entries_.rbegin()->key);
286  }
287 }
288 
289 template <class Key, class Value, uint64_t MemoryUsedBy(const Value &)>
290 ClpMap<Key, Value, MemoryUsedBy>::Entry::Entry(const Key &aKey, const Value &v, const Ttl ttl) :
291  key(aKey),
292  value(v),
293  expires(0) // reset below
294 {
296 }
297 
298 #endif /* SQUID_SRC_BASE_CLPMAP_H */
299 
ConstEntriesIterator cend() const
Definition: ClpMap.h:115
uint64_t freeMem() const
The free space of the map.
Definition: ClpMap.h:103
ConstEntriesIterator begin() const
range-based for loop support;
Definition: ClpMap.h:117
Entry(const Key &, const Value &, const Ttl)
Definition: ClpMap.h:290
ClpMap & operator=(const ClpMap &)=delete
typename Index::iterator IndexIterator
Definition: ClpMap.h:124
ClpMap(const uint64_t capacity)
Definition: ClpMap.h:70
const A & max(A const &lhs, A const &rhs)
uint64_t memCounted
memory accounted for this entry in our ClpMap
Definition: ClpMap.h:62
uint64_t memLimit() const
The memory capacity for the map.
Definition: ClpMap.h:100
~ClpMap()=default
const Value * get(const Key &)
Definition: ClpMap.h:188
ConstEntriesIterator cbegin() const
Definition: ClpMap.h:114
uint64_t memLimit_
the maximum memory we are allowed to use for all cached entries
Definition: ClpMap.h:142
void trim(uint64_t wantSpace)
purges entries to make free memory large enough to fit wantSpace bytes
Definition: ClpMap.h:278
Ttl defaultTtl_
entry TTL to use if none provided to add()
Definition: ClpMap.h:139
Definition: ClpMap.h:40
Index index_
entries_ positions indexed by the entry key
Definition: ClpMap.h:136
S SetToNaturalSumOrMax(S &var, const Args... args)
Definition: SquidMath.h:176
time_t expires
get() stops returning the entry after this time
Definition: ClpMap.h:61
#define assert(EX)
Definition: assert.h:17
int Ttl
maximum desired entry caching duration (a.k.a. TTL), in seconds
Definition: ClpMap.h:47
void del(const Key &)
Remove the corresponding entry (if any)
Definition: ClpMap.h:268
typename Entries::iterator EntriesIterator
Definition: ClpMap.h:67
static std::optional< uint64_t > MemoryCountedFor(const Key &, const Value &)
Definition: ClpMap.h:200
size_t entries() const
The number of currently stored entries, including expired ones.
Definition: ClpMap.h:109
time_t squid_curtime
Definition: stub_libtime.cc:20
Entries entries_
cached entries, including expired ones, in LRU order
Definition: ClpMap.h:133
bool add(const Key &, const Value &, Ttl)
Definition: ClpMap.h:220
std::list< Entry, PoolingAllocator< Entry > > Entries
Entries in LRU order.
Definition: ClpMap.h:66
bool add(const Key &key, const Value &v)
Copy the given value into the map (with the given key and default TTL)
Definition: ClpMap.h:91
void setMemLimit(uint64_t newLimit)
Reset the memory capacity for this map, purging if needed.
Definition: ClpMap.h:158
static StoreEntry * addedEntry(Store::Disk *aStore, String name, String, String)
std::unordered_map< Key, EntriesIterator, std::hash< Key >, std::equal_to< Key >, PoolingAllocator< IndexItem > > Index
key:entry_position mapping for fast entry lookups by key
Definition: ClpMap.h:123
STL Allocator that uses Squid memory pools for memory management.
ConstEntriesIterator end() const
Definition: ClpMap.h:118
Value mapped_type
Definition: ClpMap.h:44
uint64_t memoryUsed() const
The current (approximate) memory usage of the map.
Definition: ClpMap.h:106
bool expired() const
whether the entry is stale
Definition: ClpMap.h:56
uint64_t DefaultMemoryUsage(const Value &e)
Definition: ClpMap.h:24
Key key
the entry search key; see ClpMap::get()
Definition: ClpMap.h:59
uint64_t memUsed_
the total amount of memory we currently use for all cached entries
Definition: ClpMap.h:145
typename Entries::const_iterator ConstEntriesIterator
Definition: ClpMap.h:68
std::pair< const Key, EntriesIterator > IndexItem
Definition: ClpMap.h:121
void erase(const IndexIterator &)
removes the cached entry (identified by its index) from the map
Definition: ClpMap.h:252
the keeper of cache entry Key, Value, and caching-related entry metadata
Definition: ClpMap.h:50
IndexIterator find(const Key &)
Definition: ClpMap.h:168
Value value
cached value provided by the map user
Definition: ClpMap.h:60
int unsigned int
Definition: stub_fd.cc:19
Key key_type
Definition: ClpMap.h:43

 

Introduction

Documentation

Support

Miscellaneous