@@ -44,8 +44,6 @@ CacheAllocator<CacheTrait>::CacheAllocator(Config config)
4444 [this ](Item* it) -> ItemHandle { return acquire (it); })),
4545 chainedItemLocks_ (config_.chainedItemsLockPower,
4646 std::make_shared<MurmurHash2>()),
47- movesMap_(kShards ),
48- moveLock_(kShards ),
4947 cacheCreationTime_{util::getCurrentTimeSec ()} {
5048
5149 if (numTiers_ > 1 || std::holds_alternative<FileShmSegmentOpts>(
@@ -132,8 +130,6 @@ CacheAllocator<CacheTrait>::CacheAllocator(SharedMemNewT, Config config)
132130 [this](Item* it) -> ItemHandle { return acquire (it); })),
133131 chainedItemLocks_(config_.chainedItemsLockPower,
134132 std::make_shared<MurmurHash2>()),
135- movesMap_(kShards ),
136- moveLock_(kShards ),
137133 cacheCreationTime_{util::getCurrentTimeSec ()} {
138134 initCommon (false );
139135 shmManager_->removeShm (detail::kShmInfoName ,
@@ -170,8 +166,6 @@ CacheAllocator<CacheTrait>::CacheAllocator(SharedMemAttachT, Config config)
170166 [this](Item* it) -> ItemHandle { return acquire (it); })),
171167 chainedItemLocks_(config_.chainedItemsLockPower,
172168 std::make_shared<MurmurHash2>()),
173- movesMap_(kShards ),
174- moveLock_(kShards ),
175169 cacheCreationTime_{*metadata_.cacheCreationTime_ref ()} {
176170 /* TODO - per tier? */
177171 for (auto pid : *metadata_.compactCachePools_ref ()) {
@@ -272,6 +266,14 @@ void CacheAllocator<CacheTrait>::initCommon(bool dramCacheAttached) {
272266 nvmAdmissionPolicy_->initMinTTL (config_.nvmAdmissionMinTTL );
273267 }
274268 }
269+ if (numTiers_ > 1 && !config_.moveCb ) {
270+ XLOG (WARN, " No moveCb set, using memcpy for moving items between tiers." );
271+ config_.moveCb = [](Item& oldItem, Item& newItem, Item* parentItem){
272+ if (parentItem != nullptr )
273+ throw std::runtime_error (" TODO: chained items not supported" );
274+ std::memcpy (newItem.getMemory (), oldItem.getMemory (), oldItem.getSize ());
275+ };
276+ }
275277 initStats ();
276278 initNvmCache (dramCacheAttached);
277279 initWorkers ();
@@ -1260,159 +1262,11 @@ CacheAllocator<CacheTrait>::insertOrReplace(const ItemHandle& handle) {
12601262 return replaced;
12611263}
12621264
1263- /* Next two methods are used to asynchronously move Item between memory tiers.
1264- *
1265- * The thread, which moves Item, allocates new Item in the tier we are moving to
1266- * and calls moveRegularItemOnEviction() method. This method does the following:
1267- * 1. Create MoveCtx and put it to the movesMap.
1268- * 2. Update the access container with the new item from the tier we are
1269- * moving to. This Item has kIncomplete flag set.
1270- * 3. Copy data from the old Item to the new one.
1271- * 4. Unset the kIncomplete flag and Notify MoveCtx
1272- *
1273- * Concurrent threads which are getting handle to the same key:
1274- * 1. When a handle is created it checks if the kIncomplete flag is set
1275- * 2. If so, Handle implementation creates waitContext and adds it to the
1276- * MoveCtx by calling addWaitContextForMovingItem() method.
1277- * 3. Wait until the moving thread will complete its job.
1278- */
1279- template <typename CacheTrait>
1280- bool CacheAllocator<CacheTrait>::addWaitContextForMovingItem(
1281- folly::StringPiece key, std::shared_ptr<WaitContext<ReadHandle>> waiter) {
1282- auto shard = getShardForKey (key);
1283- auto & movesMap = getMoveMapForShard (shard);
1284- auto lock = getMoveLockForShard (shard);
1285- auto it = movesMap.find (key);
1286- if (it == movesMap.end ()) {
1287- return false ;
1288- }
1289- auto ctx = it->second .get ();
1290- ctx->addWaiter (std::move (waiter));
1291- return true ;
1292- }
1293-
1294- template <typename CacheTrait>
1295- typename CacheAllocator<CacheTrait>::ItemHandle
1296- CacheAllocator<CacheTrait>::moveRegularItemOnEviction(
1297- Item& oldItem, ItemHandle& newItemHdl) {
1298- XDCHECK (oldItem.isMoving ());
1299- // TODO: should we introduce new latency tracker. E.g. evictRegularLatency_
1300- // ??? util::LatencyTracker tracker{stats_.evictRegularLatency_};
1301-
1302- if (!oldItem.isAccessible () || oldItem.isExpired ()) {
1303- return {};
1304- }
1305-
1306- XDCHECK_EQ (newItemHdl->getSize (), oldItem.getSize ());
1307- XDCHECK_NE (getTierId (oldItem), getTierId (*newItemHdl));
1308-
1309- // take care of the flags before we expose the item to be accessed. this
1310- // will ensure that when another thread removes the item from RAM, we issue
1311- // a delete accordingly. See D7859775 for an example
1312- if (oldItem.isNvmClean ()) {
1313- newItemHdl->markNvmClean ();
1314- }
1315-
1316- folly::StringPiece key (oldItem.getKey ());
1317- auto shard = getShardForKey (key);
1318- auto & movesMap = getMoveMapForShard (shard);
1319- MoveCtx* ctx (nullptr );
1320- {
1321- auto lock = getMoveLockForShard (shard);
1322- auto res = movesMap.try_emplace (key, std::make_unique<MoveCtx>());
1323- if (!res.second ) {
1324- return {};
1325- }
1326- ctx = res.first ->second .get ();
1327- }
1328-
1329- auto resHdl = ItemHandle{};
1330- auto guard = folly::makeGuard ([key, this , ctx, shard, &resHdl]() {
1331- auto & movesMap = getMoveMapForShard (shard);
1332- if (resHdl)
1333- resHdl->unmarkIncomplete ();
1334- auto lock = getMoveLockForShard (shard);
1335- ctx->setItemHandle (std::move (resHdl));
1336- movesMap.erase (key);
1337- });
1338-
1339- // TODO: Possibly we can use markMoving() instead. But today
1340- // moveOnSlabRelease logic assume that we mark as moving old Item
1341- // and than do copy and replace old Item with the new one in access
1342- // container. Furthermore, Item can be marked as Moving only
1343- // if it is linked to MM container. In our case we mark the new Item
1344- // and update access container before the new Item is ready (content is
1345- // copied).
1346- newItemHdl->markIncomplete ();
1347-
1348- // Inside the access container's lock, this checks if the old item is
1349- // accessible and its refcount is zero. If the item is not accessible,
1350- // there is no point to replace it since it had already been removed
1351- // or in the process of being removed. If the item is in cache but the
1352- // refcount is non-zero, it means user could be attempting to remove
1353- // this item through an API such as remove(ItemHandle). In this case,
1354- // it is unsafe to replace the old item with a new one, so we should
1355- // also abort.
1356- if (!accessContainer_->replaceIf (oldItem, *newItemHdl,
1357- itemMovingPredicate)) {
1358- return {};
1359- }
1360-
1361- if (config_.moveCb ) {
1362- // Execute the move callback. We cannot make any guarantees about the
1363- // consistency of the old item beyond this point, because the callback can
1364- // do more than a simple memcpy() e.g. update external references. If there
1365- // are any remaining handles to the old item, it is the caller's
1366- // responsibility to invalidate them. The move can only fail after this
1367- // statement if the old item has been removed or replaced, in which case it
1368- // should be fine for it to be left in an inconsistent state.
1369- config_.moveCb (oldItem, *newItemHdl, nullptr );
1370- } else {
1371- std::memcpy (newItemHdl->getWritableMemory (), oldItem.getMemory (),
1372- oldItem.getSize ());
1373- }
1374-
1375- // Inside the MM container's lock, this checks if the old item exists to
1376- // make sure that no other thread removed it, and only then replaces it.
1377- if (!replaceInMMContainer (oldItem, *newItemHdl)) {
1378- accessContainer_->remove (*newItemHdl);
1379- return {};
1380- }
1381-
1382- // Replacing into the MM container was successful, but someone could have
1383- // called insertOrReplace() or remove() before or after the
1384- // replaceInMMContainer() operation, which would invalidate newItemHdl.
1385- if (!newItemHdl->isAccessible ()) {
1386- removeFromMMContainer (*newItemHdl);
1387- return {};
1388- }
1389-
1390- // no one can add or remove chained items at this point
1391- if (oldItem.hasChainedItem ()) {
1392- // safe to acquire handle for a moving Item
1393- auto oldHandle = acquire (&oldItem);
1394- XDCHECK_EQ (1u , oldHandle->getRefCount ()) << oldHandle->toString ();
1395- XDCHECK (!newItemHdl->hasChainedItem ()) << newItemHdl->toString ();
1396- try {
1397- auto l = chainedItemLocks_.lockExclusive (oldItem.getKey ());
1398- transferChainLocked (oldHandle, newItemHdl);
1399- } catch (const std::exception& e) {
1400- // this should never happen because we drained all the handles.
1401- XLOGF (DFATAL, " {}" , e.what ());
1402- throw ;
1403- }
1404-
1405- XDCHECK (!oldItem.hasChainedItem ());
1406- XDCHECK (newItemHdl->hasChainedItem ());
1407- }
1408- newItemHdl.unmarkNascent ();
1409- resHdl = std::move (newItemHdl); // guard will assign it to ctx under lock
1410- return acquire (&oldItem);
1411- }
1412-
14131265template <typename CacheTrait>
1266+ template <typename Predicate>
14141267bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
1415- ItemHandle& newItemHdl) {
1268+ ItemHandle& newItemHdl,
1269+ Predicate &&predicate) {
14161270 XDCHECK (config_.moveCb );
14171271 util::LatencyTracker tracker{stats_.moveRegularLatency_ };
14181272
@@ -1421,8 +1275,6 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
14211275 }
14221276
14231277 XDCHECK_EQ (newItemHdl->getSize (), oldItem.getSize ());
1424- XDCHECK_EQ (reinterpret_cast <uintptr_t >(&getMMContainer (oldItem)),
1425- reinterpret_cast <uintptr_t >(&getMMContainer (*newItemHdl)));
14261278
14271279 // take care of the flags before we expose the item to be accessed. this
14281280 // will ensure that when another thread removes the item from RAM, we issue
@@ -1448,7 +1300,7 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
14481300 // this item through an API such as remove(ItemHandle). In this case,
14491301 // it is unsafe to replace the old item with a new one, so we should
14501302 // also abort.
1451- if (!accessContainer_->replaceIf (oldItem, *newItemHdl, itemMovingPredicate )) {
1303+ if (!accessContainer_->replaceIf (oldItem, *newItemHdl, predicate )) {
14521304 return false ;
14531305 }
14541306
@@ -1489,62 +1341,6 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
14891341 return true ;
14901342}
14911343
1492- template <typename CacheTrait>
1493- bool CacheAllocator<CacheTrait>::moveRegularItemForPromotion(Item& oldItem,
1494- ItemHandle& newItemHdl) {
1495- util::LatencyTracker tracker{stats_.moveRegularLatency_ };
1496-
1497- if (!oldItem.isAccessible () || oldItem.isExpired ()) {
1498- return false ;
1499- }
1500-
1501- XDCHECK_EQ (newItemHdl->getSize (), oldItem.getSize ());
1502-
1503- if (config_.moveCb ) {
1504- // Execute the move callback. We cannot make any guarantees about the
1505- // consistency of the old item beyond this point, because the callback can
1506- // do more than a simple memcpy() e.g. update external references. If there
1507- // are any remaining handles to the old item, it is the caller's
1508- // responsibility to invalidate them. The move can only fail after this
1509- // statement if the old item has been removed or replaced, in which case it
1510- // should be fine for it to be left in an inconsistent state.
1511- config_.moveCb (oldItem, *newItemHdl, nullptr );
1512- } else {
1513- std::memcpy (newItemHdl->getWritableMemory (), oldItem.getMemory (),
1514- oldItem.getSize ());
1515- }
1516-
1517- auto predicate = [this ](const Item& item) {
1518- // if inclusive cache is allowed, replace even if there are active users.
1519- return config_.numDuplicateElements > 0 || item.getRefCount () == 0 ;
1520- };
1521- if (!accessContainer_->replaceIf (oldItem, *newItemHdl, predicate)) {
1522- return false ;
1523- }
1524-
1525- // Inside the MM container's lock, this checks if the old item exists to
1526- // make sure that no other thread removed it, and only then replaces it.
1527- if (!replaceInMMContainer (oldItem, *newItemHdl)) {
1528- accessContainer_->remove (*newItemHdl);
1529- return false ;
1530- }
1531-
1532- // Replacing into the MM container was successful, but someone could have
1533- // called insertOrReplace() or remove() before or after the
1534- // replaceInMMContainer() operation, which would invalidate newItemHdl.
1535- if (!newItemHdl->isAccessible ()) {
1536- removeFromMMContainer (*newItemHdl);
1537- return false ;
1538- }
1539-
1540- // no one can add or remove chained items at this point
1541- if (oldItem.hasChainedItem ()) {
1542- throw std::runtime_error (" Not supported" );
1543- }
1544- newItemHdl.unmarkNascent ();
1545- return true ;
1546- }
1547-
15481344template <typename CacheTrait>
15491345bool CacheAllocator<CacheTrait>::moveChainedItem(ChainedItem& oldItem,
15501346 ItemHandle& newItemHdl) {
@@ -1784,8 +1580,9 @@ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
17841580
17851581 if (newItemHdl) {
17861582 XDCHECK_EQ (newItemHdl->getSize (), item.getSize ());
1787-
1788- return moveRegularItemOnEviction (item, newItemHdl);
1583+ if (tryMovingForSlabRelease (item, newItemHdl, itemMovingPredicate)) {
1584+ return acquire (&item);
1585+ }
17891586 }
17901587 }
17911588
@@ -1811,7 +1608,11 @@ CacheAllocator<CacheTrait>::tryPromoteToNextMemoryTier(
18111608
18121609 if (newItemHdl) {
18131610 XDCHECK_EQ (newItemHdl->getSize (), item.getSize ());
1814- if (moveRegularItemForPromotion (item, newItemHdl)) {
1611+ auto predicate = [this ](const Item& item) {
1612+ // if inclusive cache is allowed, replace even if there are active users.
1613+ return config_.numDuplicateElements > 0 || item.getRefCount () == 0 ;
1614+ };
1615+ if (tryMovingForSlabRelease (item, newItemHdl, std::move (predicate))) {
18151616 return true ;
18161617 }
18171618 }
@@ -2927,7 +2728,7 @@ bool CacheAllocator<CacheTrait>::moveForSlabRelease(
29272728
29282729 // if we have a valid handle, try to move, if not, we retry.
29292730 if (newItemHdl) {
2930- isMoved = tryMovingForSlabRelease (oldItem, newItemHdl);
2731+ isMoved = tryMovingForSlabRelease (oldItem, newItemHdl, itemMovingPredicate );
29312732 if (isMoved) {
29322733 break ;
29332734 }
@@ -3050,8 +2851,9 @@ CacheAllocator<CacheTrait>::allocateNewItemForOldItem(const Item& oldItem) {
30502851}
30512852
30522853template <typename CacheTrait>
2854+ template <typename Predicate>
30532855bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
3054- Item& oldItem, ItemHandle& newItemHdl) {
2856+ Item& oldItem, ItemHandle& newItemHdl, Predicate&& predicate ) {
30552857 // By holding onto a user-level synchronization object, we ensure moving
30562858 // a regular item or chained item is synchronized with any potential
30572859 // user-side mutation.
@@ -3083,7 +2885,7 @@ bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
30832885
30842886 return oldItem.isChainedItem ()
30852887 ? moveChainedItem (oldItem.asChainedItem (), newItemHdl)
3086- : moveRegularItem (oldItem, newItemHdl);
2888+ : moveRegularItem (oldItem, newItemHdl, std::move (predicate) );
30872889}
30882890
30892891template <typename CacheTrait>
0 commit comments