]> git.proxmox.com Git - ceph.git/blame - ceph/src/crimson/common/shared_lru.h
import 15.2.0 Octopus source
[ceph.git] / ceph / src / crimson / common / shared_lru.h
CommitLineData
11fdf7f2
TL
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
4#pragma once
5
6#include <memory>
7#include <optional>
8#include <boost/smart_ptr/local_shared_ptr.hpp>
9#include <boost/smart_ptr/weak_ptr.hpp>
10#include "simple_lru.h"
11
12/// SharedLRU does its best to cache objects. It not only tracks the objects
13/// in its LRU cache with strong references, it also tracks objects with
14/// weak_ptr even if the cache does not hold any strong references to them. so
15/// that it can return the objects after they are evicted, as long as they've
16/// ever been cached and have not been destroyed yet.
17template<class K, class V>
18class SharedLRU {
19 using shared_ptr_t = boost::local_shared_ptr<V>;
20 using weak_ptr_t = boost::weak_ptr<V>;
21 using value_type = std::pair<K, shared_ptr_t>;
22
23 // weak_refs is already ordered, and we don't use accessors like
24 // LRUCache::lower_bound(), so unordered LRUCache would suffice.
25 SimpleLRU<K, shared_ptr_t, false> cache;
26 std::map<K, std::pair<weak_ptr_t, V*>> weak_refs;
27
28 struct Deleter {
29 SharedLRU<K,V>* cache;
30 const K key;
31 void operator()(V* ptr) {
9f95a23c 32 cache->_erase_weak(key);
11fdf7f2
TL
33 delete ptr;
34 }
35 };
9f95a23c 36 void _erase_weak(const K& key) {
11fdf7f2
TL
37 weak_refs.erase(key);
38 }
39public:
40 SharedLRU(size_t max_size = 20)
41 : cache{max_size}
42 {}
43 ~SharedLRU() {
44 cache.clear();
45 // use plain assert() in utiliy classes to avoid dependencies on logging
46 assert(weak_refs.empty());
47 }
48 /**
49 * Returns a reference to the given key, and perform an insertion if such
50 * key does not already exist
51 */
52 shared_ptr_t operator[](const K& key);
53 /**
54 * Returns true iff there are no live references left to anything that has been
55 * in the cache.
56 */
57 bool empty() const {
58 return weak_refs.empty();
59 }
60 size_t size() const {
61 return cache.size();
62 }
63 size_t capacity() const {
64 return cache.capacity();
65 }
66 /***
67 * Inserts a key if not present, or bumps it to the front of the LRU if
68 * it is, and then gives you a reference to the value. If the key already
69 * existed, you are responsible for deleting the new value you tried to
70 * insert.
71 *
72 * @param key The key to insert
73 * @param value The value that goes with the key
74 * @param existed Set to true if the value was already in the
75 * map, false otherwise
76 * @return A reference to the map's value for the given key
77 */
78 shared_ptr_t insert(const K& key, std::unique_ptr<V> value);
79 // clear all strong reference from the lru.
80 void clear() {
81 cache.clear();
82 }
83 shared_ptr_t find(const K& key);
84 // return the last element that is not greater than key
85 shared_ptr_t lower_bound(const K& key);
86 // return the first element that is greater than key
87 std::optional<value_type> upper_bound(const K& key);
9f95a23c
TL
88
89 void erase(const K& key) {
90 cache.erase(key);
91 _erase_weak(key);
92 }
11fdf7f2
TL
93};
94
95template<class K, class V>
96typename SharedLRU<K,V>::shared_ptr_t
97SharedLRU<K,V>::insert(const K& key, std::unique_ptr<V> value)
98{
99 shared_ptr_t val;
100 if (auto found = weak_refs.find(key); found != weak_refs.end()) {
101 val = found->second.first.lock();
102 }
103 if (!val) {
104 val.reset(value.release(), Deleter{this, key});
105 weak_refs.emplace(key, std::make_pair(val, val.get()));
106 }
107 cache.insert(key, val);
108 return val;
109}
110
111template<class K, class V>
112typename SharedLRU<K,V>::shared_ptr_t
113SharedLRU<K,V>::operator[](const K& key)
114{
115 if (auto found = cache.find(key); found) {
116 return *found;
117 }
118 shared_ptr_t val;
119 if (auto found = weak_refs.find(key); found != weak_refs.end()) {
120 val = found->second.first.lock();
121 }
122 if (!val) {
123 val.reset(new V{}, Deleter{this, key});
124 weak_refs.emplace(key, std::make_pair(val, val.get()));
125 }
126 cache.insert(key, val);
127 return val;
128}
129
130template<class K, class V>
131typename SharedLRU<K,V>::shared_ptr_t
132SharedLRU<K,V>::find(const K& key)
133{
134 if (auto found = cache.find(key); found) {
135 return *found;
136 }
137 shared_ptr_t val;
138 if (auto found = weak_refs.find(key); found != weak_refs.end()) {
139 val = found->second.first.lock();
140 }
141 if (val) {
142 cache.insert(key, val);
143 }
144 return val;
145}
146
147template<class K, class V>
148typename SharedLRU<K,V>::shared_ptr_t
149SharedLRU<K,V>::lower_bound(const K& key)
150{
151 if (weak_refs.empty()) {
152 return {};
153 }
154 auto found = weak_refs.lower_bound(key);
155 if (found == weak_refs.end()) {
156 --found;
157 }
158 if (auto val = found->second.first.lock(); val) {
159 cache.insert(key, val);
160 return val;
161 } else {
162 return {};
163 }
164}
165
166template<class K, class V>
167std::optional<typename SharedLRU<K,V>::value_type>
168SharedLRU<K,V>::upper_bound(const K& key)
169{
170 for (auto found = weak_refs.upper_bound(key);
171 found != weak_refs.end();
172 ++found) {
173 if (auto val = found->second.first.lock(); val) {
174 return std::make_pair(found->first, val);
175 }
176 }
177 return std::nullopt;
178}