summaryrefslogtreecommitdiffstats
path: root/library/cpp/cache
diff options
context:
space:
mode:
authordskor <[email protected]>2025-10-16 01:40:10 +0300
committerdskor <[email protected]>2025-10-16 02:22:15 +0300
commit8d275bde2bd67470dccbba997208810b4280d8f0 (patch)
treea8fdd96862e2f9ee45f76dd059714983f1ae55b3 /library/cpp/cache
parent52bb9813716cd4308702d44d69fb3a448500f50f (diff)
TWriteGuard on GetOrNull for ThreadSaveCache with promotion
Find может делать Promote, который мутирует List ( LRUList например: <https://nda.ya.ru/t/fFSNn0ML7LRudF> ) commit_hash:dca1db67d45c7faca51cc1251cbe155c7857e362
Diffstat (limited to 'library/cpp/cache')
-rw-r--r--library/cpp/cache/thread_safe_cache.h17
-rw-r--r--library/cpp/cache/ut/cache_ut.cpp51
2 files changed, 63 insertions, 5 deletions
diff --git a/library/cpp/cache/thread_safe_cache.h b/library/cpp/cache/thread_safe_cache.h
index b4bd4deedf2..045bea0c8c3 100644
--- a/library/cpp/cache/thread_safe_cache.h
+++ b/library/cpp/cache/thread_safe_cache.h
@@ -52,12 +52,19 @@ namespace NPrivate {
const TPtr GetOrNull(TArgs... args) {
Key key = Callbacks.GetKey(args...);
- TReadGuard r(Mutex);
- auto iter = Cache.Find(key);
- if (iter == Cache.End()) {
- return nullptr;
+ switch (GettersPromotionPolicy) {
+ case EGettersPromotionPolicy::Promoted: {
+ TWriteGuard r(Mutex);
+ if (auto iter = Cache.Find(key); iter != Cache.End())
+ return iter.Value();
+ }
+ case EGettersPromotionPolicy::Unpromoted: {
+ TReadGuard r(Mutex);
+ if (auto iter = Cache.Find(key); iter != Cache.End())
+ return iter.Value();
+ }
}
- return iter.Value();
+ return nullptr;
}
const TPtr Get(TArgs... args) const {
diff --git a/library/cpp/cache/ut/cache_ut.cpp b/library/cpp/cache/ut/cache_ut.cpp
index 16f29b29d1f..c4c829cdb7d 100644
--- a/library/cpp/cache/ut/cache_ut.cpp
+++ b/library/cpp/cache/ut/cache_ut.cpp
@@ -2,6 +2,10 @@
#include <library/cpp/cache/thread_safe_cache.h>
#include <library/cpp/testing/unittest/registar.h>
+#include <util/thread/pool.h>
+#include <util/string/cast.h>
+#include <util/random/random.h>
+
struct TStrokaWeighter {
static size_t Weight(const TString& s) {
return s.size();
@@ -536,6 +540,53 @@ Y_UNIT_TEST_SUITE(TThreadSafeCacheTest) {
}
}
+Y_UNIT_TEST_SUITE(TThreadSafeLRUCacheMultiThreadTest) {
+ typedef TThreadSafeLRUCache<ui32, TString, ui32> TCache;
+
+ class TSimpleCallbacks: public TCache::ICallbacks {
+ public:
+ TKey GetKey(ui32 i) const override {
+ return i;
+ }
+ TValue* CreateObject(ui32 i) const override {
+ Y_UNUSED(i);
+ return nullptr;
+ }
+ };
+
+ Y_UNIT_TEST(GetOrNullMultiThreadTest) {
+ const size_t poolSize = 8;
+ const size_t passCnt = 128;
+ const size_t tasksCnt = 128;
+
+ TRWMutex lock;
+ TThreadPool pool;
+ TSimpleCallbacks callbacks;
+ TCache cache(callbacks, poolSize);
+
+ for (size_t i = 0; i < poolSize; ++i) {
+ cache.Insert(i, MakeAtomicShared<TString>(ToString(i)));
+ }
+
+ pool.Start(poolSize);
+ {
+ TWriteGuard wGruard(lock);
+ for (size_t i = 0; i < tasksCnt; ++i) {
+ pool.SafeAddFunc([&lock, &cache]() {
+ TReadGuard rGuard(lock);
+ for (size_t j = 0; j < passCnt; ++j) {
+ UNIT_ASSERT(cache.GetOrNull(RandomNumber<size_t>(poolSize)) != nullptr);
+ }
+ });
+ }
+ } // start race
+ pool.Stop();
+ for (size_t i = 0; i < cache.Size(); ++i) {
+ UNIT_ASSERT(cache.GetOrNull(i) != nullptr);
+ }
+ }
+}
+
Y_UNIT_TEST_SUITE(TThreadSafeCacheUnsafeTest) {
typedef TThreadSafeCache<ui32, TString, ui32> TCache;