2222#include < folly/ScopeGuard.h>
2323#include < folly/logging/xlog.h>
2424#include < folly/synchronization/SanitizeThread.h>
25+ #include < folly/hash/Hash.h>
26+ #include < folly/container/F14Map.h>
2527#include < gtest/gtest.h>
2628
2729#include < chrono>
@@ -1319,7 +1321,7 @@ class CacheAllocator : public CacheBase {
13191321
13201322 private:
13211323 // wrapper around Item's refcount and active handle tracking
1322- FOLLY_ALWAYS_INLINE bool incRef (Item& it);
1324+ FOLLY_ALWAYS_INLINE RefcountWithFlags::incResult incRef (Item& it, bool failIfMoving );
13231325 FOLLY_ALWAYS_INLINE RefcountWithFlags::Value decRef (Item& it);
13241326
13251327 // drops the refcount and if needed, frees the allocation back to the memory
@@ -1550,7 +1552,7 @@ class CacheAllocator : public CacheBase {
15501552 //
15511553 // @return true If the move was completed, and the containers were updated
15521554 // successfully.
1553- bool moveRegularItemOnEviction (Item& oldItem, WriteHandle& newItemHdl);
1555+ void moveRegularItemWithSync (Item& oldItem, WriteHandle& newItemHdl);
15541556
15551557 // Moves a regular item to a different slab. This should only be used during
15561558 // slab release after the item's exclusive bit has been set. The user supplied
@@ -1637,6 +1639,10 @@ class CacheAllocator : public CacheBase {
16371639 // false if the item is not in MMContainer
16381640 bool removeFromMMContainer (Item& item);
16391641
1642+ using EvictionIterator = typename MMContainer::LockedIterator;
1643+
1644+ WriteHandle acquire (EvictionIterator& it) { return acquire (it.get ()); }
1645+
16401646 // Replaces an item in the MMContainer with another item, at the same
16411647 // position.
16421648 //
@@ -1647,6 +1653,8 @@ class CacheAllocator : public CacheBase {
16471653 // destination item did not exist in the container, or if the
16481654 // source item already existed.
16491655 bool replaceInMMContainer (Item& oldItem, Item& newItem);
1656+ bool replaceInMMContainer (Item* oldItem, Item& newItem);
1657+ bool replaceInMMContainer (EvictionIterator& oldItemIt, Item& newItem);
16501658
16511659 // Replaces an item in the MMContainer with another item, at the same
16521660 // position. Or, if the two chained items belong to two different MM
@@ -1705,7 +1713,35 @@ class CacheAllocator : public CacheBase {
17051713 // @return An evicted item or nullptr if there is no suitable candidate.
17061714 Item* findEviction (TierId tid, PoolId pid, ClassId cid);
17071715
1708- using EvictionIterator = typename MMContainer::LockedIterator;
1716+ // Try to move the item down to the next memory tier
1717+ //
1718+ // @param tid current tier ID of the item
1719+ // @param pid the pool ID the item belong to.
1720+ // @param item the item to evict
1721+ //
1722+ // @return valid handle to the item. This will be the last
1723+ // handle to the item. On failure an empty handle.
1724+ WriteHandle tryEvictToNextMemoryTier (TierId tid, PoolId pid, Item& item);
1725+
1726+ // Wakes up waiters if there are any
1727+ //
1728+ // @param item wakes waiters that are waiting on that item
1729+ // @param handle handle to pass to the waiters
1730+ void wakeUpWaiters (Item& item, WriteHandle handle);
1731+
1732+ // Unmarks item as moving and wakes up any waiters waiting on that item
1733+ //
1734+ // @param item wakes waiters that are waiting on that item
1735+ // @param handle handle to pass to the waiters
1736+ typename RefcountWithFlags::Value unmarkMovingAndWakeUpWaiters (Item &item, WriteHandle handle);
1737+
1738+ // Try to move the item down to the next memory tier
1739+ //
1740+ // @param item the item to evict
1741+ //
1742+ // @return valid handle to the item. This will be the last
1743+ // handle to the item. On failure an empty handle.
1744+ WriteHandle tryEvictToNextMemoryTier (Item& item);
17091745
17101746 // Deserializer CacheAllocatorMetadata and verify the version
17111747 //
@@ -1823,6 +1859,12 @@ class CacheAllocator : public CacheBase {
18231859
18241860 typename NvmCacheT::PutToken createPutToken (Item& item);
18251861
1862+ // Helper function to remove a item if predicates is true.
1863+ //
1864+ // @return last handle to the item on success. empty handle on failure.
1865+ template <typename Fn>
1866+ WriteHandle removeIf (Item& item, Fn&& predicate);
1867+
18261868 // Helper function to remove a item if expired.
18271869 //
18281870 // @return true if it item expire and removed successfully.
@@ -2008,6 +2050,87 @@ class CacheAllocator : public CacheBase {
20082050
20092051 size_t memoryTierSize (TierId tid) const ;
20102052
2053+ WriteHandle handleWithWaitContextForMovingItem (Item& item);
2054+
2055+ size_t wakeUpWaitersLocked (folly::StringPiece key, WriteHandle&& handle);
2056+
2057+ class MoveCtx {
2058+ public:
2059+ MoveCtx () {}
2060+
2061+ ~MoveCtx () {
2062+ // prevent any further enqueue to waiters
2063+ // Note: we don't need to hold locks since no one can enqueue
2064+ // after this point.
2065+ wakeUpWaiters ();
2066+ }
2067+
2068+ // record the item handle. Upon destruction we will wake up the waiters
2069+ // and pass a clone of the handle to the callBack. By default we pass
2070+ // a null handle
2071+ void setItemHandle (WriteHandle _it) { it = std::move (_it); }
2072+
2073+ // enqueue a waiter into the waiter list
2074+ // @param waiter WaitContext
2075+ void addWaiter (std::shared_ptr<WaitContext<ReadHandle>> waiter) {
2076+ XDCHECK (waiter);
2077+ waiters.push_back (std::move (waiter));
2078+ }
2079+
2080+ size_t numWaiters () const { return waiters.size (); }
2081+
2082+ private:
2083+ // notify all pending waiters that are waiting for the fetch.
2084+ void wakeUpWaiters () {
2085+ bool refcountOverflowed = false ;
2086+ for (auto & w : waiters) {
2087+ // If refcount overflowed earlier, then we will return miss to
2088+ // all subsequent waitors.
2089+ if (refcountOverflowed) {
2090+ w->set (WriteHandle{});
2091+ continue ;
2092+ }
2093+
2094+ try {
2095+ w->set (it.clone ());
2096+ } catch (const exception::RefcountOverflow&) {
2097+ // We'll return a miss to the user's pending read,
2098+ // so we should enqueue a delete via NvmCache.
2099+ // TODO: cache.remove(it);
2100+ refcountOverflowed = true ;
2101+ }
2102+ }
2103+ }
2104+
2105+ WriteHandle it; // will be set when Context is being filled
2106+ std::vector<std::shared_ptr<WaitContext<ReadHandle>>> waiters; // list of
2107+ // waiters
2108+ };
2109+ using MoveMap =
2110+ folly::F14ValueMap<folly::StringPiece,
2111+ std::unique_ptr<MoveCtx>,
2112+ folly::HeterogeneousAccessHash<folly::StringPiece>>;
2113+
2114+ static size_t getShardForKey (folly::StringPiece key) {
2115+ return folly::Hash ()(key) % kShards ;
2116+ }
2117+
2118+ MoveMap& getMoveMapForShard (size_t shard) {
2119+ return movesMap_[shard].movesMap_ ;
2120+ }
2121+
2122+ MoveMap& getMoveMap (folly::StringPiece key) {
2123+ return getMoveMapForShard (getShardForKey (key));
2124+ }
2125+
2126+ std::unique_lock<std::mutex> getMoveLockForShard (size_t shard) {
2127+ return std::unique_lock<std::mutex>(moveLock_[shard].moveLock_ );
2128+ }
2129+
2130+ std::unique_lock<std::mutex> getMoveLock (folly::StringPiece key) {
2131+ return getMoveLockForShard (getShardForKey (key));
2132+ }
2133+
20112134 // Whether the memory allocator for this cache allocator was created on shared
20122135 // memory. The hash table, chained item hash table etc is also created on
20132136 // shared memory except for temporary shared memory mode when they're created
@@ -2100,6 +2223,22 @@ class CacheAllocator : public CacheBase {
21002223 // poolResizer_, poolOptimizer_, memMonitor_, reaper_
21012224 mutable std::mutex workersMutex_;
21022225
2226+ static constexpr size_t kShards = 8192 ; // TODO: need to define right value
2227+
2228+ struct MovesMapShard {
2229+ alignas (folly::hardware_destructive_interference_size) MoveMap movesMap_;
2230+ };
2231+
2232+ struct MoveLock {
2233+ alignas (folly::hardware_destructive_interference_size) std::mutex moveLock_;
2234+ };
2235+
2236+ // a map of all pending moves
2237+ std::vector<MovesMapShard> movesMap_;
2238+
2239+ // a map of move locks for each shard
2240+ std::vector<MoveLock> moveLock_;
2241+
21032242 // time when the ram cache was first created
21042243 const uint32_t cacheCreationTime_{0 };
21052244
0 commit comments