thread_safe_cache.h 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. #pragma once
  2. #include "cache.h"
  3. #include <util/generic/singleton.h>
  4. #include <util/system/rwlock.h>
  5. namespace NPrivate {
  6. // We are interested in getters promotion policy _here_ because of Read-Write-Lock optimizations.
  7. enum class EGettersPromotionPolicy {
  8. Promoted, // LRU, TLRU, MRU, etc.
  9. Unpromoted // FIFO, LIFO, LW, etc.
  10. };
  11. template <class Key, class Value, template <class, class> class List, EGettersPromotionPolicy GettersPromotionPolicy, class... TArgs>
  12. class TThreadSafeCache {
  13. public:
  14. using TPtr = TAtomicSharedPtr<Value>;
  15. class ICallbacks {
  16. public:
  17. using TKey = Key;
  18. using TValue = Value;
  19. using TOwner = TThreadSafeCache<Key, Value, List, GettersPromotionPolicy, TArgs...>;
  20. public:
  21. virtual ~ICallbacks() = default;
  22. virtual TKey GetKey(TArgs... args) const = 0;
  23. virtual TValue* CreateObject(TArgs... args) const = 0;
  24. };
  25. public:
  26. TThreadSafeCache(const ICallbacks& callbacks, size_t maxSize = Max<size_t>())
  27. : Callbacks(callbacks)
  28. , Cache(maxSize)
  29. {
  30. }
  31. bool Insert(const Key& key, const TPtr& value) {
  32. if (!Contains(key)) {
  33. TWriteGuard w(Mutex);
  34. return Cache.Insert(key, value);
  35. }
  36. return false;
  37. }
  38. void Update(const Key& key, const TPtr& value) {
  39. TWriteGuard w(Mutex);
  40. Cache.Update(key, value);
  41. }
  42. const TPtr GetOrNull(TArgs... args) {
  43. Key key = Callbacks.GetKey(args...);
  44. TReadGuard r(Mutex);
  45. auto iter = Cache.Find(key);
  46. if (iter == Cache.End()) {
  47. return nullptr;
  48. }
  49. return iter.Value();
  50. }
  51. const TPtr Get(TArgs... args) const {
  52. return GetValue<true>(args...);
  53. }
  54. const TPtr GetUnsafe(TArgs... args) const {
  55. return GetValue<false>(args...);
  56. }
  57. void Clear() {
  58. TWriteGuard w(Mutex);
  59. Cache.Clear();
  60. }
  61. void Erase(TArgs... args) {
  62. Key key = Callbacks.GetKey(args...);
  63. if (!Contains(key)) {
  64. return;
  65. }
  66. TWriteGuard w(Mutex);
  67. typename TInternalCache::TIterator i = Cache.Find(key);
  68. if (i == Cache.End()) {
  69. return;
  70. }
  71. Cache.Erase(i);
  72. }
  73. bool Contains(const Key& key) const {
  74. TReadGuard r(Mutex);
  75. auto iter = Cache.FindWithoutPromote(key);
  76. return iter != Cache.End();
  77. }
  78. template <class TCallbacks>
  79. static const TPtr Get(TArgs... args) {
  80. return TThreadSafeCacheSingleton<TCallbacks>::Get(args...);
  81. }
  82. template <class TCallbacks>
  83. static const TPtr Erase(TArgs... args) {
  84. return TThreadSafeCacheSingleton<TCallbacks>::Erase(args...);
  85. }
  86. template <class TCallbacks>
  87. static void Clear() {
  88. return TThreadSafeCacheSingleton<TCallbacks>::Clear();
  89. }
  90. size_t Size() const {
  91. TReadGuard r(Mutex);
  92. return Cache.Size();
  93. }
  94. size_t TotalSize() const {
  95. TReadGuard r(Mutex);
  96. return Cache.TotalSize();
  97. }
  98. size_t GetMaxSize() const {
  99. TReadGuard w(Mutex);
  100. return Cache.GetMaxSize();
  101. }
  102. void SetMaxSize(size_t newSize) {
  103. TWriteGuard w(Mutex);
  104. Cache.SetMaxSize(newSize);
  105. }
  106. private:
  107. template <bool AllowNullValues>
  108. const TPtr GetValue(TArgs... args) const {
  109. Key key = Callbacks.GetKey(args...);
  110. switch (GettersPromotionPolicy) {
  111. case EGettersPromotionPolicy::Promoted:
  112. break;
  113. case EGettersPromotionPolicy::Unpromoted: {
  114. TReadGuard r(Mutex);
  115. typename TInternalCache::TIterator i = Cache.FindWithoutPromote(key);
  116. if (i != Cache.End()) {
  117. return i.Value();
  118. }
  119. break;
  120. }
  121. }
  122. TWriteGuard w(Mutex);
  123. typename TInternalCache::TIterator i = Cache.Find(key);
  124. if (i != Cache.End()) {
  125. return i.Value();
  126. }
  127. TPtr value = Callbacks.CreateObject(args...);
  128. if (value || AllowNullValues) {
  129. Cache.Insert(key, value);
  130. }
  131. return value;
  132. }
  133. private:
  134. using TInternalCache = TCache<Key, TPtr, List<Key, TPtr>, TNoopDelete>;
  135. template <class TCallbacks>
  136. class TThreadSafeCacheSingleton {
  137. public:
  138. static const TPtr Get(TArgs... args) {
  139. return Singleton<TThreadSafeCacheSingleton>()->Cache.Get(args...);
  140. }
  141. static const TPtr Erase(TArgs... args) {
  142. return Singleton<TThreadSafeCacheSingleton>()->Cache.Erase(args...);
  143. }
  144. static void Clear() {
  145. return Singleton<TThreadSafeCacheSingleton>()->Cache.Clear();
  146. }
  147. TThreadSafeCacheSingleton()
  148. : Cache(Callbacks)
  149. {
  150. }
  151. private:
  152. TCallbacks Callbacks;
  153. typename TCallbacks::TOwner Cache;
  154. };
  155. private:
  156. TRWMutex Mutex;
  157. const ICallbacks& Callbacks;
  158. mutable TInternalCache Cache;
  159. };
  160. struct TLWHelper {
  161. template <class TValue>
  162. struct TConstWeighter {
  163. static int Weight(const TValue& /*value*/) {
  164. return 0;
  165. }
  166. };
  167. template <class TKey, class TValue>
  168. using TListType = TLWList<TKey, TValue, int, TConstWeighter<TValue>>;
  169. template <class TKey, class TValue, class... TArgs>
  170. using TCache = TThreadSafeCache<TKey, TValue, TListType, EGettersPromotionPolicy::Unpromoted, TArgs...>;
  171. };
  172. struct TLRUHelper {
  173. template <class TKey, class TValue>
  174. using TListType = TLRUList<TKey, TValue>;
  175. template <class TKey, class TValue, class... TArgs>
  176. using TCache = TThreadSafeCache<TKey, TValue, TListType, EGettersPromotionPolicy::Promoted, TArgs...>;
  177. };
  178. struct TLFUHelper {
  179. template <class TKey, class TValue>
  180. using TListType = TLFUList<TKey, TValue>;
  181. template <class TKey, class TValue, class... TArgs>
  182. using TCache = TThreadSafeCache<TKey, TValue, TListType, EGettersPromotionPolicy::Promoted, TArgs...>;
  183. };
  184. template <class TSizeProvider, class TValue>
  185. struct TSizeProviderRemoveAtomic : TSizeProvider {
  186. // TValue in this signature is TCache::TPtr, using this wrapper user don't need
  187. // to handle TPtr (which is TAtomicSharedPtr<TValue>) and can just accept TValue
  188. // in custom size provider. See example in unittests
  189. size_t operator()(const TValue& value) const {
  190. // We can pass reference to value without synchronization, because TSizeProvider::operator()
  191. // is always called from methods secured by a guard
  192. return TSizeProvider::operator()(*value);
  193. }
  194. };
  195. template <template <class, class, class> class TTemplateListType, EGettersPromotionPolicy GettersPromotionPolicy>
  196. struct TCacheWithSizeProviderHelper {
  197. private:
  198. template <class TSizeProvider>
  199. struct TListWithProvider {
  200. template <class TKey, class TValue>
  201. using TListType = TTemplateListType<TKey, TValue, TSizeProviderRemoveAtomic<TSizeProvider, TValue>>;
  202. };
  203. public:
  204. template <class TKey, class TValue, class TSizeProvider, class... TArgs>
  205. using TCache = TThreadSafeCache<TKey, TValue, TListWithProvider<TSizeProvider>::template TListType, GettersPromotionPolicy, TArgs...>;
  206. };
  207. using TLRUWithSizeProviderHelper = TCacheWithSizeProviderHelper<TLRUList, EGettersPromotionPolicy::Promoted>;
  208. using TLFUWithSizeProviderHelper = TCacheWithSizeProviderHelper<TLFUList, EGettersPromotionPolicy::Promoted>;
  209. }
  210. template <class TKey, class TValue, class... TArgs>
  211. using TThreadSafeCache = typename NPrivate::TLWHelper::template TCache<TKey, TValue, TArgs...>;
  212. template <class TKey, class TValue, class... TArgs>
  213. using TThreadSafeLRUCache = typename NPrivate::TLRUHelper::template TCache<TKey, TValue, TArgs...>;
  214. template <class TKey, class TValue, class... TArgs>
  215. using TThreadSafeLFUCache = typename NPrivate::TLFUHelper::template TCache<TKey, TValue, TArgs...>;
  216. template <class TKey, class TValue, class TSizeProvider, class... TArgs>
  217. using TThreadSafeLRUCacheWithSizeProvider = typename NPrivate::TLRUWithSizeProviderHelper::template TCache<TKey, TValue, TSizeProvider, TArgs...>;
  218. template <class TKey, class TValue, class TSizeProvider, class... TArgs>
  219. using TThreadSafeLFUCacheWithSizeProvider = typename NPrivate::TLFUWithSizeProviderHelper::template TCache<TKey, TValue, TSizeProvider, TArgs...>;