@@ -44,8 +44,6 @@ CacheAllocator<CacheTrait>::CacheAllocator(Config config)
44
44
[this ](Item* it) -> ItemHandle { return acquire (it); })),
45
45
chainedItemLocks_ (config_.chainedItemsLockPower,
46
46
std::make_shared<MurmurHash2>()),
47
- movesMap_(kShards ),
48
- moveLock_(kShards ),
49
47
cacheCreationTime_{util::getCurrentTimeSec ()} {
50
48
51
49
if (numTiers_ > 1 || std::holds_alternative<FileShmSegmentOpts>(
@@ -132,8 +130,6 @@ CacheAllocator<CacheTrait>::CacheAllocator(SharedMemNewT, Config config)
132
130
[this](Item* it) -> ItemHandle { return acquire (it); })),
133
131
chainedItemLocks_(config_.chainedItemsLockPower,
134
132
std::make_shared<MurmurHash2>()),
135
- movesMap_(kShards ),
136
- moveLock_(kShards ),
137
133
cacheCreationTime_{util::getCurrentTimeSec ()} {
138
134
initCommon (false );
139
135
shmManager_->removeShm (detail::kShmInfoName ,
@@ -170,8 +166,6 @@ CacheAllocator<CacheTrait>::CacheAllocator(SharedMemAttachT, Config config)
170
166
[this](Item* it) -> ItemHandle { return acquire (it); })),
171
167
chainedItemLocks_(config_.chainedItemsLockPower,
172
168
std::make_shared<MurmurHash2>()),
173
- movesMap_(kShards ),
174
- moveLock_(kShards ),
175
169
cacheCreationTime_{*metadata_.cacheCreationTime_ref ()} {
176
170
/* TODO - per tier? */
177
171
for (auto pid : *metadata_.compactCachePools_ref ()) {
@@ -272,6 +266,14 @@ void CacheAllocator<CacheTrait>::initCommon(bool dramCacheAttached) {
272
266
nvmAdmissionPolicy_->initMinTTL (config_.nvmAdmissionMinTTL );
273
267
}
274
268
}
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
+ }
275
277
initStats ();
276
278
initNvmCache (dramCacheAttached);
277
279
initWorkers ();
@@ -1260,159 +1262,11 @@ CacheAllocator<CacheTrait>::insertOrReplace(const ItemHandle& handle) {
1260
1262
return replaced;
1261
1263
}
1262
1264
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
-
1413
1265
template <typename CacheTrait>
1266
+ template <typename Predicate>
1414
1267
bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
1415
- ItemHandle& newItemHdl) {
1268
+ ItemHandle& newItemHdl,
1269
+ Predicate &&predicate) {
1416
1270
XDCHECK (config_.moveCb );
1417
1271
util::LatencyTracker tracker{stats_.moveRegularLatency_ };
1418
1272
@@ -1421,8 +1275,6 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
1421
1275
}
1422
1276
1423
1277
XDCHECK_EQ (newItemHdl->getSize (), oldItem.getSize ());
1424
- XDCHECK_EQ (reinterpret_cast <uintptr_t >(&getMMContainer (oldItem)),
1425
- reinterpret_cast <uintptr_t >(&getMMContainer (*newItemHdl)));
1426
1278
1427
1279
// take care of the flags before we expose the item to be accessed. this
1428
1280
// will ensure that when another thread removes the item from RAM, we issue
@@ -1448,7 +1300,7 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
1448
1300
// this item through an API such as remove(ItemHandle). In this case,
1449
1301
// it is unsafe to replace the old item with a new one, so we should
1450
1302
// also abort.
1451
- if (!accessContainer_->replaceIf (oldItem, *newItemHdl, itemMovingPredicate )) {
1303
+ if (!accessContainer_->replaceIf (oldItem, *newItemHdl, predicate )) {
1452
1304
return false ;
1453
1305
}
1454
1306
@@ -1489,62 +1341,6 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
1489
1341
return true ;
1490
1342
}
1491
1343
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
-
1548
1344
template <typename CacheTrait>
1549
1345
bool CacheAllocator<CacheTrait>::moveChainedItem(ChainedItem& oldItem,
1550
1346
ItemHandle& newItemHdl) {
@@ -1784,8 +1580,9 @@ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
1784
1580
1785
1581
if (newItemHdl) {
1786
1582
XDCHECK_EQ (newItemHdl->getSize (), item.getSize ());
1787
-
1788
- return moveRegularItemOnEviction (item, newItemHdl);
1583
+ if (tryMovingForSlabRelease (item, newItemHdl, itemMovingPredicate)) {
1584
+ return acquire (&item);
1585
+ }
1789
1586
}
1790
1587
}
1791
1588
@@ -1811,7 +1608,11 @@ CacheAllocator<CacheTrait>::tryPromoteToNextMemoryTier(
1811
1608
1812
1609
if (newItemHdl) {
1813
1610
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))) {
1815
1616
return true ;
1816
1617
}
1817
1618
}
@@ -2927,7 +2728,7 @@ bool CacheAllocator<CacheTrait>::moveForSlabRelease(
2927
2728
2928
2729
// if we have a valid handle, try to move, if not, we retry.
2929
2730
if (newItemHdl) {
2930
- isMoved = tryMovingForSlabRelease (oldItem, newItemHdl);
2731
+ isMoved = tryMovingForSlabRelease (oldItem, newItemHdl, itemMovingPredicate );
2931
2732
if (isMoved) {
2932
2733
break ;
2933
2734
}
@@ -3050,8 +2851,9 @@ CacheAllocator<CacheTrait>::allocateNewItemForOldItem(const Item& oldItem) {
3050
2851
}
3051
2852
3052
2853
template <typename CacheTrait>
2854
+ template <typename Predicate>
3053
2855
bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
3054
- Item& oldItem, ItemHandle& newItemHdl) {
2856
+ Item& oldItem, ItemHandle& newItemHdl, Predicate&& predicate ) {
3055
2857
// By holding onto a user-level synchronization object, we ensure moving
3056
2858
// a regular item or chained item is synchronized with any potential
3057
2859
// user-side mutation.
@@ -3083,7 +2885,7 @@ bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
3083
2885
3084
2886
return oldItem.isChainedItem ()
3085
2887
? moveChainedItem (oldItem.asChainedItem (), newItemHdl)
3086
- : moveRegularItem (oldItem, newItemHdl);
2888
+ : moveRegularItem (oldItem, newItemHdl, std::move (predicate) );
3087
2889
}
3088
2890
3089
2891
template <typename CacheTrait>
0 commit comments