@@ -415,7 +415,7 @@ CacheAllocator<CacheTrait>::allocateInternalTier(TierId tid,
415415}
416416
417417template <typename CacheTrait>
418- typename CacheAllocator<CacheTrait>::ItemHandle
418+ typename CacheAllocator<CacheTrait>::WriteHandle
419419CacheAllocator<CacheTrait>::allocateInternal(PoolId pid,
420420 typename Item::Key key,
421421 uint32_t size,
@@ -1186,14 +1186,13 @@ bool CacheAllocator<CacheTrait>::addWaitContextForMovingItem(
11861186}
11871187
11881188template <typename CacheTrait>
1189- template <typename ItemPtr>
11901189typename CacheAllocator<CacheTrait>::ItemHandle
11911190CacheAllocator<CacheTrait>::moveRegularItemOnEviction(
1192- ItemPtr& oldItemPtr, ItemHandle& newItemHdl) {
1191+ Item& oldItem, ItemHandle& newItemHdl) {
1192+ XDCHECK (oldItem.isMoving ());
11931193 // TODO: should we introduce new latency tracker. E.g. evictRegularLatency_
11941194 // ??? util::LatencyTracker tracker{stats_.evictRegularLatency_};
11951195
1196- Item& oldItem = *oldItemPtr;
11971196 if (!oldItem.isAccessible () || oldItem.isExpired ()) {
11981197 return {};
11991198 }
@@ -1249,7 +1248,7 @@ CacheAllocator<CacheTrait>::moveRegularItemOnEviction(
12491248 // it is unsafe to replace the old item with a new one, so we should
12501249 // also abort.
12511250 if (!accessContainer_->replaceIf (oldItem, *newItemHdl,
1252- itemEvictionPredicate )) {
1251+ itemMovingPredicate )) {
12531252 return {};
12541253 }
12551254
@@ -1269,7 +1268,7 @@ CacheAllocator<CacheTrait>::moveRegularItemOnEviction(
12691268
12701269 // Inside the MM container's lock, this checks if the old item exists to
12711270 // make sure that no other thread removed it, and only then replaces it.
1272- if (!replaceInMMContainer (oldItemPtr , *newItemHdl)) {
1271+ if (!replaceInMMContainer (oldItem , *newItemHdl)) {
12731272 accessContainer_->remove (*newItemHdl);
12741273 return {};
12751274 }
@@ -1459,36 +1458,45 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
14591458 itr) {
14601459 ++searchTries;
14611460
1462- Item* candidate = itr.get ();
1461+ Item* toRecycle = itr.get ();
1462+
1463+ Item* candidate =
1464+ toRecycle->isChainedItem ()
1465+ ? &toRecycle->asChainedItem ().getParentItem (compressor_)
1466+ : toRecycle;
1467+
1468+ // make sure no other thead is evicting the item
1469+ if (candidate->getRefCount () != 0 || !candidate->markMoving ()) {
1470+ ++itr;
1471+ continue ;
1472+ }
1473+
1474+ itr.destroy ();
1475+
14631476 // for chained items, the ownership of the parent can change. We try to
14641477 // evict what we think as parent and see if the eviction of parent
14651478 // recycles the child we intend to.
1466-
1467- ItemHandle toReleaseHandle = tryEvictToNextMemoryTier (tid, pid, itr);
1468- bool movedToNextTier = false ;
1469- if (toReleaseHandle) {
1470- movedToNextTier = true ;
1471- } else {
1472- toReleaseHandle =
1473- itr->isChainedItem ()
1474- ? advanceIteratorAndTryEvictChainedItem (tid, pid, itr)
1475- : advanceIteratorAndTryEvictRegularItem (tid, pid, mmContainer, itr);
1476- }
1479+ auto toReleaseHandle =
1480+ evictNormalItem (*candidate, true /* skipIfTokenInvalid */ );
1481+ auto ref = candidate->unmarkMoving ();
14771482
1478- if (toReleaseHandle) {
1479- if (toReleaseHandle ->hasChainedItem ()) {
1483+ if (toReleaseHandle || ref == 0u ) {
1484+ if (candidate ->hasChainedItem ()) {
14801485 (*stats_.chainedItemEvictions )[pid][cid].inc ();
14811486 } else {
14821487 (*stats_.regularItemEvictions )[pid][cid].inc ();
14831488 }
1489+ } else {
1490+ if (candidate->hasChainedItem ()) {
1491+ stats_.evictFailParentAC .inc ();
1492+ } else {
1493+ stats_.evictFailAC .inc ();
1494+ }
1495+ }
14841496
1485- // Invalidate iterator since later on we may use this mmContainer
1486- // again, which cannot be done unless we drop this iterator
1487- itr.destroy ();
1488-
1489- // we must be the last handle and for chained items, this will be
1490- // the parent.
1491- XDCHECK (toReleaseHandle.get () == candidate || candidate->isChainedItem ());
1497+ if (toReleaseHandle) {
1498+ XDCHECK (toReleaseHandle.get () == candidate);
1499+ XDCHECK (toRecycle == candidate || toRecycle->isChainedItem ());
14921500 XDCHECK_EQ (1u , toReleaseHandle->getRefCount ());
14931501
14941502 // We manually release the item here because we don't want to
@@ -1504,16 +1512,21 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
15041512 // recycle the candidate.
15051513 if (ReleaseRes::kRecycled ==
15061514 releaseBackToAllocator (itemToRelease, RemoveContext::kEviction ,
1507- /* isNascent */ movedToNextTier, candidate)) {
1508- return candidate;
1515+ /* isNascent */ false , toRecycle)) {
1516+ return toRecycle;
1517+ }
1518+ } else if (ref == 0u ) {
1519+ // it's safe to recycle the item here as there are no more
1520+ // references and the item could not been marked as moving
1521+ // by other thread since it's detached from MMContainer.
1522+ if (ReleaseRes::kRecycled ==
1523+ releaseBackToAllocator (*candidate, RemoveContext::kEviction ,
1524+ /* isNascent */ false , toRecycle)) {
1525+ return toRecycle;
15091526 }
15101527 }
15111528
1512- // If we destroyed the itr to possibly evict and failed, we restart
1513- // from the beginning again
1514- if (!itr) {
1515- itr.resetToBegin ();
1516- }
1529+ itr.resetToBegin ();
15171530 }
15181531 return nullptr ;
15191532}
@@ -1567,24 +1580,23 @@ bool CacheAllocator<CacheTrait>::shouldWriteToNvmCacheExclusive(
15671580}
15681581
15691582template <typename CacheTrait>
1570- template <typename ItemPtr>
1571- typename CacheAllocator<CacheTrait>::ItemHandle
1583+ typename CacheAllocator<CacheTrait>::WriteHandle
15721584CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
1573- TierId tid, PoolId pid, ItemPtr & item) {
1574- if (item-> isChainedItem ()) return {}; // TODO: We do not support ChainedItem yet
1575- if (item-> isExpired ()) return acquire (item);
1585+ TierId tid, PoolId pid, Item & item) {
1586+ if (item. isChainedItem ()) return {}; // TODO: We do not support ChainedItem yet
1587+ if (item. isExpired ()) return acquire (& item);
15761588
15771589 TierId nextTier = tid; // TODO - calculate this based on some admission policy
15781590 while (++nextTier < numTiers_) { // try to evict down to the next memory tiers
15791591 // allocateInternal might trigger another eviction
15801592 auto newItemHdl = allocateInternalTier (nextTier, pid,
1581- item-> getKey (),
1582- item-> getSize (),
1583- item-> getCreationTime (),
1584- item-> getExpiryTime ());
1593+ item. getKey (),
1594+ item. getSize (),
1595+ item. getCreationTime (),
1596+ item. getExpiryTime ());
15851597
15861598 if (newItemHdl) {
1587- XDCHECK_EQ (newItemHdl->getSize (), item-> getSize ());
1599+ XDCHECK_EQ (newItemHdl->getSize (), item. getSize ());
15881600
15891601 return moveRegularItemOnEviction (item, newItemHdl);
15901602 }
@@ -1594,149 +1606,11 @@ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
15941606}
15951607
15961608template <typename CacheTrait>
1597- typename CacheAllocator<CacheTrait>::ItemHandle
1598- CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(Item* item) {
1599- auto tid = getTierId (*item);
1600- auto pid = allocator_[tid]->getAllocInfo (item->getMemory ()).poolId ;
1601- return tryEvictToNextMemoryTier (tid, pid, item);
1602- }
1603-
1604- template <typename CacheTrait>
1605- typename CacheAllocator<CacheTrait>::ItemHandle
1606- CacheAllocator<CacheTrait>::advanceIteratorAndTryEvictRegularItem(
1607- TierId tid, PoolId pid, MMContainer& mmContainer, EvictionIterator& itr) {
1608- Item& item = *itr;
1609-
1610- const bool evictToNvmCache = shouldWriteToNvmCache (item);
1611-
1612- auto token = evictToNvmCache ? nvmCache_->createPutToken (item.getKey ())
1613- : typename NvmCacheT::PutToken{};
1614- // record the in-flight eviciton. If not, we move on to next item to avoid
1615- // stalling eviction.
1616- if (evictToNvmCache && !token.isValid ()) {
1617- ++itr;
1618- stats_.evictFailConcurrentFill .inc ();
1619- return ItemHandle{};
1620- }
1621-
1622- // If there are other accessors, we should abort. Acquire a handle here since
1623- // if we remove the item from both access containers and mm containers
1624- // below, we will need a handle to ensure proper cleanup in case we end up
1625- // not evicting this item
1626- auto evictHandle = accessContainer_->removeIf (item, &itemEvictionPredicate);
1627-
1628- if (!evictHandle) {
1629- ++itr;
1630- stats_.evictFailAC .inc ();
1631- return evictHandle;
1632- }
1633-
1634- mmContainer.remove (itr);
1635- XDCHECK_EQ (reinterpret_cast <uintptr_t >(evictHandle.get ()),
1636- reinterpret_cast <uintptr_t >(&item));
1637- XDCHECK (!evictHandle->isInMMContainer ());
1638- XDCHECK (!evictHandle->isAccessible ());
1639-
1640- // If the item is now marked as moving, that means its corresponding slab is
1641- // being released right now. So, we look for the next item that is eligible
1642- // for eviction. It is safe to destroy the handle here since the moving bit
1643- // is set. Iterator was already advance by the remove call above.
1644- if (evictHandle->isMoving ()) {
1645- stats_.evictFailMove .inc ();
1646- return ItemHandle{};
1647- }
1648-
1649- // Invalidate iterator since later on if we are not evicting this
1650- // item, we may need to rely on the handle we created above to ensure
1651- // proper cleanup if the item's raw refcount has dropped to 0.
1652- // And since this item may be a parent item that has some child items
1653- // in this very same mmContainer, we need to make sure we drop this
1654- // exclusive iterator so we can gain access to it when we're cleaning
1655- // up the child items
1656- itr.destroy ();
1657-
1658- // Ensure that there are no accessors after removing from the access
1659- // container
1660- XDCHECK (evictHandle->getRefCount () == 1 );
1661-
1662- if (evictToNvmCache && shouldWriteToNvmCacheExclusive (item)) {
1663- XDCHECK (token.isValid ());
1664- nvmCache_->put (evictHandle, std::move (token));
1665- }
1666- return evictHandle;
1667- }
1668-
1669- template <typename CacheTrait>
1670- typename CacheAllocator<CacheTrait>::ItemHandle
1671- CacheAllocator<CacheTrait>::advanceIteratorAndTryEvictChainedItem(
1672- TierId tid, PoolId pid, EvictionIterator& itr) {
1673- XDCHECK (itr->isChainedItem ());
1674-
1675- ChainedItem* candidate = &itr->asChainedItem ();
1676- ++itr;
1677-
1678- // The parent could change at any point through transferChain. However, if
1679- // that happens, we would realize that the releaseBackToAllocator return
1680- // kNotRecycled and we would try another chained item, leading to transient
1681- // failure.
1682- auto & parent = candidate->getParentItem (compressor_);
1683-
1684- const bool evictToNvmCache = shouldWriteToNvmCache (parent);
1685-
1686- auto token = evictToNvmCache ? nvmCache_->createPutToken (parent.getKey ())
1687- : typename NvmCacheT::PutToken{};
1688-
1689- // if token is invalid, return. iterator is already advanced.
1690- if (evictToNvmCache && !token.isValid ()) {
1691- stats_.evictFailConcurrentFill .inc ();
1692- return ItemHandle{};
1693- }
1694-
1695- // check if the parent exists in the hashtable and refcount is drained.
1696- auto parentHandle =
1697- accessContainer_->removeIf (parent, &itemEvictionPredicate);
1698- if (!parentHandle) {
1699- stats_.evictFailParentAC .inc ();
1700- return parentHandle;
1701- }
1702-
1703- // Invalidate iterator since later on we may use the mmContainer
1704- // associated with this iterator which cannot be done unless we
1705- // drop this iterator
1706- //
1707- // This must be done once we know the parent is not nullptr.
1708- // Since we can very well be the last holder of this parent item,
1709- // which may have a chained item that is linked in this MM container.
1710- itr.destroy ();
1711-
1712- // Ensure we have the correct parent and we're the only user of the
1713- // parent, then free it from access container. Otherwise, we abort
1714- XDCHECK_EQ (reinterpret_cast <uintptr_t >(&parent),
1715- reinterpret_cast <uintptr_t >(parentHandle.get ()));
1716- XDCHECK_EQ (1u , parent.getRefCount ());
1717-
1718- removeFromMMContainer (*parentHandle);
1719-
1720- XDCHECK (!parent.isInMMContainer ());
1721- XDCHECK (!parent.isAccessible ());
1722-
1723- // TODO: add multi-tier support (similar as for unchained items)
1724-
1725- // We need to make sure the parent is not marked as moving
1726- // and we're the only holder of the parent item. Safe to destroy the handle
1727- // here since moving bit is set.
1728- if (parentHandle->isMoving ()) {
1729- stats_.evictFailParentMove .inc ();
1730- return ItemHandle{};
1731- }
1732-
1733- if (evictToNvmCache && shouldWriteToNvmCacheExclusive (*parentHandle)) {
1734- XDCHECK (token.isValid ());
1735- XDCHECK (parentHandle->hasChainedItem ());
1736- nvmCache_->put (parentHandle, std::move (token));
1737- }
1738-
1739- return parentHandle;
1609+ typename CacheAllocator<CacheTrait>::WriteHandle
1610+ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(Item& item) {
1611+ auto tid = getTierId (item);
1612+ auto pid = allocator_[tid]->getAllocInfo (item.getMemory ()).poolId ;
1613+ return tryEvictToNextMemoryTier (tid, pid, item);
17401614}
17411615
17421616template <typename CacheTrait>
@@ -2936,7 +2810,7 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
29362810 auto owningHandle =
29372811 item.isChainedItem ()
29382812 ? evictChainedItemForSlabRelease (item.asChainedItem ())
2939- : evictNormalItemForSlabRelease (item);
2813+ : evictNormalItem (item);
29402814
29412815 // we managed to evict the corresponding owner of the item and have the
29422816 // last handle for the owner.
@@ -2993,14 +2867,15 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
29932867
29942868template <typename CacheTrait>
29952869typename CacheAllocator<CacheTrait>::ItemHandle
2996- CacheAllocator<CacheTrait>::evictNormalItemForSlabRelease(Item& item) {
2870+ CacheAllocator<CacheTrait>::evictNormalItem(Item& item,
2871+ bool skipIfTokenInvalid) {
29972872 XDCHECK (item.isMoving ());
29982873
29992874 if (item.isOnlyMoving ()) {
30002875 return ItemHandle{};
30012876 }
30022877
3003- auto evictHandle = tryEvictToNextMemoryTier (& item);
2878+ auto evictHandle = tryEvictToNextMemoryTier (item);
30042879 if (evictHandle) return evictHandle;
30052880
30062881 auto predicate = [](const Item& it) { return it.getRefCount () == 0 ; };
@@ -3009,6 +2884,11 @@ CacheAllocator<CacheTrait>::evictNormalItemForSlabRelease(Item& item) {
30092884 auto token = evictToNvmCache ? nvmCache_->createPutToken (item.getKey ())
30102885 : typename NvmCacheT::PutToken{};
30112886
2887+ if (skipIfTokenInvalid && evictToNvmCache && !token.isValid ()) {
2888+ stats_.evictFailConcurrentFill .inc ();
2889+ return ItemHandle{};
2890+ }
2891+
30122892 // We remove the item from both access and mm containers. It doesn't matter
30132893 // if someone else calls remove on the item at this moment, the item cannot
30142894 // be freed as long as we have the moving bit set.
0 commit comments