proxygen
EvictingCacheMap.h
Go to the documentation of this file.
1 /*
2  * Copyright 2014-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
18 #include <algorithm>
19 #include <exception>
20 #include <functional>
21 
22 #include <boost/intrusive/list.hpp>
23 #include <boost/intrusive/unordered_set.hpp>
24 #include <boost/iterator/iterator_adaptor.hpp>
25 #include <boost/utility.hpp>
26 
27 #include <folly/lang/Exception.h>
28 
29 namespace folly {
30 
93 template <
94  class TKey,
95  class TValue,
96  class THash = std::hash<TKey>,
97  class TKeyEqual = std::equal_to<TKey>>
99  private:
100  // typedefs for brevity
101  struct Node;
102  struct KeyHasher;
103  struct KeyValueEqual;
104  typedef boost::intrusive::link_mode<boost::intrusive::safe_link> link_mode;
105  typedef boost::intrusive::unordered_set<
106  Node,
107  boost::intrusive::hash<KeyHasher>,
108  boost::intrusive::equal<KeyValueEqual>>
110  typedef boost::intrusive::list<Node> NodeList;
111  typedef std::pair<const TKey, TValue> TPair;
112 
113  public:
114  typedef std::function<void(TKey, TValue&&)> PruneHookCall;
115 
116  // iterator base : returns TPair on dereference
117  template <typename Value, typename TIterator>
118  class iterator_base : public boost::iterator_adaptor<
119  iterator_base<Value, TIterator>,
120  TIterator,
121  Value,
122  boost::bidirectional_traversal_tag> {
123  public:
125  explicit iterator_base(TIterator it)
126  : iterator_base::iterator_adaptor_(it) {}
127  Value& dereference() const {
128  return this->base_reference()->pr;
129  }
130  };
131 
132  // iterators
133  typedef iterator_base<TPair, typename NodeList::iterator> iterator;
134  typedef iterator_base<const TPair, typename NodeList::const_iterator>
136  typedef iterator_base<TPair, typename NodeList::reverse_iterator>
138  typedef iterator_base<const TPair, typename NodeList::const_reverse_iterator>
140 
141  // the default map typedefs
142  using key_type = TKey;
143  using mapped_type = TValue;
144  using hasher = THash;
145 
154  std::size_t maxSize,
155  std::size_t clearSize = 1,
156  const THash& keyHash = THash(),
157  const TKeyEqual& keyEqual = TKeyEqual())
158  : nIndexBuckets_(std::max(maxSize / 2, std::size_t(kMinNumIndexBuckets))),
159  indexBuckets_(new typename NodeMap::bucket_type[nIndexBuckets_]),
161  keyHash_(keyHash),
162  keyEqual_(keyEqual),
164  maxSize_(maxSize),
165  clearSize_(clearSize) {}
166 
167  EvictingCacheMap(const EvictingCacheMap&) = delete;
168  EvictingCacheMap& operator=(const EvictingCacheMap&) = delete;
169  EvictingCacheMap(EvictingCacheMap&&) = default;
171 
173  setPruneHook(nullptr);
174  // ignore any potential exceptions from pruneHook_
175  pruneWithFailSafeOption(size(), nullptr, true);
176  }
177 
193  void setMaxSize(size_t maxSize, PruneHookCall pruneHook = nullptr) {
194  if (maxSize != 0 && maxSize < size()) {
195  // Prune the excess elements with our new constraints.
196  prune(std::max(size() - maxSize, clearSize_), pruneHook);
197  }
198  maxSize_ = maxSize;
199  }
200 
201  size_t getMaxSize() const {
202  return maxSize_;
203  }
204 
205  void setClearSize(size_t clearSize) {
206  clearSize_ = clearSize;
207  }
208 
215  bool exists(const TKey& key) const {
216  return findInIndex(key) != index_.end();
217  }
218 
226  TValue& get(const TKey& key) {
227  auto it = find(key);
228  if (it == end()) {
229  throw_exception<std::out_of_range>("Key does not exist");
230  }
231  return it->second;
232  }
233 
241  iterator find(const TKey& key) {
242  auto it = findInIndex(key);
243  if (it == index_.end()) {
244  return end();
245  }
246  lru_.erase(lru_.iterator_to(*it));
247  lru_.push_front(*it);
248  return iterator(lru_.iterator_to(*it));
249  }
250 
258  const TValue& getWithoutPromotion(const TKey& key) const {
259  auto it = findWithoutPromotion(key);
260  if (it == end()) {
261  throw_exception<std::out_of_range>("Key does not exist");
262  }
263  return it->second;
264  }
265 
266  TValue& getWithoutPromotion(const TKey& key) {
267  auto const& cThis = *this;
268  return const_cast<TValue&>(cThis.getWithoutPromotion(key));
269  }
270 
278  const_iterator findWithoutPromotion(const TKey& key) const {
279  auto it = findInIndex(key);
280  return (it == index_.end()) ? end() : const_iterator(lru_.iterator_to(*it));
281  }
282 
283  iterator findWithoutPromotion(const TKey& key) {
284  auto it = findInIndex(key);
285  return (it == index_.end()) ? end() : iterator(lru_.iterator_to(*it));
286  }
287 
293  bool erase(const TKey& key) {
294  auto it = findInIndex(key);
295  if (it == index_.end()) {
296  return false;
297  }
298  auto node = &(*it);
299  std::unique_ptr<Node> nptr(node);
300  lru_.erase(lru_.iterator_to(*node));
301  index_.erase(it);
302  return true;
303  }
304 
314  void set(
315  const TKey& key,
316  TValue value,
317  bool promote = true,
318  PruneHookCall pruneHook = nullptr) {
319  auto it = findInIndex(key);
320  if (it != index_.end()) {
321  it->pr.second = std::move(value);
322  if (promote) {
323  lru_.erase(lru_.iterator_to(*it));
324  lru_.push_front(*it);
325  }
326  } else {
327  auto node = new Node(key, std::move(value));
328  index_.insert(*node);
329  lru_.push_front(*node);
330 
331  // no evictions if maxSize_ is 0 i.e. unlimited capacity
332  if (maxSize_ > 0 && size() > maxSize_) {
333  prune(clearSize_, pruneHook);
334  }
335  }
336  }
337 
342  std::size_t size() const {
343  return index_.size();
344  }
345 
350  bool empty() const {
351  return index_.empty();
352  }
353 
354  void clear(PruneHookCall pruneHook = nullptr) {
355  prune(size(), pruneHook);
356  }
357 
369  void setPruneHook(PruneHookCall pruneHook) {
370  pruneHook_ = pruneHook;
371  }
372 
379  void prune(std::size_t pruneSize, PruneHookCall pruneHook = nullptr) {
380  // do not swallow exceptions for prunes not triggered from destructor
381  pruneWithFailSafeOption(pruneSize, pruneHook, false);
382  }
383 
384  // Iterators and such
385  iterator begin() {
386  return iterator(lru_.begin());
387  }
388  iterator end() {
389  return iterator(lru_.end());
390  }
392  return const_iterator(lru_.begin());
393  }
394  const_iterator end() const {
395  return const_iterator(lru_.end());
396  }
397 
399  return const_iterator(lru_.cbegin());
400  }
402  return const_iterator(lru_.cend());
403  }
404 
406  return reverse_iterator(lru_.rbegin());
407  }
409  return reverse_iterator(lru_.rend());
410  }
411 
413  return const_reverse_iterator(lru_.rbegin());
414  }
416  return const_reverse_iterator(lru_.rend());
417  }
418 
420  return const_reverse_iterator(lru_.crbegin());
421  }
423  return const_reverse_iterator(lru_.crend());
424  }
425 
426  private:
427  struct Node : public boost::intrusive::unordered_set_base_hook<link_mode>,
428  public boost::intrusive::list_base_hook<link_mode> {
429  Node(const TKey& key, TValue&& value)
430  : pr(std::make_pair(key, std::move(value))) {}
431  TPair pr;
432  };
433 
434  struct KeyHasher {
435  KeyHasher(const THash& keyHash) : hash(keyHash) {}
436  std::size_t operator()(const Node& node) const {
437  return hash(node.pr.first);
438  }
439  std::size_t operator()(const TKey& key) const {
440  return hash(key);
441  }
442  THash hash;
443  };
444 
445  struct KeyValueEqual {
446  KeyValueEqual(const TKeyEqual& keyEqual) : equal(keyEqual) {}
447  bool operator()(const TKey& lhs, const Node& rhs) const {
448  return equal(lhs, rhs.pr.first);
449  }
450  bool operator()(const Node& lhs, const TKey& rhs) const {
451  return equal(lhs.pr.first, rhs);
452  }
453  bool operator()(const Node& lhs, const Node& rhs) const {
454  return equal(lhs.pr.first, rhs.pr.first);
455  }
456  TKeyEqual equal;
457  };
458 
466  typename NodeMap::iterator findInIndex(const TKey& key) {
467  return index_.find(key, KeyHasher(keyHash_), KeyValueEqual(keyEqual_));
468  }
469 
470  typename NodeMap::const_iterator findInIndex(const TKey& key) const {
471  return index_.find(key, KeyHasher(keyHash_), KeyValueEqual(keyEqual_));
472  }
473 
481  std::size_t pruneSize,
482  PruneHookCall pruneHook,
483  bool failSafe) {
484  auto& ph = (nullptr == pruneHook) ? pruneHook_ : pruneHook;
485 
486  for (std::size_t i = 0; i < pruneSize && !lru_.empty(); i++) {
487  auto* node = &(*lru_.rbegin());
488  std::unique_ptr<Node> nptr(node);
489 
490  lru_.erase(lru_.iterator_to(*node));
491  index_.erase(index_.iterator_to(*node));
492  if (ph) {
493  try {
494  ph(node->pr.first, std::move(node->pr.second));
495  } catch (...) {
496  if (!failSafe) {
497  throw;
498  }
499  }
500  }
501  }
502  }
503 
504  static const std::size_t kMinNumIndexBuckets = 100;
505  PruneHookCall pruneHook_;
506  std::size_t nIndexBuckets_;
507  std::unique_ptr<typename NodeMap::bucket_type[]> indexBuckets_;
508  typename NodeMap::bucket_traits indexTraits_;
509  THash keyHash_;
510  TKeyEqual keyEqual_;
512  NodeList lru_;
513  std::size_t maxSize_;
514  std::size_t clearSize_;
515 };
516 
517 } // namespace folly
Node(const TKey &key, TValue &&value)
std::size_t operator()(const Node &node) const
std::size_t size() const
boost::intrusive::unordered_set< Node, boost::intrusive::hash< KeyHasher >, boost::intrusive::equal< KeyValueEqual > > NodeMap
LogLevel max
Definition: LogLevel.cpp:31
bool exists(const TKey &key) const
const_reverse_iterator crend() const
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
size_t getMaxSize() const
const_iterator end() const
TValue & get(const TKey &key)
STL namespace.
bool erase(const TKey &key)
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
EvictingCacheMap & operator=(const EvictingCacheMap &)=delete
TValue & getWithoutPromotion(const TKey &key)
bool operator()(const TKey &lhs, const Node &rhs) const
FOLLY_PUSH_WARNING RHS rhs
Definition: Traits.h:649
EvictingCacheMap(std::size_t maxSize, std::size_t clearSize=1, const THash &keyHash=THash(), const TKeyEqual &keyEqual=TKeyEqual())
NodeMap::bucket_traits indexTraits_
const_iterator findWithoutPromotion(const TKey &key) const
iterator_base< TPair, typename NodeList::iterator > iterator
bool Value(const T &value, M matcher)
const_iterator cbegin() const
boost::intrusive::link_mode< boost::intrusive::safe_link > link_mode
const_reverse_iterator rend() const
bool operator()(const Node &lhs, const Node &rhs) const
boost::intrusive::list< Node > NodeList
bool operator()(const Node &lhs, const TKey &rhs) const
std::unique_ptr< typename NodeMap::bucket_type[]> indexBuckets_
reverse_iterator rend()
NodeMap::const_iterator findInIndex(const TKey &key) const
iterator_base< const TPair, typename NodeList::const_reverse_iterator > const_reverse_iterator
iterator_base< TPair, typename NodeList::reverse_iterator > reverse_iterator
void setMaxSize(size_t maxSize, PruneHookCall pruneHook=nullptr)
iterator_base< const TPair, typename NodeList::const_iterator > const_iterator
iterator findWithoutPromotion(const TKey &key)
iterator find(const TKey &key)
void prune(std::size_t pruneSize, PruneHookCall pruneHook=nullptr)
const_reverse_iterator rbegin() const
const_iterator begin() const
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
const_iterator cend() const
reverse_iterator rbegin()
void clear(PruneHookCall pruneHook=nullptr)
void setClearSize(size_t clearSize)
const TValue & getWithoutPromotion(const TKey &key) const
void pruneWithFailSafeOption(std::size_t pruneSize, PruneHookCall pruneHook, bool failSafe)
std::function< void(TKey, TValue &&)> PruneHookCall
std::pair< const TKey, TValue > TPair
NodeMap::iterator findInIndex(const TKey &key)
KeyValueEqual(const TKeyEqual &keyEqual)
const_reverse_iterator crbegin() const
void setPruneHook(PruneHookCall pruneHook)
static const std::size_t kMinNumIndexBuckets
std::size_t operator()(const TKey &key) const