@@ -1389,7 +1389,7 @@ class CacheAllocator : public CacheBase {
13891389 double slabsApproxFreePercentage (TierId tid) const ;
13901390
13911391 // wrapper around Item's refcount and active handle tracking
1392- FOLLY_ALWAYS_INLINE void incRef (Item& it);
1392+ FOLLY_ALWAYS_INLINE RefcountWithFlags::incResult incRef (Item& it, bool failIfMoving );
13931393 FOLLY_ALWAYS_INLINE RefcountWithFlags::Value decRef (Item& it);
13941394
13951395 // drops the refcount and if needed, frees the allocation back to the memory
@@ -1440,6 +1440,12 @@ class CacheAllocator : public CacheBase {
14401440 bool nascent = false ,
14411441 const Item* toRecycle = nullptr );
14421442
1443+ // Must be called by the thread which called markExclusive and
1444+ // succeeded. After this call, the item is unlinked from Access and
1445+ // MM Containers. The item is no longer marked as exclusive and it's
1446+ // ref count is 0 - it's available for recycling.
1447+ void unlinkItemExclusive (Item& it);
1448+
14431449 // acquires an handle on the item. returns an empty handle if it is null.
14441450 // @param it pointer to an item
14451451 // @return WriteHandle return a handle to this item
@@ -1550,17 +1556,17 @@ class CacheAllocator : public CacheBase {
15501556 // @return handle to the parent item if the validations pass
15511557 // otherwise, an empty Handle is returned.
15521558 //
1553- ReadHandle validateAndGetParentHandleForChainedMoveLocked (
1559+ WriteHandle validateAndGetParentHandleForChainedMoveLocked (
15541560 const ChainedItem& item, const Key& parentKey);
15551561
15561562 // Given an existing item, allocate a new one for the
15571563 // existing one to later be moved into.
15581564 //
1559- // @param oldItem the item we want to allocate a new item for
1565+ // @param item reference to the item we want to allocate a new item for
15601566 //
15611567 // @return handle to the newly allocated item
15621568 //
1563- WriteHandle allocateNewItemForOldItem (const Item& oldItem );
1569+ WriteHandle allocateNewItemForOldItem (const Item& item );
15641570
15651571 // internal helper that grabs a refcounted handle to the item. This does
15661572 // not record the access to reflect in the mmContainer.
@@ -1614,18 +1620,16 @@ class CacheAllocator : public CacheBase {
16141620 // @param oldItem Reference to the item being moved
16151621 // @param newItemHdl Reference to the handle of the new item being moved into
16161622 //
1617- // @return the handle to the oldItem if the move was completed
1618- // and the oldItem can be recycled.
1619- // Otherwise an empty handle is returned.
1620- template <typename P>
1621- WriteHandle moveRegularItemWithSync (Item& oldItem, WriteHandle& newItemHdl, P&& predicate);
1623+ // @return true If the move was completed, and the containers were updated
1624+ // successfully.
1625+ void moveRegularItemWithSync (Item& oldItem, WriteHandle& newItemHdl);
16221626
16231627 // Moves a regular item to a different slab. This should only be used during
16241628 // slab release after the item's exclusive bit has been set. The user supplied
16251629 // callback is responsible for copying the contents and fixing the semantics
16261630 // of chained item.
16271631 //
1628- // @param oldItem Reference to the item being moved
1632+ // @param oldItem item being moved
16291633 // @param newItemHdl Reference to the handle of the new item being moved into
16301634 //
16311635 // @return true If the move was completed, and the containers were updated
@@ -1789,9 +1793,9 @@ class CacheAllocator : public CacheBase {
17891793 // handle to the item. On failure an empty handle.
17901794 WriteHandle tryEvictToNextMemoryTier (TierId tid, PoolId pid, Item& item, bool fromBgThread);
17911795
1792- bool tryPromoteToNextMemoryTier (TierId tid, PoolId pid, Item& item, bool fromBgThread);
1796+ WriteHandle tryPromoteToNextMemoryTier (TierId tid, PoolId pid, Item& item, bool fromBgThread);
17931797
1794- bool tryPromoteToNextMemoryTier (Item& item, bool fromBgThread);
1798+ WriteHandle tryPromoteToNextMemoryTier (Item& item, bool fromBgThread);
17951799
17961800 // Try to move the item down to the next memory tier
17971801 //
@@ -1878,22 +1882,23 @@ class CacheAllocator : public CacheBase {
18781882
18791883 // @return true when successfully marked as moving,
18801884 // fasle when this item has already been freed
1881- bool markExclusiveForSlabRelease (const SlabReleaseContext& ctx,
1882- void * alloc,
1883- util::Throttler& throttler);
1885+ bool markMovingForSlabRelease (const SlabReleaseContext& ctx,
1886+ void * alloc,
1887+ util::Throttler& throttler);
18841888
18851889 // "Move" (by copying) the content in this item to another memory
18861890 // location by invoking the move callback.
18871891 //
18881892 //
18891893 // @param ctx slab release context
1890- // @param item old item to be moved elsewhere
1894+ // @param oldItem old item to be moved elsewhere
1895+ // @param handle handle to the item or to it's parent (if chained)
18911896 // @param throttler slow this function down as not to take too much cpu
18921897 //
18931898 // @return true if the item has been moved
18941899 // false if we have exhausted moving attempts
18951900 bool moveForSlabRelease (const SlabReleaseContext& ctx,
1896- Item& item ,
1901+ Item& oldItem ,
18971902 util::Throttler& throttler);
18981903
18991904 // "Move" (by copying) the content in this item to another memory
@@ -1929,6 +1934,8 @@ class CacheAllocator : public CacheBase {
19291934 // handle on failure. caller can retry.
19301935 WriteHandle evictChainedItemForSlabRelease (ChainedItem& item);
19311936
1937+ typename NvmCacheT::PutToken createPutToken (Item& item);
1938+
19321939 // Helper function to remove a item if predicates is true.
19331940 //
19341941 // @return last handle to the item on success. empty handle on failure.
@@ -1966,8 +1973,10 @@ class CacheAllocator : public CacheBase {
19661973 candidates.reserve (batch);
19671974
19681975 size_t tries = 0 ;
1969- mmContainer.withEvictionIterator ([&tries, &candidates, &batch, this ](auto &&itr){
1970- while (candidates.size () < batch && (config_.maxEvictionPromotionHotness == 0 || tries < config_.maxEvictionPromotionHotness ) && itr) {
1976+ mmContainer.withEvictionIterator ([&tries, &candidates, &batch, &mmContainer, this ](auto &&itr) {
1977+ while (candidates.size () < batch &&
1978+ (config_.maxEvictionPromotionHotness == 0 || tries < config_.maxEvictionPromotionHotness ) &&
1979+ itr) {
19711980 tries++;
19721981 Item* candidate = itr.get ();
19731982 XDCHECK (candidate);
@@ -1976,7 +1985,8 @@ class CacheAllocator : public CacheBase {
19761985 throw std::runtime_error (" Not supported for chained items" );
19771986 }
19781987
1979- if (candidate->getRefCount () == 0 && candidate->markExclusive ()) {
1988+ if (candidate->markMoving (true )) {
1989+ mmContainer.remove (itr);
19801990 candidates.push_back (candidate);
19811991 }
19821992
@@ -1985,37 +1995,45 @@ class CacheAllocator : public CacheBase {
19851995 });
19861996
19871997 for (Item *candidate : candidates) {
1988- {
1989- auto toReleaseHandle =
1990- evictNormalItem (*candidate,
1991- true /* skipIfTokenInvalid */ , true /* from BG thread */ );
1992- // destroy toReleseHandle. The item won't be release to allocator
1993- // since we marked it as exclusive.
1994- }
1995- auto ref = candidate->unmarkExclusive ();
1996-
1997- if (ref == 0u ) {
1998- if (candidate->hasChainedItem ()) {
1999- (*stats_.chainedItemEvictions )[pid][cid].inc ();
2000- } else {
2001- (*stats_.regularItemEvictions )[pid][cid].inc ();
2002- }
1998+ auto evictedToNext = tryEvictToNextMemoryTier (*candidate, true /* from BgThread */ );
1999+ if (!evictedToNext) {
2000+ auto token = createPutToken (*candidate);
2001+
2002+ auto ret = candidate->markExclusiveWhenMoving ();
2003+ XDCHECK (ret);
2004+
2005+ unlinkItemExclusive (*candidate);
2006+ // wake up any readers that wait for the move to complete
2007+ // it's safe to do now, as we have the item marked exclusive and
2008+ // no other reader can be added to the waiters list
2009+ wakeUpWaiters (candidate->getKey (), WriteHandle{});
2010+
2011+ if (token.isValid () && shouldWriteToNvmCacheExclusive (*candidate)) {
2012+ nvmCache_->put (*candidate, std::move (token));
2013+ }
2014+ } else {
2015+ evictions++;
2016+ XDCHECK (!evictedToNext->isExclusive () && !evictedToNext->isMoving ());
2017+ XDCHECK (!candidate->isExclusive () && !candidate->isMoving ());
2018+ XDCHECK (!candidate->isAccessible ());
2019+ XDCHECK (candidate->getKey () == evictedToNext->getKey ());
20032020
2004- evictions++;
2005- // it's safe to recycle the item here as there are no more
2006- // references and the item could not been marked as moving
2007- // by other thread since it's detached from MMContainer.
2008- auto res = releaseBackToAllocator (*candidate, RemoveContext::kEviction ,
2009- /* isNascent */ false );
2010- XDCHECK (res == ReleaseRes::kReleased );
2021+ wakeUpWaiters (candidate->getKey (), std::move (evictedToNext));
2022+ }
2023+ XDCHECK (!candidate->isExclusive () && !candidate->isMoving ());
20112024
2025+ if (candidate->hasChainedItem ()) {
2026+ (*stats_.chainedItemEvictions )[pid][cid].inc ();
20122027 } else {
2013- if (candidate->hasChainedItem ()) {
2014- stats_.evictFailParentAC .inc ();
2015- } else {
2016- stats_.evictFailAC .inc ();
2017- }
2028+ (*stats_.regularItemEvictions )[pid][cid].inc ();
20182029 }
2030+
2031+ // it's safe to recycle the item here as there are no more
2032+ // references and the item could not been marked as moving
2033+ // by other thread since it's detached from MMContainer.
2034+ auto res = releaseBackToAllocator (*candidate, RemoveContext::kEviction ,
2035+ /* isNascent */ false );
2036+ XDCHECK (res == ReleaseRes::kReleased );
20192037 }
20202038 return evictions;
20212039 }
@@ -2028,7 +2046,7 @@ class CacheAllocator : public CacheBase {
20282046
20292047 size_t tries = 0 ;
20302048
2031- mmContainer.withPromotionIterator ([&tries, &candidates, &batch, this ](auto &&itr){
2049+ mmContainer.withPromotionIterator ([&tries, &candidates, &batch, &mmContainer, this ](auto &&itr){
20322050 while (candidates.size () < batch && (config_.maxEvictionPromotionHotness == 0 || tries < config_.maxEvictionPromotionHotness ) && itr) {
20332051 tries++;
20342052 Item* candidate = itr.get ();
@@ -2038,10 +2056,9 @@ class CacheAllocator : public CacheBase {
20382056 throw std::runtime_error (" Not supported for chained items" );
20392057 }
20402058
2041-
20422059 // TODO: only allow it for read-only items?
20432060 // or implement mvcc
2044- if (! candidate->isExpired () && candidate-> markExclusive ( )) {
2061+ if (candidate->markMoving ( true )) {
20452062 candidates.push_back (candidate);
20462063 }
20472064
@@ -2051,18 +2068,28 @@ class CacheAllocator : public CacheBase {
20512068
20522069 for (Item *candidate : candidates) {
20532070 auto promoted = tryPromoteToNextMemoryTier (*candidate, true );
2054- auto ref = candidate->unmarkExclusive ();
2055- if (promoted)
2071+ if (promoted) {
20562072 promotions++;
2057-
2058- if (ref == 0u ) {
2059- // stats_.promotionMoveSuccess.inc();
2060- auto res = releaseBackToAllocator (*candidate, RemoveContext::kEviction ,
2061- /* isNascent */ false );
2062- XDCHECK (res == ReleaseRes::kReleased );
2073+ removeFromMMContainer (*candidate);
2074+ XDCHECK (!candidate->isExclusive () && !candidate->isMoving ());
2075+ // it's safe to recycle the item here as there are no more
2076+ // references and the item could not been marked as moving
2077+ // by other thread since it's detached from MMContainer.
2078+ auto res = releaseBackToAllocator (*candidate, RemoveContext::kEviction ,
2079+ /* isNascent */ false );
2080+ XDCHECK (res == ReleaseRes::kReleased );
2081+ } else {
2082+ // we failed to allocate a new item, this item is no longer moving
2083+ auto ref = candidate->unmarkMoving ();
2084+ if (UNLIKELY (ref == 0 )) {
2085+ const auto res =
2086+ releaseBackToAllocator (*candidate,
2087+ RemoveContext::kNormal , false );
2088+ XDCHECK (res == ReleaseRes::kReleased );
2089+ }
20632090 }
2091+
20642092 }
2065-
20662093 return promotions;
20672094 }
20682095
@@ -2173,18 +2200,14 @@ class CacheAllocator : public CacheBase {
21732200 std::optional<bool > saveNvmCache ();
21742201 void saveRamCache ();
21752202
2176- static bool itemExclusivePredicate (const Item& item) {
2177- return item.getRefCount () == 0 ;
2203+ static bool itemSlabMovePredicate (const Item& item) {
2204+ return item.isMoving () && item. getRefCount () == 0 ;
21782205 }
21792206
21802207 static bool itemExpiryPredicate (const Item& item) {
21812208 return item.getRefCount () == 1 && item.isExpired ();
21822209 }
21832210
2184- static bool parentEvictForSlabReleasePredicate (const Item& item) {
2185- return item.getRefCount () == 1 && !item.isExclusive ();
2186- }
2187-
21882211 std::unique_ptr<Deserializer> createDeserializer ();
21892212
21902213 // Execute func on each item. `func` can throw exception but must ensure
@@ -2234,8 +2257,9 @@ class CacheAllocator : public CacheBase {
22342257 return memoryTierConfigs.size ();
22352258 }
22362259
2237- bool addWaitContextForMovingItem (
2238- folly::StringPiece key, std::shared_ptr<WaitContext<ReadHandle>> waiter);
2260+ WriteHandle handleWithWaitContextForMovingItem (Item& item);
2261+
2262+ size_t wakeUpWaiters (folly::StringPiece key, WriteHandle&& handle);
22392263
22402264 class MoveCtx {
22412265 public:
@@ -2260,6 +2284,8 @@ class CacheAllocator : public CacheBase {
22602284 waiters.push_back (std::move (waiter));
22612285 }
22622286
2287+ size_t numWaiters () const { return waiters.size (); }
2288+
22632289 private:
22642290 // notify all pending waiters that are waiting for the fetch.
22652291 void wakeUpWaiters () {
0 commit comments