@@ -1349,7 +1349,7 @@ class CacheAllocator : public CacheBase {
13491349
13501350 private:
13511351 // wrapper around Item's refcount and active handle tracking
1352- FOLLY_ALWAYS_INLINE bool incRef (Item& it);
1352+ FOLLY_ALWAYS_INLINE RefcountWithFlags::incResult incRef (Item& it);
13531353 FOLLY_ALWAYS_INLINE RefcountWithFlags::Value decRef (Item& it);
13541354
13551355 // drops the refcount and if needed, frees the allocation back to the memory
@@ -1473,26 +1473,13 @@ class CacheAllocator : public CacheBase {
14731473 // The parent handle parameter here is mainly used to find the
14741474 // correct pool to allocate memory for this chained item
14751475 //
1476- // @param parent handle to the cache item
1476+ // @param parent the parent item
14771477 // @param size the size for the chained allocation
14781478 //
14791479 // @return handle to the chained allocation
14801480 // @throw std::invalid_argument if the size requested is invalid or
14811481 // if the item is invalid
1482- WriteHandle allocateChainedItemInternal (const ReadHandle& parent,
1483- uint32_t size);
1484-
1485- // Given an item and its parentKey, validate that the parentKey
1486- // corresponds to an item that's the parent of the supplied item.
1487- //
1488- // @param item item that we want to get the parent handle for
1489- // @param parentKey key of the item's parent
1490- //
1491- // @return handle to the parent item if the validations pass
1492- // otherwise, an empty Handle is returned.
1493- //
1494- ReadHandle validateAndGetParentHandleForChainedMoveLocked (
1495- const ChainedItem& item, const Key& parentKey);
1482+ WriteHandle allocateChainedItemInternal (const Item& parent, uint32_t size);
14961483
14971484 // Given an existing item, allocate a new one for the
14981485 // existing one to later be moved into.
@@ -1609,7 +1596,7 @@ class CacheAllocator : public CacheBase {
16091596 // @param newParent the new parent for the chain
16101597 //
16111598 // @throw if any of the conditions for parent or newParent are not met.
1612- void transferChainLocked (WriteHandle & parent, WriteHandle& newParent);
1599+ void transferChainLocked (Item & parent, WriteHandle& newParent);
16131600
16141601 // replace a chained item in the existing chain. This needs to be called
16151602 // with the chained item lock held exclusive
@@ -1623,6 +1610,24 @@ class CacheAllocator : public CacheBase {
16231610 WriteHandle newItemHdl,
16241611 const Item& parent);
16251612
1613+ //
1614+ // Performs the actual inplace replace - it is called from
1615+ // moveChainedItem and replaceChainedItemLocked
1616+ // must hold chainedItemLock
1617+ //
1618+ // @param oldItem the item we are replacing in the chain
1619+ // @param newItem the item we are replacing it with
1620+ // @param parent the parent for the chain
1621+ // @param fromMove used to determine if the replaced was called from
1622+ // moveChainedItem - we avoid the handle destructor
1623+ // in this case.
1624+ //
1625+ // @return handle to the oldItem
1626+ void replaceInChainLocked (Item& oldItem,
1627+ WriteHandle& newItemHdl,
1628+ const Item& parent,
1629+ bool fromMove);
1630+
16261631 // Insert an item into MM container. The caller must hold a valid handle for
16271632 // the item.
16281633 //
@@ -1731,6 +1736,19 @@ class CacheAllocator : public CacheBase {
17311736
17321737 using EvictionIterator = typename MMContainer::LockedIterator;
17331738
1739+ // Wakes up waiters if there are any
1740+ //
1741+ // @param item wakes waiters that are waiting on that item
1742+ // @param handle handle to pass to the waiters
1743+ void wakeUpWaiters (Item& item, WriteHandle handle);
1744+
1745+ // Unmarks item as moving and wakes up any waiters waiting on that item
1746+ //
1747+ // @param item wakes waiters that are waiting on that item
1748+ // @param handle handle to pass to the waiters
1749+ typename RefcountWithFlags::Value unmarkMovingAndWakeUpWaiters (
1750+ Item& item, WriteHandle handle);
1751+
17341752 // Deserializer CacheAllocatorMetadata and verify the version
17351753 //
17361754 // @param deserializer Deserializer object
@@ -1824,16 +1842,6 @@ class CacheAllocator : public CacheBase {
18241842 Item& item,
18251843 util::Throttler& throttler);
18261844
1827- // "Move" (by copying) the content in this item to another memory
1828- // location by invoking the move callback.
1829- //
1830- // @param item old item to be moved elsewhere
1831- // @param newItemHdl handle of new item to be moved into
1832- //
1833- // @return true if the item has been moved
1834- // false if we have exhausted moving attempts
1835- bool tryMovingForSlabRelease (Item& item, WriteHandle& newItemHdl);
1836-
18371845 // Evict an item from access and mm containers and
18381846 // ensure it is safe for freeing.
18391847 //
@@ -1844,6 +1852,11 @@ class CacheAllocator : public CacheBase {
18441852 Item& item,
18451853 util::Throttler& throttler);
18461854
1855+ // Helper function to create PutToken
1856+ //
1857+ // @return valid token if the item should be written to NVM cache.
1858+ typename NvmCacheT::PutToken createPutToken (Item& item);
1859+
18471860 // Helper function to evict a normal item for slab release
18481861 //
18491862 // @return last handle for corresponding to item on success. empty handle on
@@ -2082,6 +2095,88 @@ class CacheAllocator : public CacheBase {
20822095
20832096 // BEGIN private members
20842097
2098+ bool tryGetHandleWithWaitContextForMovingItem (Item& item,
2099+ WriteHandle& handle);
2100+
2101+ size_t wakeUpWaitersLocked (folly::StringPiece key, WriteHandle&& handle);
2102+
2103+ class MoveCtx {
2104+ public:
2105+ MoveCtx () {}
2106+
2107+ ~MoveCtx () {
2108+ // prevent any further enqueue to waiters
2109+ // Note: we don't need to hold locks since no one can enqueue
2110+ // after this point.
2111+ wakeUpWaiters ();
2112+ }
2113+
2114+ // record the item handle. Upon destruction we will wake up the waiters
2115+ // and pass a clone of the handle to the callBack. By default we pass
2116+ // a null handle
2117+ void setItemHandle (WriteHandle _it) { it = std::move (_it); }
2118+
2119+ // enqueue a waiter into the waiter list
2120+ // @param waiter WaitContext
2121+ void addWaiter (std::shared_ptr<WaitContext<ReadHandle>> waiter) {
2122+ XDCHECK (waiter);
2123+ waiters.push_back (std::move (waiter));
2124+ }
2125+
2126+ size_t numWaiters () const { return waiters.size (); }
2127+
2128+ private:
2129+ // notify all pending waiters that are waiting for the fetch.
2130+ void wakeUpWaiters () {
2131+ bool refcountOverflowed = false ;
2132+ for (auto & w : waiters) {
2133+ // If refcount overflowed earlier, then we will return miss to
2134+ // all subsequent waiters.
2135+ if (refcountOverflowed) {
2136+ w->set (WriteHandle{});
2137+ continue ;
2138+ }
2139+
2140+ try {
2141+ w->set (it.clone ());
2142+ } catch (const exception::RefcountOverflow&) {
2143+ // We'll return a miss to the user's pending read,
2144+ // so we should enqueue a delete via NvmCache.
2145+ // TODO: cache.remove(it);
2146+ refcountOverflowed = true ;
2147+ }
2148+ }
2149+ }
2150+
2151+ WriteHandle it; // will be set when Context is being filled
2152+ std::vector<std::shared_ptr<WaitContext<ReadHandle>>> waiters; // list of
2153+ // waiters
2154+ };
2155+ using MoveMap =
2156+ folly::F14ValueMap<folly::StringPiece,
2157+ std::unique_ptr<MoveCtx>,
2158+ folly::HeterogeneousAccessHash<folly::StringPiece>>;
2159+
2160+ static size_t getShardForKey (folly::StringPiece key) {
2161+ return folly::Hash ()(key) % kShards ;
2162+ }
2163+
2164+ MoveMap& getMoveMapForShard (size_t shard) {
2165+ return movesMap_[shard].movesMap_ ;
2166+ }
2167+
2168+ MoveMap& getMoveMap (folly::StringPiece key) {
2169+ return getMoveMapForShard (getShardForKey (key));
2170+ }
2171+
2172+ std::unique_lock<std::mutex> getMoveLockForShard (size_t shard) {
2173+ return std::unique_lock<std::mutex>(moveLock_[shard].moveLock_ );
2174+ }
2175+
2176+ std::unique_lock<std::mutex> getMoveLock (folly::StringPiece key) {
2177+ return getMoveLockForShard (getShardForKey (key));
2178+ }
2179+
20852180 // Whether the memory allocator for this cache allocator was created on shared
20862181 // memory. The hash table, chained item hash table etc is also created on
20872182 // shared memory except for temporary shared memory mode when they're created
@@ -2175,6 +2270,22 @@ class CacheAllocator : public CacheBase {
21752270 // poolResizer_, poolOptimizer_, memMonitor_, reaper_
21762271 mutable std::mutex workersMutex_;
21772272
2273+ static constexpr size_t kShards = 8192 ; // TODO: need to define right value
2274+
2275+ struct MovesMapShard {
2276+ alignas (folly::hardware_destructive_interference_size) MoveMap movesMap_;
2277+ };
2278+
2279+ struct MoveLock {
2280+ alignas (folly::hardware_destructive_interference_size) std::mutex moveLock_;
2281+ };
2282+
2283+ // a map of all pending moves
2284+ std::vector<MovesMapShard> movesMap_;
2285+
2286+ // a map of move locks for each shard
2287+ std::vector<MoveLock> moveLock_;
2288+
21782289 // time when the ram cache was first created
21792290 const uint32_t cacheCreationTime_{0 };
21802291
0 commit comments