@@ -1216,14 +1216,13 @@ bool CacheAllocator<CacheTrait>::addWaitContextForMovingItem(
12161216}
12171217
12181218template <typename CacheTrait>
1219- template <typename ItemPtr>
12201219typename CacheAllocator<CacheTrait>::WriteHandle
12211220CacheAllocator<CacheTrait>::moveRegularItemOnEviction(
1222- ItemPtr& oldItemPtr, WriteHandle& newItemHdl) {
1221+ Item& oldItem, WriteHandle& newItemHdl) {
1222+ XDCHECK (oldItem.isMoving ());
12231223 // TODO: should we introduce new latency tracker. E.g. evictRegularLatency_
12241224 // ??? util::LatencyTracker tracker{stats_.evictRegularLatency_};
12251225
1226- Item& oldItem = *oldItemPtr;
12271226 if (!oldItem.isAccessible () || oldItem.isExpired ()) {
12281227 return {};
12291228 }
@@ -1279,7 +1278,7 @@ CacheAllocator<CacheTrait>::moveRegularItemOnEviction(
12791278 // it is unsafe to replace the old item with a new one, so we should
12801279 // also abort.
12811280 if (!accessContainer_->replaceIf (oldItem, *newItemHdl,
1282- itemEvictionPredicate )) {
1281+ itemMovingPredicate )) {
12831282 return {};
12841283 }
12851284
@@ -1299,7 +1298,7 @@ CacheAllocator<CacheTrait>::moveRegularItemOnEviction(
12991298
13001299 // Inside the MM container's lock, this checks if the old item exists to
13011300 // make sure that no other thread removed it, and only then replaces it.
1302- if (!replaceInMMContainer (oldItemPtr , *newItemHdl)) {
1301+ if (!replaceInMMContainer (oldItem , *newItemHdl)) {
13031302 accessContainer_->remove (*newItemHdl);
13041303 return {};
13051304 }
@@ -1490,42 +1489,52 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
14901489 ++searchTries;
14911490 (*stats_.evictionAttempts )[pid][cid].inc ();
14921491
1493- Item* candidate = itr.get ();
1492+ Item* toRecycle = itr.get ();
1493+
1494+ Item* candidate =
1495+ toRecycle->isChainedItem ()
1496+ ? &toRecycle->asChainedItem ().getParentItem (compressor_)
1497+ : toRecycle;
1498+
1499+ // make sure no other thead is evicting the item
1500+ if (candidate->getRefCount () != 0 || !candidate->markMoving ()) {
1501+ ++itr;
1502+ continue ;
1503+ }
1504+
1505+ itr.destroy ();
1506+
14941507 // for chained items, the ownership of the parent can change. We try to
14951508 // evict what we think as parent and see if the eviction of parent
14961509 // recycles the child we intend to.
1497-
1498- ItemHandle toReleaseHandle = tryEvictToNextMemoryTier (tid, pid, itr);
1499- bool movedToNextTier = false ;
1500- if (toReleaseHandle) {
1501- movedToNextTier = true ;
1502- } else {
1503- toReleaseHandle =
1504- itr->isChainedItem ()
1505- ? advanceIteratorAndTryEvictChainedItem (tid, pid, itr)
1506- : advanceIteratorAndTryEvictRegularItem (tid, pid, mmContainer, itr);
1507- }
1510+ auto toReleaseHandle =
1511+ evictNormalItem (*candidate, true /* skipIfTokenInvalid */ );
1512+ auto ref = candidate->unmarkMoving ();
15081513
1509- if (toReleaseHandle) {
1510- if (toReleaseHandle ->hasChainedItem ()) {
1514+ if (toReleaseHandle || ref == 0u ) {
1515+ if (candidate ->hasChainedItem ()) {
15111516 (*stats_.chainedItemEvictions )[pid][cid].inc ();
15121517 } else {
15131518 (*stats_.regularItemEvictions )[pid][cid].inc ();
15141519 }
1520+ } else {
1521+ if (candidate->hasChainedItem ()) {
1522+ stats_.evictFailParentAC .inc ();
1523+ } else {
1524+ stats_.evictFailAC .inc ();
1525+ }
1526+ }
15151527
1528+ if (toReleaseHandle) {
15161529 if (auto eventTracker = getEventTracker ()) {
15171530 eventTracker->record (
15181531 AllocatorApiEvent::DRAM_EVICT, toReleaseHandle->getKey (),
15191532 AllocatorApiResult::EVICTED, toReleaseHandle->getSize (),
15201533 toReleaseHandle->getConfiguredTTL ().count ());
15211534 }
1522- // Invalidate iterator since later on we may use this mmContainer
1523- // again, which cannot be done unless we drop this iterator
1524- itr.destroy ();
15251535
1526- // we must be the last handle and for chained items, this will be
1527- // the parent.
1528- XDCHECK (toReleaseHandle.get () == candidate || candidate->isChainedItem ());
1536+ XDCHECK (toReleaseHandle.get () == candidate);
1537+ XDCHECK (toRecycle == candidate || toRecycle->isChainedItem ());
15291538 XDCHECK_EQ (1u , toReleaseHandle->getRefCount ());
15301539
15311540 // We manually release the item here because we don't want to
@@ -1541,16 +1550,21 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
15411550 // recycle the candidate.
15421551 if (ReleaseRes::kRecycled ==
15431552 releaseBackToAllocator (itemToRelease, RemoveContext::kEviction ,
1544- /* isNascent */ movedToNextTier, candidate)) {
1545- return candidate;
1553+ /* isNascent */ false , toRecycle)) {
1554+ return toRecycle;
1555+ }
1556+ } else if (ref == 0u ) {
1557+ // it's safe to recycle the item here as there are no more
1558+ // references and the item could not been marked as moving
1559+ // by other thread since it's detached from MMContainer.
1560+ if (ReleaseRes::kRecycled ==
1561+ releaseBackToAllocator (*candidate, RemoveContext::kEviction ,
1562+ /* isNascent */ false , toRecycle)) {
1563+ return toRecycle;
15461564 }
15471565 }
15481566
1549- // If we destroyed the itr to possibly evict and failed, we restart
1550- // from the beginning again
1551- if (!itr) {
1552- itr.resetToBegin ();
1553- }
1567+ itr.resetToBegin ();
15541568 }
15551569 return nullptr ;
15561570}
@@ -1604,24 +1618,23 @@ bool CacheAllocator<CacheTrait>::shouldWriteToNvmCacheExclusive(
16041618}
16051619
16061620template <typename CacheTrait>
1607- template <typename ItemPtr>
16081621typename CacheAllocator<CacheTrait>::WriteHandle
16091622CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
1610- TierId tid, PoolId pid, ItemPtr & item) {
1611- if (item-> isChainedItem ()) return {}; // TODO: We do not support ChainedItem yet
1612- if (item-> isExpired ()) return acquire (item);
1623+ TierId tid, PoolId pid, Item & item) {
1624+ if (item. isChainedItem ()) return {}; // TODO: We do not support ChainedItem yet
1625+ if (item. isExpired ()) return acquire (& item);
16131626
16141627 TierId nextTier = tid; // TODO - calculate this based on some admission policy
16151628 while (++nextTier < getNumTiers ()) { // try to evict down to the next memory tiers
16161629 // allocateInternal might trigger another eviction
16171630 auto newItemHdl = allocateInternalTier (nextTier, pid,
1618- item-> getKey (),
1619- item-> getSize (),
1620- item-> getCreationTime (),
1621- item-> getExpiryTime ());
1631+ item. getKey (),
1632+ item. getSize (),
1633+ item. getCreationTime (),
1634+ item. getExpiryTime ());
16221635
16231636 if (newItemHdl) {
1624- XDCHECK_EQ (newItemHdl->getSize (), item-> getSize ());
1637+ XDCHECK_EQ (newItemHdl->getSize (), item. getSize ());
16251638
16261639 return moveRegularItemOnEviction (item, newItemHdl);
16271640 }
@@ -1632,150 +1645,12 @@ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
16321645
16331646template <typename CacheTrait>
16341647typename CacheAllocator<CacheTrait>::WriteHandle
1635- CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(Item* item) {
1636- auto tid = getTierId (* item);
1637- auto pid = allocator_[tid]->getAllocInfo (item-> getMemory ()).poolId ;
1648+ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(Item& item) {
1649+ auto tid = getTierId (item);
1650+ auto pid = allocator_[tid]->getAllocInfo (item. getMemory ()).poolId ;
16381651 return tryEvictToNextMemoryTier (tid, pid, item);
16391652}
16401653
1641- template <typename CacheTrait>
1642- typename CacheAllocator<CacheTrait>::WriteHandle
1643- CacheAllocator<CacheTrait>::advanceIteratorAndTryEvictRegularItem(
1644- TierId tid, PoolId pid, MMContainer& mmContainer, EvictionIterator& itr) {
1645- Item& item = *itr;
1646-
1647- const bool evictToNvmCache = shouldWriteToNvmCache (item);
1648-
1649- auto token = evictToNvmCache ? nvmCache_->createPutToken (item.getKey ())
1650- : typename NvmCacheT::PutToken{};
1651- // record the in-flight eviciton. If not, we move on to next item to avoid
1652- // stalling eviction.
1653- if (evictToNvmCache && !token.isValid ()) {
1654- ++itr;
1655- stats_.evictFailConcurrentFill .inc ();
1656- return WriteHandle{};
1657- }
1658-
1659- // If there are other accessors, we should abort. Acquire a handle here since
1660- // if we remove the item from both access containers and mm containers
1661- // below, we will need a handle to ensure proper cleanup in case we end up
1662- // not evicting this item
1663- auto evictHandle = accessContainer_->removeIf (item, &itemEvictionPredicate);
1664-
1665- if (!evictHandle) {
1666- ++itr;
1667- stats_.evictFailAC .inc ();
1668- return evictHandle;
1669- }
1670-
1671- mmContainer.remove (itr);
1672- XDCHECK_EQ (reinterpret_cast <uintptr_t >(evictHandle.get ()),
1673- reinterpret_cast <uintptr_t >(&item));
1674- XDCHECK (!evictHandle->isInMMContainer ());
1675- XDCHECK (!evictHandle->isAccessible ());
1676-
1677- // If the item is now marked as moving, that means its corresponding slab is
1678- // being released right now. So, we look for the next item that is eligible
1679- // for eviction. It is safe to destroy the handle here since the moving bit
1680- // is set. Iterator was already advance by the remove call above.
1681- if (evictHandle->isMoving ()) {
1682- stats_.evictFailMove .inc ();
1683- return WriteHandle{};
1684- }
1685-
1686- // Invalidate iterator since later on if we are not evicting this
1687- // item, we may need to rely on the handle we created above to ensure
1688- // proper cleanup if the item's raw refcount has dropped to 0.
1689- // And since this item may be a parent item that has some child items
1690- // in this very same mmContainer, we need to make sure we drop this
1691- // exclusive iterator so we can gain access to it when we're cleaning
1692- // up the child items
1693- itr.destroy ();
1694-
1695- // Ensure that there are no accessors after removing from the access
1696- // container
1697- XDCHECK (evictHandle->getRefCount () == 1 );
1698-
1699- if (evictToNvmCache && shouldWriteToNvmCacheExclusive (item)) {
1700- XDCHECK (token.isValid ());
1701- nvmCache_->put (evictHandle, std::move (token));
1702- }
1703- return evictHandle;
1704- }
1705-
1706- template <typename CacheTrait>
1707- typename CacheAllocator<CacheTrait>::WriteHandle
1708- CacheAllocator<CacheTrait>::advanceIteratorAndTryEvictChainedItem(
1709- TierId tid, PoolId pid, EvictionIterator& itr) {
1710- XDCHECK (itr->isChainedItem ());
1711-
1712- ChainedItem* candidate = &itr->asChainedItem ();
1713- ++itr;
1714-
1715- // The parent could change at any point through transferChain. However, if
1716- // that happens, we would realize that the releaseBackToAllocator return
1717- // kNotRecycled and we would try another chained item, leading to transient
1718- // failure.
1719- auto & parent = candidate->getParentItem (compressor_);
1720-
1721- const bool evictToNvmCache = shouldWriteToNvmCache (parent);
1722-
1723- auto token = evictToNvmCache ? nvmCache_->createPutToken (parent.getKey ())
1724- : typename NvmCacheT::PutToken{};
1725-
1726- // if token is invalid, return. iterator is already advanced.
1727- if (evictToNvmCache && !token.isValid ()) {
1728- stats_.evictFailConcurrentFill .inc ();
1729- return WriteHandle{};
1730- }
1731-
1732- // check if the parent exists in the hashtable and refcount is drained.
1733- auto parentHandle =
1734- accessContainer_->removeIf (parent, &itemEvictionPredicate);
1735- if (!parentHandle) {
1736- stats_.evictFailParentAC .inc ();
1737- return parentHandle;
1738- }
1739-
1740- // Invalidate iterator since later on we may use the mmContainer
1741- // associated with this iterator which cannot be done unless we
1742- // drop this iterator
1743- //
1744- // This must be done once we know the parent is not nullptr.
1745- // Since we can very well be the last holder of this parent item,
1746- // which may have a chained item that is linked in this MM container.
1747- itr.destroy ();
1748-
1749- // Ensure we have the correct parent and we're the only user of the
1750- // parent, then free it from access container. Otherwise, we abort
1751- XDCHECK_EQ (reinterpret_cast <uintptr_t >(&parent),
1752- reinterpret_cast <uintptr_t >(parentHandle.get ()));
1753- XDCHECK_EQ (1u , parent.getRefCount ());
1754-
1755- removeFromMMContainer (*parentHandle);
1756-
1757- XDCHECK (!parent.isInMMContainer ());
1758- XDCHECK (!parent.isAccessible ());
1759-
1760- // TODO: add multi-tier support (similar as for unchained items)
1761-
1762- // We need to make sure the parent is not marked as moving
1763- // and we're the only holder of the parent item. Safe to destroy the handle
1764- // here since moving bit is set.
1765- if (parentHandle->isMoving ()) {
1766- stats_.evictFailParentMove .inc ();
1767- return WriteHandle{};
1768- }
1769-
1770- if (evictToNvmCache && shouldWriteToNvmCacheExclusive (*parentHandle)) {
1771- XDCHECK (token.isValid ());
1772- XDCHECK (parentHandle->hasChainedItem ());
1773- nvmCache_->put (parentHandle, std::move (token));
1774- }
1775-
1776- return parentHandle;
1777- }
1778-
17791654template <typename CacheTrait>
17801655typename CacheAllocator<CacheTrait>::RemoveRes
17811656CacheAllocator<CacheTrait>::remove(typename Item::Key key) {
@@ -3043,7 +2918,7 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
30432918 auto owningHandle =
30442919 item.isChainedItem ()
30452920 ? evictChainedItemForSlabRelease (item.asChainedItem ())
3046- : evictNormalItemForSlabRelease (item);
2921+ : evictNormalItem (item);
30472922
30482923 // we managed to evict the corresponding owner of the item and have the
30492924 // last handle for the owner.
@@ -3100,14 +2975,15 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
31002975
31012976template <typename CacheTrait>
31022977typename CacheAllocator<CacheTrait>::WriteHandle
3103- CacheAllocator<CacheTrait>::evictNormalItemForSlabRelease(Item& item) {
2978+ CacheAllocator<CacheTrait>::evictNormalItem(Item& item,
2979+ bool skipIfTokenInvalid) {
31042980 XDCHECK (item.isMoving ());
31052981
31062982 if (item.isOnlyMoving ()) {
31072983 return WriteHandle{};
31082984 }
31092985
3110- auto evictHandle = tryEvictToNextMemoryTier (& item);
2986+ auto evictHandle = tryEvictToNextMemoryTier (item);
31112987 if (evictHandle) return evictHandle;
31122988
31132989 auto predicate = [](const Item& it) { return it.getRefCount () == 0 ; };
@@ -3116,6 +2992,11 @@ CacheAllocator<CacheTrait>::evictNormalItemForSlabRelease(Item& item) {
31162992 auto token = evictToNvmCache ? nvmCache_->createPutToken (item.getKey ())
31172993 : typename NvmCacheT::PutToken{};
31182994
2995+ if (skipIfTokenInvalid && evictToNvmCache && !token.isValid ()) {
2996+ stats_.evictFailConcurrentFill .inc ();
2997+ return WriteHandle{};
2998+ }
2999+
31193000 // We remove the item from both access and mm containers. It doesn't matter
31203001 // if someone else calls remove on the item at this moment, the item cannot
31213002 // be freed as long as we have the moving bit set.
0 commit comments