@@ -1221,55 +1221,44 @@ CacheAllocator<CacheTrait>::findEviction(PoolId pid, ClassId cid) {
12211221 // Keep searching for a candidate until we were able to evict it
12221222 // or until the search limit has been exhausted
12231223 unsigned int searchTries = 0 ;
1224+ auto itr = mmContainer.getEvictionIterator ();
12241225 while ((config_.evictionSearchTries == 0 ||
1225- config_.evictionSearchTries > searchTries)) {
1226+ config_.evictionSearchTries > searchTries) &&
1227+ itr) {
12261228 ++searchTries;
12271229 (*stats_.evictionAttempts )[pid][cid].inc ();
12281230
1229- Item* toRecycle = nullptr ;
1230- Item* candidate = nullptr ;
1231-
1232- mmContainer.withEvictionIterator (
1233- [this , &candidate, &toRecycle, &searchTries](auto && itr) {
1234- while ((config_.evictionSearchTries == 0 ||
1235- config_.evictionSearchTries > searchTries) &&
1236- itr) {
1237- ++searchTries;
1238-
1239- auto * toRecycle_ = itr.get ();
1240- auto * candidate_ =
1241- toRecycle_->isChainedItem ()
1242- ? &toRecycle_->asChainedItem ().getParentItem (compressor_)
1243- : toRecycle_;
1244-
1245- // make sure no other thead is evicting the item
1246- if (candidate_->getRefCount () == 0 && candidate_->markExclusive ()) {
1247- toRecycle = toRecycle_;
1248- candidate = candidate_;
1249- return ;
1250- }
1251-
1252- ++itr;
1253- }
1254- });
1255-
1256- if (!toRecycle)
1257- continue ;
1231+ Item* toRecycle = itr.get ();
1232+
1233+ Item* candidate =
1234+ toRecycle->isChainedItem ()
1235+ ? &toRecycle->asChainedItem ().getParentItem (compressor_)
1236+ : toRecycle;
12581237
1259- XDCHECK (toRecycle);
1260- XDCHECK (candidate);
1238+ // make sure no other thead is evicting the item
1239+ if (candidate->getRefCount () != 0 || !candidate->markExclusive ()) {
1240+ ++itr;
1241+ continue ;
1242+ }
12611243
12621244 // for chained items, the ownership of the parent can change. We try to
12631245 // evict what we think as parent and see if the eviction of parent
12641246 // recycles the child we intend to.
12651247 {
1266- auto toReleaseHandle = evictNormalItem (*candidate);
1248+ auto toReleaseHandle =
1249+ itr->isChainedItem ()
1250+ ? advanceIteratorAndTryEvictChainedItem (itr)
1251+ : advanceIteratorAndTryEvictRegularItem (mmContainer, itr);
12671252 // destroy toReleseHandle. The item won't be release to allocator
12681253 // since we marked it as exclusive.
12691254 }
1270- const auto ref = candidate->unmarkExclusive ();
12711255
1256+ const auto ref = candidate->unmarkExclusive ();
12721257 if (ref == 0u ) {
1258+ // Invalidate iterator since later on we may use this mmContainer
1259+ // again, which cannot be done unless we drop this iterator
1260+ itr.destroy ();
1261+
12731262 // recycle the item. it's safe to do so, even if toReleaseHandle was
12741263 // NULL. If `ref` == 0 then it means that we are the last holder of
12751264 // that item.
@@ -1299,6 +1288,8 @@ CacheAllocator<CacheTrait>::findEviction(PoolId pid, ClassId cid) {
12991288 stats_.evictFailAC .inc ();
13001289 }
13011290 }
1291+
1292+ itr.resetToBegin ();
13021293 }
13031294 return nullptr ;
13041295}
@@ -2589,7 +2580,7 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
25892580 auto owningHandle =
25902581 item.isChainedItem ()
25912582 ? evictChainedItemForSlabRelease (item.asChainedItem ())
2592- : evictNormalItem (item);
2583+ : evictNormalItemForSlabRelease (item);
25932584
25942585 // we managed to evict the corresponding owner of the item and have the
25952586 // last handle for the owner.
@@ -2644,7 +2635,128 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
26442635
26452636template <typename CacheTrait>
26462637typename CacheAllocator<CacheTrait>::WriteHandle
2647- CacheAllocator<CacheTrait>::evictNormalItem(Item& item) {
2638+ CacheAllocator<CacheTrait>::advanceIteratorAndTryEvictRegularItem(
2639+ MMContainer& mmContainer, EvictionIterator& itr) {
2640+ // we should flush this to nvmcache if it is not already present in nvmcache
2641+ // and the item is not expired.
2642+ Item& item = *itr;
2643+ const bool evictToNvmCache = shouldWriteToNvmCache (item);
2644+
2645+ auto token = evictToNvmCache ? nvmCache_->createPutToken (item.getKey ())
2646+ : typename NvmCacheT::PutToken{};
2647+
2648+ // record the in-flight eviciton. If not, we move on to next item to avoid
2649+ // stalling eviction.
2650+ if (evictToNvmCache && !token.isValid ()) {
2651+ ++itr;
2652+ stats_.evictFailConcurrentFill .inc ();
2653+ return WriteHandle{};
2654+ }
2655+
2656+ // If there are other accessors, we should abort. Acquire a handle here since
2657+ // if we remove the item from both access containers and mm containers
2658+ // below, we will need a handle to ensure proper cleanup in case we end up
2659+ // not evicting this item
2660+ auto evictHandle = accessContainer_->removeIf (item, &itemExclusivePredicate);
2661+ if (!evictHandle) {
2662+ ++itr;
2663+ stats_.evictFailAC .inc ();
2664+ return evictHandle;
2665+ }
2666+
2667+ mmContainer.remove (itr);
2668+ XDCHECK_EQ (reinterpret_cast <uintptr_t >(evictHandle.get ()),
2669+ reinterpret_cast <uintptr_t >(&item));
2670+ XDCHECK (!evictHandle->isInMMContainer ());
2671+ XDCHECK (!evictHandle->isAccessible ());
2672+
2673+ // Invalidate iterator since later on if we are not evicting this
2674+ // item, we may need to rely on the handle we created above to ensure
2675+ // proper cleanup if the item's raw refcount has dropped to 0.
2676+ // And since this item may be a parent item that has some child items
2677+ // in this very same mmContainer, we need to make sure we drop this
2678+ // exclusive iterator so we can gain access to it when we're cleaning
2679+ // up the child items
2680+ itr.destroy ();
2681+
2682+ // Ensure that there are no accessors after removing from the access
2683+ // container
2684+ XDCHECK (evictHandle->getRefCount () == 1 );
2685+
2686+ if (evictToNvmCache && shouldWriteToNvmCacheExclusive (item)) {
2687+ XDCHECK (token.isValid ());
2688+ nvmCache_->put (evictHandle, std::move (token));
2689+ }
2690+ return evictHandle;
2691+ }
2692+
2693+ template <typename CacheTrait>
2694+ typename CacheAllocator<CacheTrait>::WriteHandle
2695+ CacheAllocator<CacheTrait>::advanceIteratorAndTryEvictChainedItem(
2696+ EvictionIterator& itr) {
2697+ XDCHECK (itr->isChainedItem ());
2698+
2699+ ChainedItem* candidate = &itr->asChainedItem ();
2700+ ++itr;
2701+
2702+ // The parent could change at any point through transferChain. However, if
2703+ // that happens, we would realize that the releaseBackToAllocator return
2704+ // kNotRecycled and we would try another chained item, leading to transient
2705+ // failure.
2706+ auto & parent = candidate->getParentItem (compressor_);
2707+
2708+ const bool evictToNvmCache = shouldWriteToNvmCache (parent);
2709+
2710+ auto token = evictToNvmCache ? nvmCache_->createPutToken (parent.getKey ())
2711+ : typename NvmCacheT::PutToken{};
2712+
2713+ // if token is invalid, return. iterator is already advanced.
2714+ if (evictToNvmCache && !token.isValid ()) {
2715+ ++itr;
2716+ stats_.evictFailConcurrentFill .inc ();
2717+ return WriteHandle{};
2718+ }
2719+
2720+ // check if the parent exists in the hashtable and refcount is drained.
2721+ auto parentHandle =
2722+ accessContainer_->removeIf (parent, &itemExclusivePredicate);
2723+ if (!parentHandle) {
2724+ stats_.evictFailParentAC .inc ();
2725+ return parentHandle;
2726+ }
2727+
2728+ // Invalidate iterator since later on we may use the mmContainer
2729+ // associated with this iterator which cannot be done unless we
2730+ // drop this iterator
2731+ //
2732+ // This must be done once we know the parent is not nullptr.
2733+ // Since we can very well be the last holder of this parent item,
2734+ // which may have a chained item that is linked in this MM container.
2735+ itr.destroy ();
2736+
2737+ // Ensure we have the correct parent and we're the only user of the
2738+ // parent, then free it from access container. Otherwise, we abort
2739+ XDCHECK_EQ (reinterpret_cast <uintptr_t >(&parent),
2740+ reinterpret_cast <uintptr_t >(parentHandle.get ()));
2741+ XDCHECK_EQ (1u , parent.getRefCount ());
2742+
2743+ removeFromMMContainer (*parentHandle);
2744+
2745+ XDCHECK (!parent.isInMMContainer ());
2746+ XDCHECK (!parent.isAccessible ());
2747+
2748+ if (evictToNvmCache && shouldWriteToNvmCacheExclusive (*parentHandle)) {
2749+ XDCHECK (token.isValid ());
2750+ XDCHECK (parentHandle->hasChainedItem ());
2751+ nvmCache_->put (parentHandle, std::move (token));
2752+ }
2753+
2754+ return parentHandle;
2755+ }
2756+
2757+ template <typename CacheTrait>
2758+ typename CacheAllocator<CacheTrait>::WriteHandle
2759+ CacheAllocator<CacheTrait>::evictNormalItemForSlabRelease(Item& item) {
26482760 XDCHECK (item.isExclusive ());
26492761
26502762 if (item.isOnlyExclusive ()) {
0 commit comments