Skip to content

Commit 1a9525b

Browse files
authored
Merge pull request #27 from vinser52/fix_async_movement
Fix moveRegularItemWithSync and add tests
2 parents 829a434 + 1762b57 commit 1a9525b

File tree

4 files changed

+101
-4
lines changed

4 files changed

+101
-4
lines changed

cachelib/allocator/CacheAllocator-inl.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -1308,15 +1308,15 @@ CacheAllocator<CacheTrait>::moveRegularItemWithSync(
13081308
// make sure that no other thread removed it, and only then replaces it.
13091309
if (!replaceInMMContainer(oldItem, *newItemHdl)) {
13101310
accessContainer_->remove(*newItemHdl);
1311-
return {};
1311+
return acquire(&oldItem);
13121312
}
13131313

13141314
// Replacing into the MM container was successful, but someone could have
13151315
// called insertOrReplace() or remove() before or after the
13161316
// replaceInMMContainer() operation, which would invalidate newItemHdl.
13171317
if (!newItemHdl->isAccessible()) {
13181318
removeFromMMContainer(*newItemHdl);
1319-
return {};
1319+
return acquire(&oldItem);
13201320
}
13211321

13221322
// no one can add or remove chained items at this point

cachelib/allocator/CacheAllocator.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -1496,8 +1496,9 @@ class CacheAllocator : public CacheBase {
14961496
// @param oldItem Reference to the item being moved
14971497
// @param newItemHdl Reference to the handle of the new item being moved into
14981498
//
1499-
// @return true If the move was completed, and the containers were updated
1500-
// successfully.
1499+
// @return the handle to the oldItem if the move was completed
1500+
// and the oldItem can be recycled.
1501+
// Otherwise an empty handle is returned.
15011502
template <typename P>
15021503
WriteHandle moveRegularItemWithSync(Item& oldItem, WriteHandle& newItemHdl, P&& predicate);
15031504

cachelib/allocator/tests/AllocatorMemoryTiersTest.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ TEST_F(LruAllocatorMemoryTiersTest, MultiTiersFromFileValid) { this->testMultiTi
2828
TEST_F(LruAllocatorMemoryTiersTest, MultiTiersValidMixed) { this->testMultiTiersValidMixed(); }
2929
TEST_F(LruAllocatorMemoryTiersTest, MultiTiersNumaBindingsSysVValid) { this->testMultiTiersNumaBindingsSysVValid(); }
3030
TEST_F(LruAllocatorMemoryTiersTest, MultiTiersNumaBindingsPosixValid) { this->testMultiTiersNumaBindingsPosixValid(); }
31+
TEST_F(LruAllocatorMemoryTiersTest, MultiTiersRemoveDuringEviction) { this->testMultiTiersRemoveDuringEviction(); }
32+
TEST_F(LruAllocatorMemoryTiersTest, MultiTiersReplaceDuringEviction) { this->testMultiTiersReplaceDuringEviction(); }
3133

3234
} // end of namespace tests
3335
} // end of namespace cachelib

cachelib/allocator/tests/AllocatorMemoryTiersTest.h

+94
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,42 @@
2020
#include "cachelib/allocator/MemoryTierCacheConfig.h"
2121
#include "cachelib/allocator/tests/TestBase.h"
2222

23+
#include <folly/synchronization/Latch.h>
24+
2325
namespace facebook {
2426
namespace cachelib {
2527
namespace tests {
2628

2729
template <typename AllocatorT>
2830
class AllocatorMemoryTiersTest : public AllocatorTest<AllocatorT> {
31+
private:
32+
template<typename MvCallback>
33+
void testMultiTiersAsyncOpDuringMove(std::unique_ptr<AllocatorT>& alloc,
34+
PoolId& pool, bool& quit, MvCallback&& moveCb) {
35+
typename AllocatorT::Config config;
36+
config.setCacheSize(4 * Slab::kSize);
37+
config.enableCachePersistence("/tmp");
38+
config.configureMemoryTiers({
39+
MemoryTierCacheConfig::fromShm()
40+
.setRatio(1).setMemBind({0}),
41+
MemoryTierCacheConfig::fromShm()
42+
.setRatio(1).setMemBind({0})
43+
});
44+
45+
config.enableMovingOnSlabRelease(moveCb, {} /* ChainedItemsMoveSync */,
46+
-1 /* movingAttemptsLimit */);
47+
48+
alloc = std::make_unique<AllocatorT>(AllocatorT::SharedMemNew, config);
49+
ASSERT(alloc != nullptr);
50+
pool = alloc->addPool("default", alloc->getCacheMemoryStats().cacheSize);
51+
52+
int i = 0;
53+
while(!quit) {
54+
auto handle = alloc->allocate(pool, std::to_string(++i), std::string("value").size());
55+
ASSERT(handle != nullptr);
56+
ASSERT_NO_THROW(alloc->insertOrReplace(handle));
57+
}
58+
}
2959
public:
3060
void testMultiTiersFormFileInvalid() {
3161
typename AllocatorT::Config config;
@@ -124,6 +154,70 @@ class AllocatorMemoryTiersTest : public AllocatorTest<AllocatorT> {
124154
ASSERT(handle != nullptr);
125155
ASSERT_NO_THROW(alloc->insertOrReplace(handle));
126156
}
157+
158+
void testMultiTiersRemoveDuringEviction() {
159+
std::unique_ptr<AllocatorT> alloc;
160+
PoolId pool;
161+
std::unique_ptr<std::thread> t;
162+
folly::Latch latch(1);
163+
bool quit = false;
164+
165+
auto moveCb = [&] (typename AllocatorT::Item& oldItem,
166+
typename AllocatorT::Item& newItem,
167+
typename AllocatorT::Item* /* parentPtr */) {
168+
169+
auto key = oldItem.getKey();
170+
t = std::make_unique<std::thread>([&](){
171+
// remove() function is blocked by wait context
172+
// till item is moved to next tier. So that, we should
173+
// notify latch before calling remove()
174+
latch.count_down();
175+
alloc->remove(key);
176+
});
177+
// wait till async thread is running
178+
latch.wait();
179+
memcpy(newItem.getMemory(), oldItem.getMemory(), oldItem.getSize());
180+
quit = true;
181+
};
182+
183+
testMultiTiersAsyncOpDuringMove(alloc, pool, quit, moveCb);
184+
185+
t->join();
186+
}
187+
188+
void testMultiTiersReplaceDuringEviction() {
189+
std::unique_ptr<AllocatorT> alloc;
190+
PoolId pool;
191+
std::unique_ptr<std::thread> t;
192+
folly::Latch latch(1);
193+
bool quit = false;
194+
195+
auto moveCb = [&] (typename AllocatorT::Item& oldItem,
196+
typename AllocatorT::Item& newItem,
197+
typename AllocatorT::Item* /* parentPtr */) {
198+
auto key = oldItem.getKey();
199+
if(!quit) {
200+
// we need to replace only once because subsequent allocate calls
201+
// will cause evictions recursevly
202+
quit = true;
203+
t = std::make_unique<std::thread>([&](){
204+
auto handle = alloc->allocate(pool, key, std::string("new value").size());
205+
// insertOrReplace() function is blocked by wait context
206+
// till item is moved to next tier. So that, we should
207+
// notify latch before calling insertOrReplace()
208+
latch.count_down();
209+
ASSERT_NO_THROW(alloc->insertOrReplace(handle));
210+
});
211+
// wait till async thread is running
212+
latch.wait();
213+
}
214+
memcpy(newItem.getMemory(), oldItem.getMemory(), oldItem.getSize());
215+
};
216+
217+
testMultiTiersAsyncOpDuringMove(alloc, pool, quit, moveCb);
218+
219+
t->join();
220+
}
127221
};
128222
} // namespace tests
129223
} // namespace cachelib

0 commit comments

Comments
 (0)