@@ -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 ();
@@ -1290,159 +1292,11 @@ CacheAllocator<CacheTrait>::insertOrReplace(const ItemHandle& handle) {
12901292 return replaced;
12911293}
12921294
1293- /* Next two methods are used to asynchronously move Item between memory tiers.
1294- *
1295- * The thread, which moves Item, allocates new Item in the tier we are moving to
1296- * and calls moveRegularItemOnEviction() method. This method does the following:
1297- * 1. Create MoveCtx and put it to the movesMap.
1298- * 2. Update the access container with the new item from the tier we are
1299- * moving to. This Item has kIncomplete flag set.
1300- * 3. Copy data from the old Item to the new one.
1301- * 4. Unset the kIncomplete flag and Notify MoveCtx
1302- *
1303- * Concurrent threads which are getting handle to the same key:
1304- * 1. When a handle is created it checks if the kIncomplete flag is set
1305- * 2. If so, Handle implementation creates waitContext and adds it to the
1306- * MoveCtx by calling addWaitContextForMovingItem() method.
1307- * 3. Wait until the moving thread will complete its job.
1308- */
1309- template <typename CacheTrait>
1310- bool CacheAllocator<CacheTrait>::addWaitContextForMovingItem(
1311- folly::StringPiece key, std::shared_ptr<WaitContext<ReadHandle>> waiter) {
1312- auto shard = getShardForKey (key);
1313- auto & movesMap = getMoveMapForShard (shard);
1314- auto lock = getMoveLockForShard (shard);
1315- auto it = movesMap.find (key);
1316- if (it == movesMap.end ()) {
1317- return false ;
1318- }
1319- auto ctx = it->second .get ();
1320- ctx->addWaiter (std::move (waiter));
1321- return true ;
1322- }
1323-
1324- template <typename CacheTrait>
1325- typename CacheAllocator<CacheTrait>::ItemHandle
1326- CacheAllocator<CacheTrait>::moveRegularItemOnEviction(
1327- Item& oldItem, ItemHandle& newItemHdl) {
1328- XDCHECK (oldItem.isMoving ());
1329- // TODO: should we introduce new latency tracker. E.g. evictRegularLatency_
1330- // ??? util::LatencyTracker tracker{stats_.evictRegularLatency_};
1331-
1332- if (!oldItem.isAccessible () || oldItem.isExpired ()) {
1333- return {};
1334- }
1335-
1336- XDCHECK_EQ (newItemHdl->getSize (), oldItem.getSize ());
1337- XDCHECK_NE (getTierId (oldItem), getTierId (*newItemHdl));
1338-
1339- // take care of the flags before we expose the item to be accessed. this
1340- // will ensure that when another thread removes the item from RAM, we issue
1341- // a delete accordingly. See D7859775 for an example
1342- if (oldItem.isNvmClean ()) {
1343- newItemHdl->markNvmClean ();
1344- }
1345-
1346- folly::StringPiece key (oldItem.getKey ());
1347- auto shard = getShardForKey (key);
1348- auto & movesMap = getMoveMapForShard (shard);
1349- MoveCtx* ctx (nullptr );
1350- {
1351- auto lock = getMoveLockForShard (shard);
1352- auto res = movesMap.try_emplace (key, std::make_unique<MoveCtx>());
1353- if (!res.second ) {
1354- return {};
1355- }
1356- ctx = res.first ->second .get ();
1357- }
1358-
1359- auto resHdl = ItemHandle{};
1360- auto guard = folly::makeGuard ([key, this , ctx, shard, &resHdl]() {
1361- auto & movesMap = getMoveMapForShard (shard);
1362- if (resHdl)
1363- resHdl->unmarkIncomplete ();
1364- auto lock = getMoveLockForShard (shard);
1365- ctx->setItemHandle (std::move (resHdl));
1366- movesMap.erase (key);
1367- });
1368-
1369- // TODO: Possibly we can use markMoving() instead. But today
1370- // moveOnSlabRelease logic assume that we mark as moving old Item
1371- // and than do copy and replace old Item with the new one in access
1372- // container. Furthermore, Item can be marked as Moving only
1373- // if it is linked to MM container. In our case we mark the new Item
1374- // and update access container before the new Item is ready (content is
1375- // copied).
1376- newItemHdl->markIncomplete ();
1377-
1378- // Inside the access container's lock, this checks if the old item is
1379- // accessible and its refcount is zero. If the item is not accessible,
1380- // there is no point to replace it since it had already been removed
1381- // or in the process of being removed. If the item is in cache but the
1382- // refcount is non-zero, it means user could be attempting to remove
1383- // this item through an API such as remove(ItemHandle). In this case,
1384- // it is unsafe to replace the old item with a new one, so we should
1385- // also abort.
1386- if (!accessContainer_->replaceIf (oldItem, *newItemHdl,
1387- itemMovingPredicate)) {
1388- return {};
1389- }
1390-
1391- if (config_.moveCb ) {
1392- // Execute the move callback. We cannot make any guarantees about the
1393- // consistency of the old item beyond this point, because the callback can
1394- // do more than a simple memcpy() e.g. update external references. If there
1395- // are any remaining handles to the old item, it is the caller's
1396- // responsibility to invalidate them. The move can only fail after this
1397- // statement if the old item has been removed or replaced, in which case it
1398- // should be fine for it to be left in an inconsistent state.
1399- config_.moveCb (oldItem, *newItemHdl, nullptr );
1400- } else {
1401- std::memcpy (newItemHdl->getWritableMemory (), oldItem.getMemory (),
1402- oldItem.getSize ());
1403- }
1404-
1405- // Inside the MM container's lock, this checks if the old item exists to
1406- // make sure that no other thread removed it, and only then replaces it.
1407- if (!replaceInMMContainer (oldItem, *newItemHdl)) {
1408- accessContainer_->remove (*newItemHdl);
1409- return {};
1410- }
1411-
1412- // Replacing into the MM container was successful, but someone could have
1413- // called insertOrReplace() or remove() before or after the
1414- // replaceInMMContainer() operation, which would invalidate newItemHdl.
1415- if (!newItemHdl->isAccessible ()) {
1416- removeFromMMContainer (*newItemHdl);
1417- return {};
1418- }
1419-
1420- // no one can add or remove chained items at this point
1421- if (oldItem.hasChainedItem ()) {
1422- // safe to acquire handle for a moving Item
1423- auto oldHandle = acquire (&oldItem);
1424- XDCHECK_EQ (1u , oldHandle->getRefCount ()) << oldHandle->toString ();
1425- XDCHECK (!newItemHdl->hasChainedItem ()) << newItemHdl->toString ();
1426- try {
1427- auto l = chainedItemLocks_.lockExclusive (oldItem.getKey ());
1428- transferChainLocked (oldHandle, newItemHdl);
1429- } catch (const std::exception& e) {
1430- // this should never happen because we drained all the handles.
1431- XLOGF (DFATAL, " {}" , e.what ());
1432- throw ;
1433- }
1434-
1435- XDCHECK (!oldItem.hasChainedItem ());
1436- XDCHECK (newItemHdl->hasChainedItem ());
1437- }
1438- newItemHdl.unmarkNascent ();
1439- resHdl = std::move (newItemHdl); // guard will assign it to ctx under lock
1440- return acquire (&oldItem);
1441- }
1442-
14431295template <typename CacheTrait>
1296+ template <typename Predicate>
14441297bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
1445- ItemHandle& newItemHdl) {
1298+ ItemHandle& newItemHdl,
1299+ Predicate &&predicate) {
14461300 XDCHECK (config_.moveCb );
14471301 util::LatencyTracker tracker{stats_.moveRegularLatency_ };
14481302
@@ -1451,8 +1305,6 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
14511305 }
14521306
14531307 XDCHECK_EQ (newItemHdl->getSize (), oldItem.getSize ());
1454- XDCHECK_EQ (reinterpret_cast <uintptr_t >(&getMMContainer (oldItem)),
1455- reinterpret_cast <uintptr_t >(&getMMContainer (*newItemHdl)));
14561308
14571309 // take care of the flags before we expose the item to be accessed. this
14581310 // will ensure that when another thread removes the item from RAM, we issue
@@ -1478,7 +1330,7 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
14781330 // this item through an API such as remove(ItemHandle). In this case,
14791331 // it is unsafe to replace the old item with a new one, so we should
14801332 // also abort.
1481- if (!accessContainer_->replaceIf (oldItem, *newItemHdl, itemMovingPredicate )) {
1333+ if (!accessContainer_->replaceIf (oldItem, *newItemHdl, predicate )) {
14821334 return false ;
14831335 }
14841336
@@ -1519,63 +1371,6 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
15191371 return true ;
15201372}
15211373
1522- template <typename CacheTrait>
1523- bool CacheAllocator<CacheTrait>::moveRegularItemForPromotion(Item& oldItem,
1524- ItemHandle& newItemHdl) {
1525- XDCHECK (config_.moveCb );
1526- util::LatencyTracker tracker{stats_.moveRegularLatency_ };
1527-
1528- if (!oldItem.isAccessible () || oldItem.isExpired ()) {
1529- return false ;
1530- }
1531-
1532- XDCHECK_EQ (newItemHdl->getSize (), oldItem.getSize ());
1533-
1534- if (config_.moveCb ) {
1535- // Execute the move callback. We cannot make any guarantees about the
1536- // consistency of the old item beyond this point, because the callback can
1537- // do more than a simple memcpy() e.g. update external references. If there
1538- // are any remaining handles to the old item, it is the caller's
1539- // responsibility to invalidate them. The move can only fail after this
1540- // statement if the old item has been removed or replaced, in which case it
1541- // should be fine for it to be left in an inconsistent state.
1542- config_.moveCb (oldItem, *newItemHdl, nullptr );
1543- } else {
1544- std::memcpy (newItemHdl->getWritableMemory (), oldItem.getMemory (),
1545- oldItem.getSize ());
1546- }
1547-
1548- auto predicate = [this ](const Item& item) {
1549- // if inclusive cache is allowed, replace even if there are active users.
1550- return config_.numDuplicateElements > 0 || item.getRefCount () == 0 ;
1551- };
1552- if (!accessContainer_->replaceIf (oldItem, *newItemHdl, predicate)) {
1553- return false ;
1554- }
1555-
1556- // Inside the MM container's lock, this checks if the old item exists to
1557- // make sure that no other thread removed it, and only then replaces it.
1558- if (!replaceInMMContainer (oldItem, *newItemHdl)) {
1559- accessContainer_->remove (*newItemHdl);
1560- return false ;
1561- }
1562-
1563- // Replacing into the MM container was successful, but someone could have
1564- // called insertOrReplace() or remove() before or after the
1565- // replaceInMMContainer() operation, which would invalidate newItemHdl.
1566- if (!newItemHdl->isAccessible ()) {
1567- removeFromMMContainer (*newItemHdl);
1568- return false ;
1569- }
1570-
1571- // no one can add or remove chained items at this point
1572- if (oldItem.hasChainedItem ()) {
1573- throw std::runtime_error (" Not supported" );
1574- }
1575- newItemHdl.unmarkNascent ();
1576- return true ;
1577- }
1578-
15791374template <typename CacheTrait>
15801375bool CacheAllocator<CacheTrait>::moveChainedItem(ChainedItem& oldItem,
15811376 ItemHandle& newItemHdl) {
@@ -1815,8 +1610,9 @@ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
18151610
18161611 if (newItemHdl) {
18171612 XDCHECK_EQ (newItemHdl->getSize (), item.getSize ());
1818-
1819- return moveRegularItemOnEviction (item, newItemHdl);
1613+ if (tryMovingForSlabRelease (item, newItemHdl, itemMovingPredicate)) {
1614+ return acquire (&item);
1615+ }
18201616 }
18211617 }
18221618
@@ -1842,7 +1638,11 @@ CacheAllocator<CacheTrait>::tryPromoteToNextMemoryTier(
18421638
18431639 if (newItemHdl) {
18441640 XDCHECK_EQ (newItemHdl->getSize (), item.getSize ());
1845- if (moveRegularItemForPromotion (item, newItemHdl)) {
1641+ auto predicate = [this ](const Item& item) {
1642+ // if inclusive cache is allowed, replace even if there are active users.
1643+ return config_.numDuplicateElements > 0 || item.getRefCount () == 0 ;
1644+ };
1645+ if (tryMovingForSlabRelease (item, newItemHdl, std::move (predicate))) {
18461646 return true ;
18471647 }
18481648 }
@@ -2919,7 +2719,7 @@ bool CacheAllocator<CacheTrait>::moveForSlabRelease(
29192719
29202720 // if we have a valid handle, try to move, if not, we retry.
29212721 if (newItemHdl) {
2922- isMoved = tryMovingForSlabRelease (oldItem, newItemHdl);
2722+ isMoved = tryMovingForSlabRelease (oldItem, newItemHdl, itemMovingPredicate );
29232723 if (isMoved) {
29242724 break ;
29252725 }
@@ -3042,8 +2842,9 @@ CacheAllocator<CacheTrait>::allocateNewItemForOldItem(const Item& oldItem) {
30422842}
30432843
30442844template <typename CacheTrait>
2845+ template <typename Predicate>
30452846bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
3046- Item& oldItem, ItemHandle& newItemHdl) {
2847+ Item& oldItem, ItemHandle& newItemHdl, Predicate&& predicate ) {
30472848 // By holding onto a user-level synchronization object, we ensure moving
30482849 // a regular item or chained item is synchronized with any potential
30492850 // user-side mutation.
@@ -3075,7 +2876,7 @@ bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
30752876
30762877 return oldItem.isChainedItem ()
30772878 ? moveChainedItem (oldItem.asChainedItem (), newItemHdl)
3078- : moveRegularItem (oldItem, newItemHdl);
2879+ : moveRegularItem (oldItem, newItemHdl, std::move (predicate) );
30792880}
30802881
30812882template <typename CacheTrait>
0 commit comments