@@ -124,6 +124,98 @@ class AllocatorMemoryTiersTest : public AllocatorTest<AllocatorT> {
124124 ASSERT (handle != nullptr );
125125 ASSERT_NO_THROW (alloc->insertOrReplace (handle));
126126 }
127+
128+ void testMultiTiersRemoveDuringEviction () {
129+ bool quit = false ;
130+ typename AllocatorT::Config config;
131+ config.setCacheSize (4 * Slab::kSize );
132+ config.enableCachePersistence (" /tmp" );
133+ config.configureMemoryTiers ({
134+ MemoryTierCacheConfig::fromShm ()
135+ .setRatio (1 ).setMemBind ({0 }),
136+ MemoryTierCacheConfig::fromShm ()
137+ .setRatio (1 ).setMemBind ({0 })
138+ });
139+
140+ std::unique_ptr<AllocatorT> alloc;
141+ std::unique_ptr<std::thread> t;
142+
143+ const auto moveCb = [&] (typename AllocatorT::Item& oldItem,
144+ typename AllocatorT::Item& newItem,
145+ typename AllocatorT::Item* /* parentPtr */ ) {
146+ memcpy (newItem.getMemory (), oldItem.getMemory (), oldItem.getSize ());
147+ auto key = oldItem.getKey ();
148+ t = std::make_unique<std::thread>([&](){ alloc->remove (key); });
149+ // sleep to make sure async thread calls remove(key)
150+ std::this_thread::sleep_for (std::chrono::milliseconds (100 ));
151+ quit = true ;
152+ };
153+ config.enableMovingOnSlabRelease (moveCb, {} /* ChainedItemsMoveSync */ ,
154+ -1 /* movingAttemptsLimit */ );
155+
156+ alloc = std::make_unique<AllocatorT>(AllocatorT::SharedMemNew, config);
157+ ASSERT (alloc != nullptr );
158+ auto pool = alloc->addPool (" default" , alloc->getCacheMemoryStats ().cacheSize );
159+
160+ int i = 0 ;
161+ while (!quit) {
162+ auto handle = alloc->allocate (pool, std::to_string (++i), std::string (" value" ).size ());
163+ ASSERT (handle != nullptr );
164+ ASSERT_NO_THROW (alloc->insertOrReplace (handle));
165+ }
166+
167+ t->join ();
168+ }
169+
170+ void testMultiTiersReplaceDuringEviction () {
171+ bool quit = false ;
172+ typename AllocatorT::Config config;
173+ config.setCacheSize (4 * Slab::kSize );
174+ config.enableCachePersistence (" /tmp" );
175+ config.configureMemoryTiers ({
176+ MemoryTierCacheConfig::fromShm ()
177+ .setRatio (1 ).setMemBind ({0 }),
178+ MemoryTierCacheConfig::fromShm ()
179+ .setRatio (1 ).setMemBind ({0 })
180+ });
181+
182+ std::unique_ptr<AllocatorT> alloc;
183+ PoolId pool;
184+ std::unique_ptr<std::thread> t;
185+
186+ const auto moveCb = [&] (typename AllocatorT::Item& oldItem,
187+ typename AllocatorT::Item& newItem,
188+ typename AllocatorT::Item* /* parentPtr */ ) {
189+ memcpy (newItem.getMemory (), oldItem.getMemory (), oldItem.getSize ());
190+ auto key = oldItem.getKey ();
191+ if (!quit) {
192+ // we need to replace only once because subsequent allocate calls
193+ // will cause evictions recursevly
194+ quit = true ;
195+ t = std::make_unique<std::thread>([&](){
196+ auto handle = alloc->allocate (pool, key, std::string (" new value" ).size ());
197+ ASSERT_NO_THROW (alloc->insertOrReplace (handle));
198+ });
199+ // sleep to make sure async thread calls remove(key)
200+ std::this_thread::sleep_for (std::chrono::milliseconds (100 ));
201+ }
202+ };
203+ config.enableMovingOnSlabRelease (moveCb, {} /* ChainedItemsMoveSync */ ,
204+ -1 /* movingAttemptsLimit */ );
205+
206+ alloc = std::make_unique<AllocatorT>(AllocatorT::SharedMemNew, config);
207+ ASSERT (alloc != nullptr );
208+ pool = alloc->addPool (" default" , alloc->getCacheMemoryStats ().cacheSize );
209+
210+ int i = 0 ;
211+ while (!quit) {
212+ auto handle = alloc->allocate (pool, std::to_string (++i), std::string (" value" ).size ());
213+ ASSERT (handle != nullptr );
214+ ASSERT_NO_THROW (alloc->insertOrReplace (handle));
215+ }
216+
217+ t->join ();
218+ }
127219};
128220} // namespace tests
129221} // namespace cachelib
0 commit comments