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>
@@ -1326,7 +1328,7 @@ class CacheAllocator : public CacheBase {
13261328
13271329 private:
13281330 // wrapper around Item's refcount and active handle tracking
1329- FOLLY_ALWAYS_INLINE bool incRef (Item& it);
1331+ FOLLY_ALWAYS_INLINE RefcountWithFlags::incResult incRef (Item& it, bool failIfMoving );
13301332 FOLLY_ALWAYS_INLINE RefcountWithFlags::Value decRef (Item& it);
13311333
13321334 // drops the refcount and if needed, frees the allocation back to the memory
@@ -1556,7 +1558,7 @@ class CacheAllocator : public CacheBase {
15561558 //
15571559 // @return true If the move was completed, and the containers were updated
15581560 // successfully.
1559- bool moveRegularItemOnEviction (Item& oldItem, WriteHandle& newItemHdl);
1561+ void moveRegularItemWithSync (Item& oldItem, WriteHandle& newItemHdl);
15601562
15611563 // Moves a regular item to a different slab. This should only be used during
15621564 // slab release after the item's exclusive bit has been set. The user supplied
@@ -1643,6 +1645,10 @@ class CacheAllocator : public CacheBase {
16431645 // false if the item is not in MMContainer
16441646 bool removeFromMMContainer (Item& item);
16451647
1648+ using EvictionIterator = typename MMContainer::LockedIterator;
1649+
1650+ WriteHandle acquire (EvictionIterator& it) { return acquire (it.get ()); }
1651+
16461652 // Replaces an item in the MMContainer with another item, at the same
16471653 // position.
16481654 //
@@ -1653,6 +1659,8 @@ class CacheAllocator : public CacheBase {
16531659 // destination item did not exist in the container, or if the
16541660 // source item already existed.
16551661 bool replaceInMMContainer (Item& oldItem, Item& newItem);
1662+ bool replaceInMMContainer (Item* oldItem, Item& newItem);
1663+ bool replaceInMMContainer (EvictionIterator& oldItemIt, Item& newItem);
16561664
16571665 // Replaces an item in the MMContainer with another item, at the same
16581666 // position. Or, if the two chained items belong to two different MM
@@ -1735,7 +1743,35 @@ class CacheAllocator : public CacheBase {
17351743 ClassId cid,
17361744 unsigned int & searchTries);
17371745
1738- using EvictionIterator = typename MMContainer::LockedIterator;
1746+ // Try to move the item down to the next memory tier
1747+ //
1748+ // @param tid current tier ID of the item
1749+ // @param pid the pool ID the item belong to.
1750+ // @param item the item to evict
1751+ //
1752+ // @return valid handle to the item. This will be the last
1753+ // handle to the item. On failure an empty handle.
1754+ WriteHandle tryEvictToNextMemoryTier (TierId tid, PoolId pid, Item& item);
1755+
1756+ // Wakes up waiters if there are any
1757+ //
1758+ // @param item wakes waiters that are waiting on that item
1759+ // @param handle handle to pass to the waiters
1760+ void wakeUpWaiters (Item& item, WriteHandle handle);
1761+
1762+ // Unmarks item as moving and wakes up any waiters waiting on that item
1763+ //
1764+ // @param item wakes waiters that are waiting on that item
1765+ // @param handle handle to pass to the waiters
1766+ typename RefcountWithFlags::Value unmarkMovingAndWakeUpWaiters (Item &item, WriteHandle handle);
1767+
1768+ // Try to move the item down to the next memory tier
1769+ //
1770+ // @param item the item to evict
1771+ //
1772+ // @return valid handle to the item. This will be the last
1773+ // handle to the item. On failure an empty handle.
1774+ WriteHandle tryEvictToNextMemoryTier (Item& item);
17391775
17401776 // Deserializer CacheAllocatorMetadata and verify the version
17411777 //
@@ -1853,6 +1889,12 @@ class CacheAllocator : public CacheBase {
18531889
18541890 typename NvmCacheT::PutToken createPutToken (Item& item);
18551891
1892+ // Helper function to remove a item if predicates is true.
1893+ //
1894+ // @return last handle to the item on success. empty handle on failure.
1895+ template <typename Fn>
1896+ WriteHandle removeIf (Item& item, Fn&& predicate);
1897+
18561898 // Helper function to remove a item if expired.
18571899 //
18581900 // @return true if it item expire and removed successfully.
@@ -2038,6 +2080,87 @@ class CacheAllocator : public CacheBase {
20382080
20392081 size_t memoryTierSize (TierId tid) const ;
20402082
2083+ WriteHandle handleWithWaitContextForMovingItem (Item& item);
2084+
2085+ size_t wakeUpWaitersLocked (folly::StringPiece key, WriteHandle&& handle);
2086+
2087+ class MoveCtx {
2088+ public:
2089+ MoveCtx () {}
2090+
2091+ ~MoveCtx () {
2092+ // prevent any further enqueue to waiters
2093+ // Note: we don't need to hold locks since no one can enqueue
2094+ // after this point.
2095+ wakeUpWaiters ();
2096+ }
2097+
2098+ // record the item handle. Upon destruction we will wake up the waiters
2099+ // and pass a clone of the handle to the callBack. By default we pass
2100+ // a null handle
2101+ void setItemHandle (WriteHandle _it) { it = std::move (_it); }
2102+
2103+ // enqueue a waiter into the waiter list
2104+ // @param waiter WaitContext
2105+ void addWaiter (std::shared_ptr<WaitContext<ReadHandle>> waiter) {
2106+ XDCHECK (waiter);
2107+ waiters.push_back (std::move (waiter));
2108+ }
2109+
2110+ size_t numWaiters () const { return waiters.size (); }
2111+
2112+ private:
2113+ // notify all pending waiters that are waiting for the fetch.
2114+ void wakeUpWaiters () {
2115+ bool refcountOverflowed = false ;
2116+ for (auto & w : waiters) {
2117+ // If refcount overflowed earlier, then we will return miss to
2118+ // all subsequent waitors.
2119+ if (refcountOverflowed) {
2120+ w->set (WriteHandle{});
2121+ continue ;
2122+ }
2123+
2124+ try {
2125+ w->set (it.clone ());
2126+ } catch (const exception::RefcountOverflow&) {
2127+ // We'll return a miss to the user's pending read,
2128+ // so we should enqueue a delete via NvmCache.
2129+ // TODO: cache.remove(it);
2130+ refcountOverflowed = true ;
2131+ }
2132+ }
2133+ }
2134+
2135+ WriteHandle it; // will be set when Context is being filled
2136+ std::vector<std::shared_ptr<WaitContext<ReadHandle>>> waiters; // list of
2137+ // waiters
2138+ };
2139+ using MoveMap =
2140+ folly::F14ValueMap<folly::StringPiece,
2141+ std::unique_ptr<MoveCtx>,
2142+ folly::HeterogeneousAccessHash<folly::StringPiece>>;
2143+
2144+ static size_t getShardForKey (folly::StringPiece key) {
2145+ return folly::Hash ()(key) % kShards ;
2146+ }
2147+
2148+ MoveMap& getMoveMapForShard (size_t shard) {
2149+ return movesMap_[shard].movesMap_ ;
2150+ }
2151+
2152+ MoveMap& getMoveMap (folly::StringPiece key) {
2153+ return getMoveMapForShard (getShardForKey (key));
2154+ }
2155+
2156+ std::unique_lock<std::mutex> getMoveLockForShard (size_t shard) {
2157+ return std::unique_lock<std::mutex>(moveLock_[shard].moveLock_ );
2158+ }
2159+
2160+ std::unique_lock<std::mutex> getMoveLock (folly::StringPiece key) {
2161+ return getMoveLockForShard (getShardForKey (key));
2162+ }
2163+
20412164 // Whether the memory allocator for this cache allocator was created on shared
20422165 // memory. The hash table, chained item hash table etc is also created on
20432166 // shared memory except for temporary shared memory mode when they're created
@@ -2128,6 +2251,22 @@ class CacheAllocator : public CacheBase {
21282251 // poolResizer_, poolOptimizer_, memMonitor_, reaper_
21292252 mutable std::mutex workersMutex_;
21302253
2254+ static constexpr size_t kShards = 8192 ; // TODO: need to define right value
2255+
2256+ struct MovesMapShard {
2257+ alignas (folly::hardware_destructive_interference_size) MoveMap movesMap_;
2258+ };
2259+
2260+ struct MoveLock {
2261+ alignas (folly::hardware_destructive_interference_size) std::mutex moveLock_;
2262+ };
2263+
2264+ // a map of all pending moves
2265+ std::vector<MovesMapShard> movesMap_;
2266+
2267+ // a map of move locks for each shard
2268+ std::vector<MoveLock> moveLock_;
2269+
21312270 // time when the ram cache was first created
21322271 const uint32_t cacheCreationTime_{0 };
21332272
0 commit comments