@@ -1221,55 +1221,44 @@ CacheAllocator<CacheTrait>::findEviction(PoolId pid, ClassId cid) {
1221
1221
// Keep searching for a candidate until we were able to evict it
1222
1222
// or until the search limit has been exhausted
1223
1223
unsigned int searchTries = 0 ;
1224
+ auto itr = mmContainer.getEvictionIterator ();
1224
1225
while ((config_.evictionSearchTries == 0 ||
1225
- config_.evictionSearchTries > searchTries)) {
1226
+ config_.evictionSearchTries > searchTries) &&
1227
+ itr) {
1226
1228
++searchTries;
1227
1229
(*stats_.evictionAttempts )[pid][cid].inc ();
1228
1230
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;
1258
1237
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
+ }
1261
1243
1262
1244
// for chained items, the ownership of the parent can change. We try to
1263
1245
// evict what we think as parent and see if the eviction of parent
1264
1246
// recycles the child we intend to.
1265
1247
{
1266
- auto toReleaseHandle = evictNormalItem (*candidate);
1248
+ auto toReleaseHandle =
1249
+ itr->isChainedItem ()
1250
+ ? advanceIteratorAndTryEvictChainedItem (itr)
1251
+ : advanceIteratorAndTryEvictRegularItem (mmContainer, itr);
1267
1252
// destroy toReleseHandle. The item won't be release to allocator
1268
1253
// since we marked it as exclusive.
1269
1254
}
1270
- const auto ref = candidate->unmarkExclusive ();
1271
1255
1256
+ const auto ref = candidate->unmarkExclusive ();
1272
1257
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
+
1273
1262
// recycle the item. it's safe to do so, even if toReleaseHandle was
1274
1263
// NULL. If `ref` == 0 then it means that we are the last holder of
1275
1264
// that item.
@@ -1299,6 +1288,8 @@ CacheAllocator<CacheTrait>::findEviction(PoolId pid, ClassId cid) {
1299
1288
stats_.evictFailAC .inc ();
1300
1289
}
1301
1290
}
1291
+
1292
+ itr.resetToBegin ();
1302
1293
}
1303
1294
return nullptr ;
1304
1295
}
@@ -2589,7 +2580,7 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
2589
2580
auto owningHandle =
2590
2581
item.isChainedItem ()
2591
2582
? evictChainedItemForSlabRelease (item.asChainedItem ())
2592
- : evictNormalItem (item);
2583
+ : evictNormalItemForSlabRelease (item);
2593
2584
2594
2585
// we managed to evict the corresponding owner of the item and have the
2595
2586
// last handle for the owner.
@@ -2644,7 +2635,128 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
2644
2635
2645
2636
template <typename CacheTrait>
2646
2637
typename 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) {
2648
2760
XDCHECK (item.isExclusive ());
2649
2761
2650
2762
if (item.isOnlyExclusive ()) {
@@ -2841,7 +2953,6 @@ bool CacheAllocator<CacheTrait>::markExclusiveForSlabRelease(
2841
2953
itemFreed = true ;
2842
2954
2843
2955
if (shutDownInProgress_) {
2844
- XDCHECK (!static_cast <Item*>(alloc)->isExclusive ());
2845
2956
allocator_->abortSlabRelease (ctx);
2846
2957
throw exception::SlabReleaseAborted (
2847
2958
folly::sformat (" Slab Release aborted while still trying to mark"
0 commit comments