@@ -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