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>
@@ -1326,7 +1328,7 @@ class CacheAllocator : public CacheBase {
1326
1328
1327
1329
private:
1328
1330
// 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 );
1330
1332
FOLLY_ALWAYS_INLINE RefcountWithFlags::Value decRef (Item& it);
1331
1333
1332
1334
// drops the refcount and if needed, frees the allocation back to the memory
@@ -1556,7 +1558,7 @@ class CacheAllocator : public CacheBase {
1556
1558
//
1557
1559
// @return true If the move was completed, and the containers were updated
1558
1560
// successfully.
1559
- bool moveRegularItemOnEviction (Item& oldItem, WriteHandle& newItemHdl);
1561
+ void moveRegularItemWithSync (Item& oldItem, WriteHandle& newItemHdl);
1560
1562
1561
1563
// Moves a regular item to a different slab. This should only be used during
1562
1564
// slab release after the item's exclusive bit has been set. The user supplied
@@ -1643,6 +1645,10 @@ class CacheAllocator : public CacheBase {
1643
1645
// false if the item is not in MMContainer
1644
1646
bool removeFromMMContainer (Item& item);
1645
1647
1648
+ using EvictionIterator = typename MMContainer::LockedIterator;
1649
+
1650
+ WriteHandle acquire (EvictionIterator& it) { return acquire (it.get ()); }
1651
+
1646
1652
// Replaces an item in the MMContainer with another item, at the same
1647
1653
// position.
1648
1654
//
@@ -1653,6 +1659,8 @@ class CacheAllocator : public CacheBase {
1653
1659
// destination item did not exist in the container, or if the
1654
1660
// source item already existed.
1655
1661
bool replaceInMMContainer (Item& oldItem, Item& newItem);
1662
+ bool replaceInMMContainer (Item* oldItem, Item& newItem);
1663
+ bool replaceInMMContainer (EvictionIterator& oldItemIt, Item& newItem);
1656
1664
1657
1665
// Replaces an item in the MMContainer with another item, at the same
1658
1666
// position. Or, if the two chained items belong to two different MM
@@ -1735,7 +1743,35 @@ class CacheAllocator : public CacheBase {
1735
1743
ClassId cid,
1736
1744
unsigned int & searchTries);
1737
1745
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);
1739
1775
1740
1776
// Deserializer CacheAllocatorMetadata and verify the version
1741
1777
//
@@ -1853,6 +1889,12 @@ class CacheAllocator : public CacheBase {
1853
1889
1854
1890
typename NvmCacheT::PutToken createPutToken (Item& item);
1855
1891
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
+
1856
1898
// Helper function to remove a item if expired.
1857
1899
//
1858
1900
// @return true if it item expire and removed successfully.
@@ -2038,6 +2080,87 @@ class CacheAllocator : public CacheBase {
2038
2080
2039
2081
size_t memoryTierSize (TierId tid) const ;
2040
2082
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
+
2041
2164
// Whether the memory allocator for this cache allocator was created on shared
2042
2165
// memory. The hash table, chained item hash table etc is also created on
2043
2166
// shared memory except for temporary shared memory mode when they're created
@@ -2128,6 +2251,22 @@ class CacheAllocator : public CacheBase {
2128
2251
// poolResizer_, poolOptimizer_, memMonitor_, reaper_
2129
2252
mutable std::mutex workersMutex_;
2130
2253
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
+
2131
2270
// time when the ram cache was first created
2132
2271
const uint32_t cacheCreationTime_{0 };
2133
2272
0 commit comments