/**
* intrusive_lru: lru implementation with embedded map and list hook
*
+ * Elements will be stored in an intrusive set. Once an element is no longer
+ * referenced it will remain in the set. The unreferenced elements will be
+ * evicted from the set once the set size exceeds the `lru_target_size`.
+ * Referenced elements will not be evicted as this is a registery with
+ * extra caching capabilities.
+ *
* Note, this implementation currently is entirely thread-unsafe.
*/
class intrusive_lru_base {
unsigned use_count = 0;
- // null if unreferenced
+ // lru points to the corresponding intrusive_lru
+ // which will be set to null if its use_count
+ // is zero (aka unreferenced).
intrusive_lru<Config> *lru = nullptr;
public:
size_t lru_target_size = 0;
+ // when the lru_set exceeds its target size, evict
+ // only unreferenced elements from it (if any).
void evict() {
while (!unreferenced_list.empty() &&
lru_set.size() > lru_target_size) {
- auto &b = unreferenced_list.front();
- assert(b.is_unreferenced());
+ auto &evict_target = unreferenced_list.front();
+ assert(evict_target.is_unreferenced());
unreferenced_list.pop_front();
lru_set.erase_and_dispose(
- lru_set.iterator_to(b),
+ lru_set.iterator_to(evict_target),
[](auto *p) { delete p; }
);
}
}
+ // access an existing element in the lru_set.
+ // mark as referenced if necessary.
void access(base_t &b) {
if (b.is_referenced())
return;
b.lru = this;
}
+ // insert a new element to the lru_set.
+ // attempt to evict if possible.
void insert(base_t &b) {
assert(b.is_unreferenced());
lru_set.insert(b);
evict();
}
+ // an element in the lru_set has no users,
+ // mark it as unreferenced and try to evict.
void mark_as_unreferenced(base_t &b) {
assert(b.is_referenced());
unreferenced_list.push_back(b);