thread_safe_cache.h 9.0 KB

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