@@ -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,13 +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,
1482+ WriteHandle allocateChainedItemInternal (const Item & parent,
14831483 uint32_t size);
14841484
14851485 // Given an item and its parentKey, validate that the parentKey
@@ -1609,7 +1609,7 @@ class CacheAllocator : public CacheBase {
16091609 // @param newParent the new parent for the chain
16101610 //
16111611 // @throw if any of the conditions for parent or newParent are not met.
1612- void transferChainLocked (WriteHandle & parent, WriteHandle& newParent);
1612+ void transferChainLocked (Item & parent, WriteHandle& newParent);
16131613
16141614 // replace a chained item in the existing chain. This needs to be called
16151615 // with the chained item lock held exclusive
@@ -1623,6 +1623,24 @@ class CacheAllocator : public CacheBase {
16231623 WriteHandle newItemHdl,
16241624 const Item& parent);
16251625
1626+ //
1627+ // Performs the actual inplace replace - it is called from
1628+ // moveChainedItem and replaceChainedItemLocked
1629+ // must hold chainedItemLock
1630+ //
1631+ // @param oldItem the item we are replacing in the chain
1632+ // @param newItem the item we are replacing it with
1633+ // @param parent the parent for the chain
1634+ // @param fromMove used to determine if the replaced was called from
1635+ // moveChainedItem - we avoid the handle destructor
1636+ // in this case.
1637+ //
1638+ // @return handle to the oldItem
1639+ void replaceInChainLocked (Item& oldItem,
1640+ WriteHandle& newItemHdl,
1641+ const Item& parent,
1642+ bool fromMove);
1643+
16261644 // Insert an item into MM container. The caller must hold a valid handle for
16271645 // the item.
16281646 //
@@ -1731,6 +1749,18 @@ class CacheAllocator : public CacheBase {
17311749
17321750 using EvictionIterator = typename MMContainer::LockedIterator;
17331751
1752+ // Wakes up waiters if there are any
1753+ //
1754+ // @param item wakes waiters that are waiting on that item
1755+ // @param handle handle to pass to the waiters
1756+ void wakeUpWaiters (Item& item, WriteHandle handle);
1757+
1758+ // Unmarks item as moving and wakes up any waiters waiting on that item
1759+ //
1760+ // @param item wakes waiters that are waiting on that item
1761+ // @param handle handle to pass to the waiters
1762+ typename RefcountWithFlags::Value unmarkMovingAndWakeUpWaiters (Item &item, WriteHandle handle);
1763+
17341764 // Deserializer CacheAllocatorMetadata and verify the version
17351765 //
17361766 // @param deserializer Deserializer object
@@ -1844,6 +1874,11 @@ class CacheAllocator : public CacheBase {
18441874 Item& item,
18451875 util::Throttler& throttler);
18461876
1877+ // Helper function to create PutToken
1878+ //
1879+ // @return valid token if the item should be written to NVM cache.
1880+ typename NvmCacheT::PutToken createPutToken (Item& item);
1881+
18471882 // Helper function to evict a normal item for slab release
18481883 //
18491884 // @return last handle for corresponding to item on success. empty handle on
@@ -2082,6 +2117,87 @@ class CacheAllocator : public CacheBase {
20822117
20832118 // BEGIN private members
20842119
2120+ bool tryGetHandleWithWaitContextForMovingItem (Item& item, WriteHandle& handle);
2121+
2122+ size_t wakeUpWaitersLocked (folly::StringPiece key, WriteHandle&& handle);
2123+
2124+ class MoveCtx {
2125+ public:
2126+ MoveCtx () {}
2127+
2128+ ~MoveCtx () {
2129+ // prevent any further enqueue to waiters
2130+ // Note: we don't need to hold locks since no one can enqueue
2131+ // after this point.
2132+ wakeUpWaiters ();
2133+ }
2134+
2135+ // record the item handle. Upon destruction we will wake up the waiters
2136+ // and pass a clone of the handle to the callBack. By default we pass
2137+ // a null handle
2138+ void setItemHandle (WriteHandle _it) { it = std::move (_it); }
2139+
2140+ // enqueue a waiter into the waiter list
2141+ // @param waiter WaitContext
2142+ void addWaiter (std::shared_ptr<WaitContext<ReadHandle>> waiter) {
2143+ XDCHECK (waiter);
2144+ waiters.push_back (std::move (waiter));
2145+ }
2146+
2147+ size_t numWaiters () const { return waiters.size (); }
2148+
2149+ private:
2150+ // notify all pending waiters that are waiting for the fetch.
2151+ void wakeUpWaiters () {
2152+ bool refcountOverflowed = false ;
2153+ for (auto & w : waiters) {
2154+ // If refcount overflowed earlier, then we will return miss to
2155+ // all subsequent waiters.
2156+ if (refcountOverflowed) {
2157+ w->set (WriteHandle{});
2158+ continue ;
2159+ }
2160+
2161+ try {
2162+ w->set (it.clone ());
2163+ } catch (const exception::RefcountOverflow&) {
2164+ // We'll return a miss to the user's pending read,
2165+ // so we should enqueue a delete via NvmCache.
2166+ // TODO: cache.remove(it);
2167+ refcountOverflowed = true ;
2168+ }
2169+ }
2170+ }
2171+
2172+ WriteHandle it; // will be set when Context is being filled
2173+ std::vector<std::shared_ptr<WaitContext<ReadHandle>>> waiters; // list of
2174+ // waiters
2175+ };
2176+ using MoveMap =
2177+ folly::F14ValueMap<folly::StringPiece,
2178+ std::unique_ptr<MoveCtx>,
2179+ folly::HeterogeneousAccessHash<folly::StringPiece>>;
2180+
2181+ static size_t getShardForKey (folly::StringPiece key) {
2182+ return folly::Hash ()(key) % kShards ;
2183+ }
2184+
2185+ MoveMap& getMoveMapForShard (size_t shard) {
2186+ return movesMap_[shard].movesMap_ ;
2187+ }
2188+
2189+ MoveMap& getMoveMap (folly::StringPiece key) {
2190+ return getMoveMapForShard (getShardForKey (key));
2191+ }
2192+
2193+ std::unique_lock<std::mutex> getMoveLockForShard (size_t shard) {
2194+ return std::unique_lock<std::mutex>(moveLock_[shard].moveLock_ );
2195+ }
2196+
2197+ std::unique_lock<std::mutex> getMoveLock (folly::StringPiece key) {
2198+ return getMoveLockForShard (getShardForKey (key));
2199+ }
2200+
20852201 // Whether the memory allocator for this cache allocator was created on shared
20862202 // memory. The hash table, chained item hash table etc is also created on
20872203 // shared memory except for temporary shared memory mode when they're created
@@ -2175,6 +2291,22 @@ class CacheAllocator : public CacheBase {
21752291 // poolResizer_, poolOptimizer_, memMonitor_, reaper_
21762292 mutable std::mutex workersMutex_;
21772293
2294+ static constexpr size_t kShards = 8192 ; // TODO: need to define right value
2295+
2296+ struct MovesMapShard {
2297+ alignas (folly::hardware_destructive_interference_size) MoveMap movesMap_;
2298+ };
2299+
2300+ struct MoveLock {
2301+ alignas (folly::hardware_destructive_interference_size) std::mutex moveLock_;
2302+ };
2303+
2304+ // a map of all pending moves
2305+ std::vector<MovesMapShard> movesMap_;
2306+
2307+ // a map of move locks for each shard
2308+ std::vector<MoveLock> moveLock_;
2309+
21782310 // time when the ram cache was first created
21792311 const uint32_t cacheCreationTime_{0 };
21802312
0 commit comments