@@ -1349,7 +1349,7 @@ class CacheAllocator : public CacheBase {
1349
1349
1350
1350
private:
1351
1351
// 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);
1353
1353
FOLLY_ALWAYS_INLINE RefcountWithFlags::Value decRef (Item& it);
1354
1354
1355
1355
// drops the refcount and if needed, frees the allocation back to the memory
@@ -1473,26 +1473,13 @@ class CacheAllocator : public CacheBase {
1473
1473
// The parent handle parameter here is mainly used to find the
1474
1474
// correct pool to allocate memory for this chained item
1475
1475
//
1476
- // @param parent handle to the cache item
1476
+ // @param parent the parent item
1477
1477
// @param size the size for the chained allocation
1478
1478
//
1479
1479
// @return handle to the chained allocation
1480
1480
// @throw std::invalid_argument if the size requested is invalid or
1481
1481
// if the item is invalid
1482
- WriteHandle allocateChainedItemInternal (const ReadHandle& parent,
1483
- uint32_t size);
1484
-
1485
- // Given an item and its parentKey, validate that the parentKey
1486
- // corresponds to an item that's the parent of the supplied item.
1487
- //
1488
- // @param item item that we want to get the parent handle for
1489
- // @param parentKey key of the item's parent
1490
- //
1491
- // @return handle to the parent item if the validations pass
1492
- // otherwise, an empty Handle is returned.
1493
- //
1494
- ReadHandle validateAndGetParentHandleForChainedMoveLocked (
1495
- const ChainedItem& item, const Key& parentKey);
1482
+ WriteHandle allocateChainedItemInternal (const Item& parent, uint32_t size);
1496
1483
1497
1484
// Given an existing item, allocate a new one for the
1498
1485
// existing one to later be moved into.
@@ -1609,7 +1596,7 @@ class CacheAllocator : public CacheBase {
1609
1596
// @param newParent the new parent for the chain
1610
1597
//
1611
1598
// @throw if any of the conditions for parent or newParent are not met.
1612
- void transferChainLocked (WriteHandle & parent, WriteHandle& newParent);
1599
+ void transferChainLocked (Item & parent, WriteHandle& newParent);
1613
1600
1614
1601
// replace a chained item in the existing chain. This needs to be called
1615
1602
// with the chained item lock held exclusive
@@ -1623,6 +1610,24 @@ class CacheAllocator : public CacheBase {
1623
1610
WriteHandle newItemHdl,
1624
1611
const Item& parent);
1625
1612
1613
+ //
1614
+ // Performs the actual inplace replace - it is called from
1615
+ // moveChainedItem and replaceChainedItemLocked
1616
+ // must hold chainedItemLock
1617
+ //
1618
+ // @param oldItem the item we are replacing in the chain
1619
+ // @param newItem the item we are replacing it with
1620
+ // @param parent the parent for the chain
1621
+ // @param fromMove used to determine if the replaced was called from
1622
+ // moveChainedItem - we avoid the handle destructor
1623
+ // in this case.
1624
+ //
1625
+ // @return handle to the oldItem
1626
+ void replaceInChainLocked (Item& oldItem,
1627
+ WriteHandle& newItemHdl,
1628
+ const Item& parent,
1629
+ bool fromMove);
1630
+
1626
1631
// Insert an item into MM container. The caller must hold a valid handle for
1627
1632
// the item.
1628
1633
//
@@ -1731,6 +1736,19 @@ class CacheAllocator : public CacheBase {
1731
1736
1732
1737
using EvictionIterator = typename MMContainer::LockedIterator;
1733
1738
1739
+ // Wakes up waiters if there are any
1740
+ //
1741
+ // @param item wakes waiters that are waiting on that item
1742
+ // @param handle handle to pass to the waiters
1743
+ void wakeUpWaiters (Item& item, WriteHandle handle);
1744
+
1745
+ // Unmarks item as moving and wakes up any waiters waiting on that item
1746
+ //
1747
+ // @param item wakes waiters that are waiting on that item
1748
+ // @param handle handle to pass to the waiters
1749
+ typename RefcountWithFlags::Value unmarkMovingAndWakeUpWaiters (
1750
+ Item& item, WriteHandle handle);
1751
+
1734
1752
// Deserializer CacheAllocatorMetadata and verify the version
1735
1753
//
1736
1754
// @param deserializer Deserializer object
@@ -1824,16 +1842,6 @@ class CacheAllocator : public CacheBase {
1824
1842
Item& item,
1825
1843
util::Throttler& throttler);
1826
1844
1827
- // "Move" (by copying) the content in this item to another memory
1828
- // location by invoking the move callback.
1829
- //
1830
- // @param item old item to be moved elsewhere
1831
- // @param newItemHdl handle of new item to be moved into
1832
- //
1833
- // @return true if the item has been moved
1834
- // false if we have exhausted moving attempts
1835
- bool tryMovingForSlabRelease (Item& item, WriteHandle& newItemHdl);
1836
-
1837
1845
// Evict an item from access and mm containers and
1838
1846
// ensure it is safe for freeing.
1839
1847
//
@@ -1844,6 +1852,11 @@ class CacheAllocator : public CacheBase {
1844
1852
Item& item,
1845
1853
util::Throttler& throttler);
1846
1854
1855
+ // Helper function to create PutToken
1856
+ //
1857
+ // @return valid token if the item should be written to NVM cache.
1858
+ typename NvmCacheT::PutToken createPutToken (Item& item);
1859
+
1847
1860
// Helper function to evict a normal item for slab release
1848
1861
//
1849
1862
// @return last handle for corresponding to item on success. empty handle on
@@ -2082,6 +2095,88 @@ class CacheAllocator : public CacheBase {
2082
2095
2083
2096
// BEGIN private members
2084
2097
2098
+ bool tryGetHandleWithWaitContextForMovingItem (Item& item,
2099
+ WriteHandle& handle);
2100
+
2101
+ size_t wakeUpWaitersLocked (folly::StringPiece key, WriteHandle&& handle);
2102
+
2103
+ class MoveCtx {
2104
+ public:
2105
+ MoveCtx () {}
2106
+
2107
+ ~MoveCtx () {
2108
+ // prevent any further enqueue to waiters
2109
+ // Note: we don't need to hold locks since no one can enqueue
2110
+ // after this point.
2111
+ wakeUpWaiters ();
2112
+ }
2113
+
2114
+ // record the item handle. Upon destruction we will wake up the waiters
2115
+ // and pass a clone of the handle to the callBack. By default we pass
2116
+ // a null handle
2117
+ void setItemHandle (WriteHandle _it) { it = std::move (_it); }
2118
+
2119
+ // enqueue a waiter into the waiter list
2120
+ // @param waiter WaitContext
2121
+ void addWaiter (std::shared_ptr<WaitContext<ReadHandle>> waiter) {
2122
+ XDCHECK (waiter);
2123
+ waiters.push_back (std::move (waiter));
2124
+ }
2125
+
2126
+ size_t numWaiters () const { return waiters.size (); }
2127
+
2128
+ private:
2129
+ // notify all pending waiters that are waiting for the fetch.
2130
+ void wakeUpWaiters () {
2131
+ bool refcountOverflowed = false ;
2132
+ for (auto & w : waiters) {
2133
+ // If refcount overflowed earlier, then we will return miss to
2134
+ // all subsequent waiters.
2135
+ if (refcountOverflowed) {
2136
+ w->set (WriteHandle{});
2137
+ continue ;
2138
+ }
2139
+
2140
+ try {
2141
+ w->set (it.clone ());
2142
+ } catch (const exception ::RefcountOverflow&) {
2143
+ // We'll return a miss to the user's pending read,
2144
+ // so we should enqueue a delete via NvmCache.
2145
+ // TODO: cache.remove(it);
2146
+ refcountOverflowed = true ;
2147
+ }
2148
+ }
2149
+ }
2150
+
2151
+ WriteHandle it; // will be set when Context is being filled
2152
+ std::vector<std::shared_ptr<WaitContext<ReadHandle>>> waiters; // list of
2153
+ // waiters
2154
+ };
2155
+ using MoveMap =
2156
+ folly::F14ValueMap<folly::StringPiece,
2157
+ std::unique_ptr<MoveCtx>,
2158
+ folly::HeterogeneousAccessHash<folly::StringPiece>>;
2159
+
2160
+ static size_t getShardForKey (folly::StringPiece key) {
2161
+ return folly::Hash ()(key) % kShards ;
2162
+ }
2163
+
2164
+ MoveMap& getMoveMapForShard (size_t shard) {
2165
+ return movesMap_[shard].movesMap_ ;
2166
+ }
2167
+
2168
+ MoveMap& getMoveMap (folly::StringPiece key) {
2169
+ return getMoveMapForShard (getShardForKey (key));
2170
+ }
2171
+
2172
+ std::unique_lock<std::mutex> getMoveLockForShard (size_t shard) {
2173
+ return std::unique_lock<std::mutex>(moveLock_[shard].moveLock_ );
2174
+ }
2175
+
2176
+ std::unique_lock<std::mutex> getMoveLock (folly::StringPiece key) {
2177
+ return getMoveLockForShard (getShardForKey (key));
2178
+ }
2179
+
2085
2180
// Whether the memory allocator for this cache allocator was created on shared
2086
2181
// memory. The hash table, chained item hash table etc is also created on
2087
2182
// shared memory except for temporary shared memory mode when they're created
@@ -2175,6 +2270,22 @@ class CacheAllocator : public CacheBase {
2175
2270
// poolResizer_, poolOptimizer_, memMonitor_, reaper_
2176
2271
mutable std::mutex workersMutex_;
2177
2272
2273
+ static constexpr size_t kShards = 8192 ; // TODO: need to define right value
2274
+
2275
+ struct MovesMapShard {
2276
+ alignas (folly::hardware_destructive_interference_size) MoveMap movesMap_;
2277
+ };
2278
+
2279
+ struct MoveLock {
2280
+ alignas (folly::hardware_destructive_interference_size) std::mutex moveLock_;
2281
+ };
2282
+
2283
+ // a map of all pending moves
2284
+ std::vector<MovesMapShard> movesMap_;
2285
+
2286
+ // a map of move locks for each shard
2287
+ std::vector<MoveLock> moveLock_;
2288
+
2178
2289
// time when the ram cache was first created
2179
2290
const uint32_t cacheCreationTime_{0 };
2180
2291
0 commit comments