123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- #pragma once
- #include "cache.h"
- #include <util/generic/singleton.h>
- #include <util/generic/ylimits.h>
- #include <util/system/rwlock.h>
- namespace NPrivate {
- // We are interested in getters promotion policy _here_ because of Read-Write-Lock optimizations.
- enum class EGettersPromotionPolicy {
- Promoted, // LRU, TLRU, MRU, etc.
- Unpromoted // FIFO, LIFO, LW, etc.
- };
- template <class Key, class Value, template <class, class> class List, EGettersPromotionPolicy GettersPromotionPolicy, class... TArgs>
- class TThreadSafeCache {
- public:
- using TPtr = TAtomicSharedPtr<Value>;
- class ICallbacks {
- public:
- using TKey = Key;
- using TValue = Value;
- using TOwner = TThreadSafeCache<Key, Value, List, GettersPromotionPolicy, TArgs...>;
- public:
- virtual ~ICallbacks() = default;
- virtual TKey GetKey(TArgs... args) const = 0;
- virtual TValue* CreateObject(TArgs... args) const = 0;
- };
- public:
- TThreadSafeCache(const ICallbacks& callbacks, size_t maxSize = Max<size_t>())
- : Callbacks(callbacks)
- , Cache(maxSize)
- {
- }
- bool Insert(const Key& key, const TPtr& value) {
- if (!Contains(key)) {
- TWriteGuard w(Mutex);
- return Cache.Insert(key, value);
- }
- return false;
- }
- void Update(const Key& key, const TPtr& value) {
- TWriteGuard w(Mutex);
- Cache.Update(key, value);
- }
- const TPtr GetOrNull(TArgs... args) {
- Key key = Callbacks.GetKey(args...);
- TReadGuard r(Mutex);
- auto iter = Cache.Find(key);
- if (iter == Cache.End()) {
- return nullptr;
- }
- return iter.Value();
- }
- const TPtr Get(TArgs... args) const {
- return GetValue<true>(args...);
- }
- const TPtr GetUnsafe(TArgs... args) const {
- return GetValue<false>(args...);
- }
- void Clear() {
- TWriteGuard w(Mutex);
- Cache.Clear();
- }
- void Erase(TArgs... args) {
- Key key = Callbacks.GetKey(args...);
- if (!Contains(key)) {
- return;
- }
- TWriteGuard w(Mutex);
- typename TInternalCache::TIterator i = Cache.Find(key);
- if (i == Cache.End()) {
- return;
- }
- Cache.Erase(i);
- }
- bool Contains(const Key& key) const {
- TReadGuard r(Mutex);
- auto iter = Cache.FindWithoutPromote(key);
- return iter != Cache.End();
- }
- template <class TCallbacks>
- static const TPtr Get(TArgs... args) {
- return TThreadSafeCacheSingleton<TCallbacks>::Get(args...);
- }
- template <class TCallbacks>
- static const TPtr Erase(TArgs... args) {
- return TThreadSafeCacheSingleton<TCallbacks>::Erase(args...);
- }
- template <class TCallbacks>
- static void Clear() {
- return TThreadSafeCacheSingleton<TCallbacks>::Clear();
- }
- size_t Size() const {
- TReadGuard r(Mutex);
- return Cache.Size();
- }
- size_t TotalSize() const {
- TReadGuard r(Mutex);
- return Cache.TotalSize();
- }
- size_t GetMaxSize() const {
- TReadGuard w(Mutex);
- return Cache.GetMaxSize();
- }
- void SetMaxSize(size_t newSize) {
- TWriteGuard w(Mutex);
- Cache.SetMaxSize(newSize);
- }
- private:
- template <bool AllowNullValues>
- const TPtr GetValue(TArgs... args) const {
- Key key = Callbacks.GetKey(args...);
- switch (GettersPromotionPolicy) {
- case EGettersPromotionPolicy::Promoted:
- break;
- case EGettersPromotionPolicy::Unpromoted: {
- TReadGuard r(Mutex);
- typename TInternalCache::TIterator i = Cache.FindWithoutPromote(key);
- if (i != Cache.End()) {
- return i.Value();
- }
- break;
- }
- }
- TWriteGuard w(Mutex);
- typename TInternalCache::TIterator i = Cache.Find(key);
- if (i != Cache.End()) {
- return i.Value();
- }
- TPtr value = Callbacks.CreateObject(args...);
- if (value || AllowNullValues) {
- Cache.Insert(key, value);
- }
- return value;
- }
- private:
- using TInternalCache = TCache<Key, TPtr, List<Key, TPtr>, TNoopDelete>;
- template <class TCallbacks>
- class TThreadSafeCacheSingleton {
- public:
- static const TPtr Get(TArgs... args) {
- return Singleton<TThreadSafeCacheSingleton>()->Cache.Get(args...);
- }
- static const TPtr Erase(TArgs... args) {
- return Singleton<TThreadSafeCacheSingleton>()->Cache.Erase(args...);
- }
- static void Clear() {
- return Singleton<TThreadSafeCacheSingleton>()->Cache.Clear();
- }
- TThreadSafeCacheSingleton()
- : Cache(Callbacks)
- {
- }
- private:
- TCallbacks Callbacks;
- typename TCallbacks::TOwner Cache;
- };
- private:
- TRWMutex Mutex;
- const ICallbacks& Callbacks;
- mutable TInternalCache Cache;
- };
- struct TLWHelper {
- template <class TValue>
- struct TConstWeighter {
- static int Weight(const TValue& /*value*/) {
- return 0;
- }
- };
- template <class TKey, class TValue>
- using TListType = TLWList<TKey, TValue, int, TConstWeighter<TValue>>;
- template <class TKey, class TValue, class... TArgs>
- using TCache = TThreadSafeCache<TKey, TValue, TListType, EGettersPromotionPolicy::Unpromoted, TArgs...>;
- };
- struct TLRUHelper {
- template <class TKey, class TValue>
- using TListType = TLRUList<TKey, TValue>;
- template <class TKey, class TValue, class... TArgs>
- using TCache = TThreadSafeCache<TKey, TValue, TListType, EGettersPromotionPolicy::Promoted, TArgs...>;
- };
- struct TLFUHelper {
- template <class TKey, class TValue>
- using TListType = TLFUList<TKey, TValue>;
- template <class TKey, class TValue, class... TArgs>
- using TCache = TThreadSafeCache<TKey, TValue, TListType, EGettersPromotionPolicy::Promoted, TArgs...>;
- };
- template <class TSizeProvider, class TValue>
- struct TSizeProviderRemoveAtomic : TSizeProvider {
- // TValue in this signature is TCache::TPtr, using this wrapper user don't need
- // to handle TPtr (which is TAtomicSharedPtr<TValue>) and can just accept TValue
- // in custom size provider. See example in unittests
- size_t operator()(const TValue& value) const {
- // We can pass reference to value without synchronization, because TSizeProvider::operator()
- // is always called from methods secured by a guard
- return TSizeProvider::operator()(*value);
- }
- };
- template <template <class, class, class> class TTemplateListType, EGettersPromotionPolicy GettersPromotionPolicy>
- struct TCacheWithSizeProviderHelper {
- private:
- template <class TSizeProvider>
- struct TListWithProvider {
- template <class TKey, class TValue>
- using TListType = TTemplateListType<TKey, TValue, TSizeProviderRemoveAtomic<TSizeProvider, TValue>>;
- };
- public:
- template <class TKey, class TValue, class TSizeProvider, class... TArgs>
- using TCache = TThreadSafeCache<TKey, TValue, TListWithProvider<TSizeProvider>::template TListType, GettersPromotionPolicy, TArgs...>;
- };
- using TLRUWithSizeProviderHelper = TCacheWithSizeProviderHelper<TLRUList, EGettersPromotionPolicy::Promoted>;
- using TLFUWithSizeProviderHelper = TCacheWithSizeProviderHelper<TLFUList, EGettersPromotionPolicy::Promoted>;
- }
- template <class TKey, class TValue, class... TArgs>
- using TThreadSafeCache = typename NPrivate::TLWHelper::template TCache<TKey, TValue, TArgs...>;
- template <class TKey, class TValue, class... TArgs>
- using TThreadSafeLRUCache = typename NPrivate::TLRUHelper::template TCache<TKey, TValue, TArgs...>;
- template <class TKey, class TValue, class... TArgs>
- using TThreadSafeLFUCache = typename NPrivate::TLFUHelper::template TCache<TKey, TValue, TArgs...>;
- template <class TKey, class TValue, class TSizeProvider, class... TArgs>
- using TThreadSafeLRUCacheWithSizeProvider = typename NPrivate::TLRUWithSizeProviderHelper::template TCache<TKey, TValue, TSizeProvider, TArgs...>;
- template <class TKey, class TValue, class TSizeProvider, class... TArgs>
- using TThreadSafeLFUCacheWithSizeProvider = typename NPrivate::TLFUWithSizeProviderHelper::template TCache<TKey, TValue, TSizeProvider, TArgs...>;
|