From e6918187568dbd01842d8d1d2c808ce16a894239 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 21 Apr 2024 13:54:28 +0200 Subject: Adding upstream version 18.2.2. Signed-off-by: Daniel Baumann --- src/common/intrusive_lru.h | 222 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 src/common/intrusive_lru.h (limited to 'src/common/intrusive_lru.h') diff --git a/src/common/intrusive_lru.h b/src/common/intrusive_lru.h new file mode 100644 index 000000000..e8c3cda3e --- /dev/null +++ b/src/common/intrusive_lru.h @@ -0,0 +1,222 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#pragma once + +#include +#include +#include + +namespace ceph::common { + +/** + * intrusive_lru: lru implementation with embedded map and list hook + * + * Note, this implementation currently is entirely thread-unsafe. + */ + +template +struct intrusive_lru_config { + using key_type = K; + using value_type = V; + using key_of_value = VToK; +}; + +template +class intrusive_lru; + +template +class intrusive_lru_base; + +template +void intrusive_ptr_add_ref(intrusive_lru_base *p); + +template +void intrusive_ptr_release(intrusive_lru_base *p); + + +template +class intrusive_lru_base { + unsigned use_count = 0; + + // null if unreferenced + intrusive_lru *lru = nullptr; + +public: + boost::intrusive::set_member_hook<> set_hook; + boost::intrusive::list_member_hook<> list_hook; + + using Ref = boost::intrusive_ptr; + using lru_t = intrusive_lru; + + friend intrusive_lru; + friend void intrusive_ptr_add_ref<>(intrusive_lru_base *); + friend void intrusive_ptr_release<>(intrusive_lru_base *); + + virtual ~intrusive_lru_base() {} +}; + +template +class intrusive_lru { + using base_t = intrusive_lru_base; + using K = typename Config::key_type; + using T = typename Config::value_type; + using TRef = typename base_t::Ref; + + using lru_set_option_t = boost::intrusive::member_hook< + base_t, + boost::intrusive::set_member_hook<>, + &base_t::set_hook>; + + using VToK = typename Config::key_of_value; + struct VToKWrapped { + using type = typename VToK::type; + const type &operator()(const base_t &obc) { + return VToK()(static_cast(obc)); + } + }; + using lru_set_t = boost::intrusive::set< + base_t, + lru_set_option_t, + boost::intrusive::key_of_value + >; + lru_set_t lru_set; + + using lru_list_t = boost::intrusive::list< + base_t, + boost::intrusive::member_hook< + base_t, + boost::intrusive::list_member_hook<>, + &base_t::list_hook>>; + lru_list_t unreferenced_list; + + size_t lru_target_size = 0; + + void evict() { + while (!unreferenced_list.empty() && + lru_set.size() > lru_target_size) { + auto &b = unreferenced_list.front(); + assert(!b.lru); + unreferenced_list.pop_front(); + lru_set.erase_and_dispose( + lru_set.iterator_to(b), + [](auto *p) { delete p; } + ); + } + } + + void access(base_t &b) { + if (b.lru) + return; + unreferenced_list.erase(lru_list_t::s_iterator_to(b)); + b.lru = this; + } + + void insert(base_t &b) { + assert(!b.lru); + lru_set.insert(b); + b.lru = this; + evict(); + } + + void unreferenced(base_t &b) { + assert(b.lru); + unreferenced_list.push_back(b); + b.lru = nullptr; + evict(); + } + +public: + /** + * Returns the TRef corresponding to k if it exists or + * creates it otherwise. Return is: + * std::pair(reference_to_val, found) + */ + std::pair get_or_create(const K &k) { + typename lru_set_t::insert_commit_data icd; + auto [iter, missing] = lru_set.insert_check( + k, + icd); + if (missing) { + auto ret = new T(k); + lru_set.insert_commit(*ret, icd); + insert(*ret); + return {TRef(ret), false}; + } else { + access(*iter); + return {TRef(static_cast(&*iter)), true}; + } + } + + /* + * Clears unreferenced elements from the lru set [from, to] + */ + void clear_range( + const K& from, + const K& to) { + auto from_iter = lru_set.lower_bound(from); + auto to_iter = lru_set.upper_bound(to); + for (auto i = from_iter; i != to_iter; ) { + if (!(*i).lru) { + unreferenced_list.erase(lru_list_t::s_iterator_to(*i)); + i = lru_set.erase_and_dispose(i, [](auto *p) + { delete p; } ); + } else { + i++; + } + } + } + + template + void for_each(F&& f) { + for (auto& v : lru_set) { + access(v); + f(TRef{static_cast(&v)}); + } + } + + /** + * Returns the TRef corresponding to k if it exists or + * nullptr otherwise. + */ + TRef get(const K &k) { + if (auto iter = lru_set.find(k); iter != std::end(lru_set)) { + access(*iter); + return TRef(static_cast(&*iter)); + } else { + return nullptr; + } + } + + void set_target_size(size_t target_size) { + lru_target_size = target_size; + evict(); + } + + ~intrusive_lru() { + set_target_size(0); + } + + friend void intrusive_ptr_add_ref<>(intrusive_lru_base *); + friend void intrusive_ptr_release<>(intrusive_lru_base *); +}; + +template +void intrusive_ptr_add_ref(intrusive_lru_base *p) { + assert(p); + assert(p->lru); + p->use_count++; +} + +template +void intrusive_ptr_release(intrusive_lru_base *p) { + assert(p); + assert(p->use_count > 0); + --p->use_count; + if (p->use_count == 0) { + p->lru->unreferenced(*p); + } +} + + +} -- cgit v1.2.3