22
22
#include < folly/ScopeGuard.h>
23
23
#include < folly/logging/xlog.h>
24
24
#include < folly/synchronization/SanitizeThread.h>
25
+ #include < folly/hash/Hash.h>
26
+ #include < folly/container/F14Map.h>
25
27
#include < gtest/gtest.h>
26
28
27
29
#include < chrono>
@@ -1319,7 +1321,7 @@ class CacheAllocator : public CacheBase {
1319
1321
1320
1322
private:
1321
1323
// 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 );
1323
1325
FOLLY_ALWAYS_INLINE RefcountWithFlags::Value decRef (Item& it);
1324
1326
1325
1327
// drops the refcount and if needed, frees the allocation back to the memory
@@ -1550,7 +1552,7 @@ class CacheAllocator : public CacheBase {
1550
1552
//
1551
1553
// @return true If the move was completed, and the containers were updated
1552
1554
// successfully.
1553
- bool moveRegularItemOnEviction (Item& oldItem, WriteHandle& newItemHdl);
1555
+ void moveRegularItemWithSync (Item& oldItem, WriteHandle& newItemHdl);
1554
1556
1555
1557
// Moves a regular item to a different slab. This should only be used during
1556
1558
// slab release after the item's exclusive bit has been set. The user supplied
@@ -1637,6 +1639,10 @@ class CacheAllocator : public CacheBase {
1637
1639
// false if the item is not in MMContainer
1638
1640
bool removeFromMMContainer (Item& item);
1639
1641
1642
+ using EvictionIterator = typename MMContainer::LockedIterator;
1643
+
1644
+ WriteHandle acquire (EvictionIterator& it) { return acquire (it.get ()); }
1645
+
1640
1646
// Replaces an item in the MMContainer with another item, at the same
1641
1647
// position.
1642
1648
//
@@ -1647,6 +1653,8 @@ class CacheAllocator : public CacheBase {
1647
1653
// destination item did not exist in the container, or if the
1648
1654
// source item already existed.
1649
1655
bool replaceInMMContainer (Item& oldItem, Item& newItem);
1656
+ bool replaceInMMContainer (Item* oldItem, Item& newItem);
1657
+ bool replaceInMMContainer (EvictionIterator& oldItemIt, Item& newItem);
1650
1658
1651
1659
// Replaces an item in the MMContainer with another item, at the same
1652
1660
// position. Or, if the two chained items belong to two different MM
@@ -1705,7 +1713,35 @@ class CacheAllocator : public CacheBase {
1705
1713
// @return An evicted item or nullptr if there is no suitable candidate.
1706
1714
Item* findEviction (TierId tid, PoolId pid, ClassId cid);
1707
1715
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);
1709
1745
1710
1746
// Deserializer CacheAllocatorMetadata and verify the version
1711
1747
//
@@ -1823,6 +1859,12 @@ class CacheAllocator : public CacheBase {
1823
1859
1824
1860
typename NvmCacheT::PutToken createPutToken (Item& item);
1825
1861
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
+
1826
1868
// Helper function to remove a item if expired.
1827
1869
//
1828
1870
// @return true if it item expire and removed successfully.
@@ -2008,6 +2050,87 @@ class CacheAllocator : public CacheBase {
2008
2050
2009
2051
size_t memoryTierSize (TierId tid) const ;
2010
2052
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
+
2011
2134
// Whether the memory allocator for this cache allocator was created on shared
2012
2135
// memory. The hash table, chained item hash table etc is also created on
2013
2136
// shared memory except for temporary shared memory mode when they're created
@@ -2100,6 +2223,22 @@ class CacheAllocator : public CacheBase {
2100
2223
// poolResizer_, poolOptimizer_, memMonitor_, reaper_
2101
2224
mutable std::mutex workersMutex_;
2102
2225
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
+
2103
2242
// time when the ram cache was first created
2104
2243
const uint32_t cacheCreationTime_{0 };
2105
2244
0 commit comments