@@ -1389,7 +1389,7 @@ class CacheAllocator : public CacheBase {
1389
1389
double slabsApproxFreePercentage (TierId tid) const ;
1390
1390
1391
1391
// wrapper around Item's refcount and active handle tracking
1392
- FOLLY_ALWAYS_INLINE void incRef (Item& it);
1392
+ FOLLY_ALWAYS_INLINE RefcountWithFlags::incResult incRef (Item& it, bool failIfMoving );
1393
1393
FOLLY_ALWAYS_INLINE RefcountWithFlags::Value decRef (Item& it);
1394
1394
1395
1395
// drops the refcount and if needed, frees the allocation back to the memory
@@ -1440,6 +1440,12 @@ class CacheAllocator : public CacheBase {
1440
1440
bool nascent = false ,
1441
1441
const Item* toRecycle = nullptr );
1442
1442
1443
+ // Must be called by the thread which called markExclusive and
1444
+ // succeeded. After this call, the item is unlinked from Access and
1445
+ // MM Containers. The item is no longer marked as exclusive and it's
1446
+ // ref count is 0 - it's available for recycling.
1447
+ void unlinkItemExclusive (Item& it);
1448
+
1443
1449
// acquires an handle on the item. returns an empty handle if it is null.
1444
1450
// @param it pointer to an item
1445
1451
// @return WriteHandle return a handle to this item
@@ -1550,17 +1556,17 @@ class CacheAllocator : public CacheBase {
1550
1556
// @return handle to the parent item if the validations pass
1551
1557
// otherwise, an empty Handle is returned.
1552
1558
//
1553
- ReadHandle validateAndGetParentHandleForChainedMoveLocked (
1559
+ WriteHandle validateAndGetParentHandleForChainedMoveLocked (
1554
1560
const ChainedItem& item, const Key& parentKey);
1555
1561
1556
1562
// Given an existing item, allocate a new one for the
1557
1563
// existing one to later be moved into.
1558
1564
//
1559
- // @param oldItem the item we want to allocate a new item for
1565
+ // @param item reference to the item we want to allocate a new item for
1560
1566
//
1561
1567
// @return handle to the newly allocated item
1562
1568
//
1563
- WriteHandle allocateNewItemForOldItem (const Item& oldItem );
1569
+ WriteHandle allocateNewItemForOldItem (const Item& item );
1564
1570
1565
1571
// internal helper that grabs a refcounted handle to the item. This does
1566
1572
// not record the access to reflect in the mmContainer.
@@ -1614,18 +1620,16 @@ class CacheAllocator : public CacheBase {
1614
1620
// @param oldItem Reference to the item being moved
1615
1621
// @param newItemHdl Reference to the handle of the new item being moved into
1616
1622
//
1617
- // @return the handle to the oldItem if the move was completed
1618
- // and the oldItem can be recycled.
1619
- // Otherwise an empty handle is returned.
1620
- template <typename P>
1621
- WriteHandle moveRegularItemWithSync (Item& oldItem, WriteHandle& newItemHdl, P&& predicate);
1623
+ // @return true If the move was completed, and the containers were updated
1624
+ // successfully.
1625
+ void moveRegularItemWithSync (Item& oldItem, WriteHandle& newItemHdl);
1622
1626
1623
1627
// Moves a regular item to a different slab. This should only be used during
1624
1628
// slab release after the item's exclusive bit has been set. The user supplied
1625
1629
// callback is responsible for copying the contents and fixing the semantics
1626
1630
// of chained item.
1627
1631
//
1628
- // @param oldItem Reference to the item being moved
1632
+ // @param oldItem item being moved
1629
1633
// @param newItemHdl Reference to the handle of the new item being moved into
1630
1634
//
1631
1635
// @return true If the move was completed, and the containers were updated
@@ -1789,9 +1793,9 @@ class CacheAllocator : public CacheBase {
1789
1793
// handle to the item. On failure an empty handle.
1790
1794
WriteHandle tryEvictToNextMemoryTier (TierId tid, PoolId pid, Item& item, bool fromBgThread);
1791
1795
1792
- bool tryPromoteToNextMemoryTier (TierId tid, PoolId pid, Item& item, bool fromBgThread);
1796
+ WriteHandle tryPromoteToNextMemoryTier (TierId tid, PoolId pid, Item& item, bool fromBgThread);
1793
1797
1794
- bool tryPromoteToNextMemoryTier (Item& item, bool fromBgThread);
1798
+ WriteHandle tryPromoteToNextMemoryTier (Item& item, bool fromBgThread);
1795
1799
1796
1800
// Try to move the item down to the next memory tier
1797
1801
//
@@ -1878,22 +1882,23 @@ class CacheAllocator : public CacheBase {
1878
1882
1879
1883
// @return true when successfully marked as moving,
1880
1884
// fasle when this item has already been freed
1881
- bool markExclusiveForSlabRelease (const SlabReleaseContext& ctx,
1882
- void * alloc,
1883
- util::Throttler& throttler);
1885
+ bool markMovingForSlabRelease (const SlabReleaseContext& ctx,
1886
+ void * alloc,
1887
+ util::Throttler& throttler);
1884
1888
1885
1889
// "Move" (by copying) the content in this item to another memory
1886
1890
// location by invoking the move callback.
1887
1891
//
1888
1892
//
1889
1893
// @param ctx slab release context
1890
- // @param item old item to be moved elsewhere
1894
+ // @param oldItem old item to be moved elsewhere
1895
+ // @param handle handle to the item or to it's parent (if chained)
1891
1896
// @param throttler slow this function down as not to take too much cpu
1892
1897
//
1893
1898
// @return true if the item has been moved
1894
1899
// false if we have exhausted moving attempts
1895
1900
bool moveForSlabRelease (const SlabReleaseContext& ctx,
1896
- Item& item ,
1901
+ Item& oldItem ,
1897
1902
util::Throttler& throttler);
1898
1903
1899
1904
// "Move" (by copying) the content in this item to another memory
@@ -1929,6 +1934,8 @@ class CacheAllocator : public CacheBase {
1929
1934
// handle on failure. caller can retry.
1930
1935
WriteHandle evictChainedItemForSlabRelease (ChainedItem& item);
1931
1936
1937
+ typename NvmCacheT::PutToken createPutToken (Item& item);
1938
+
1932
1939
// Helper function to remove a item if predicates is true.
1933
1940
//
1934
1941
// @return last handle to the item on success. empty handle on failure.
@@ -1966,8 +1973,10 @@ class CacheAllocator : public CacheBase {
1966
1973
candidates.reserve (batch);
1967
1974
1968
1975
size_t tries = 0 ;
1969
- mmContainer.withEvictionIterator ([&tries, &candidates, &batch, this ](auto &&itr){
1970
- while (candidates.size () < batch && (config_.maxEvictionPromotionHotness == 0 || tries < config_.maxEvictionPromotionHotness ) && itr) {
1976
+ mmContainer.withEvictionIterator ([&tries, &candidates, &batch, &mmContainer, this ](auto &&itr) {
1977
+ while (candidates.size () < batch &&
1978
+ (config_.maxEvictionPromotionHotness == 0 || tries < config_.maxEvictionPromotionHotness ) &&
1979
+ itr) {
1971
1980
tries++;
1972
1981
Item* candidate = itr.get ();
1973
1982
XDCHECK (candidate);
@@ -1976,7 +1985,8 @@ class CacheAllocator : public CacheBase {
1976
1985
throw std::runtime_error (" Not supported for chained items" );
1977
1986
}
1978
1987
1979
- if (candidate->getRefCount () == 0 && candidate->markExclusive ()) {
1988
+ if (candidate->markMoving (true )) {
1989
+ mmContainer.remove (itr);
1980
1990
candidates.push_back (candidate);
1981
1991
}
1982
1992
@@ -1985,37 +1995,45 @@ class CacheAllocator : public CacheBase {
1985
1995
});
1986
1996
1987
1997
for (Item *candidate : candidates) {
1988
- {
1989
- auto toReleaseHandle =
1990
- evictNormalItem (*candidate,
1991
- true /* skipIfTokenInvalid */ , true /* from BG thread */ );
1992
- // destroy toReleseHandle. The item won't be release to allocator
1993
- // since we marked it as exclusive.
1994
- }
1995
- auto ref = candidate->unmarkExclusive ();
1996
-
1997
- if (ref == 0u ) {
1998
- if (candidate->hasChainedItem ()) {
1999
- (*stats_.chainedItemEvictions )[pid][cid].inc ();
2000
- } else {
2001
- (*stats_.regularItemEvictions )[pid][cid].inc ();
2002
- }
1998
+ auto evictedToNext = tryEvictToNextMemoryTier (*candidate, true /* from BgThread */ );
1999
+ if (!evictedToNext) {
2000
+ auto token = createPutToken (*candidate);
2001
+
2002
+ auto ret = candidate->markExclusiveWhenMoving ();
2003
+ XDCHECK (ret);
2004
+
2005
+ unlinkItemExclusive (*candidate);
2006
+ // wake up any readers that wait for the move to complete
2007
+ // it's safe to do now, as we have the item marked exclusive and
2008
+ // no other reader can be added to the waiters list
2009
+ wakeUpWaiters (candidate->getKey (), WriteHandle{});
2010
+
2011
+ if (token.isValid () && shouldWriteToNvmCacheExclusive (*candidate)) {
2012
+ nvmCache_->put (*candidate, std::move (token));
2013
+ }
2014
+ } else {
2015
+ evictions++;
2016
+ XDCHECK (!evictedToNext->isExclusive () && !evictedToNext->isMoving ());
2017
+ XDCHECK (!candidate->isExclusive () && !candidate->isMoving ());
2018
+ XDCHECK (!candidate->isAccessible ());
2019
+ XDCHECK (candidate->getKey () == evictedToNext->getKey ());
2003
2020
2004
- evictions++;
2005
- // it's safe to recycle the item here as there are no more
2006
- // references and the item could not been marked as moving
2007
- // by other thread since it's detached from MMContainer.
2008
- auto res = releaseBackToAllocator (*candidate, RemoveContext::kEviction ,
2009
- /* isNascent */ false );
2010
- XDCHECK (res == ReleaseRes::kReleased );
2021
+ wakeUpWaiters (candidate->getKey (), std::move (evictedToNext));
2022
+ }
2023
+ XDCHECK (!candidate->isExclusive () && !candidate->isMoving ());
2011
2024
2025
+ if (candidate->hasChainedItem ()) {
2026
+ (*stats_.chainedItemEvictions )[pid][cid].inc ();
2012
2027
} else {
2013
- if (candidate->hasChainedItem ()) {
2014
- stats_.evictFailParentAC .inc ();
2015
- } else {
2016
- stats_.evictFailAC .inc ();
2017
- }
2028
+ (*stats_.regularItemEvictions )[pid][cid].inc ();
2018
2029
}
2030
+
2031
+ // it's safe to recycle the item here as there are no more
2032
+ // references and the item could not been marked as moving
2033
+ // by other thread since it's detached from MMContainer.
2034
+ auto res = releaseBackToAllocator (*candidate, RemoveContext::kEviction ,
2035
+ /* isNascent */ false );
2036
+ XDCHECK (res == ReleaseRes::kReleased );
2019
2037
}
2020
2038
return evictions;
2021
2039
}
@@ -2028,7 +2046,7 @@ class CacheAllocator : public CacheBase {
2028
2046
2029
2047
size_t tries = 0 ;
2030
2048
2031
- mmContainer.withPromotionIterator ([&tries, &candidates, &batch, this ](auto &&itr){
2049
+ mmContainer.withPromotionIterator ([&tries, &candidates, &batch, &mmContainer, this ](auto &&itr){
2032
2050
while (candidates.size () < batch && (config_.maxEvictionPromotionHotness == 0 || tries < config_.maxEvictionPromotionHotness ) && itr) {
2033
2051
tries++;
2034
2052
Item* candidate = itr.get ();
@@ -2038,10 +2056,9 @@ class CacheAllocator : public CacheBase {
2038
2056
throw std::runtime_error (" Not supported for chained items" );
2039
2057
}
2040
2058
2041
-
2042
2059
// TODO: only allow it for read-only items?
2043
2060
// or implement mvcc
2044
- if (! candidate->isExpired () && candidate-> markExclusive ( )) {
2061
+ if (candidate->markMoving ( true )) {
2045
2062
candidates.push_back (candidate);
2046
2063
}
2047
2064
@@ -2051,18 +2068,28 @@ class CacheAllocator : public CacheBase {
2051
2068
2052
2069
for (Item *candidate : candidates) {
2053
2070
auto promoted = tryPromoteToNextMemoryTier (*candidate, true );
2054
- auto ref = candidate->unmarkExclusive ();
2055
- if (promoted)
2071
+ if (promoted) {
2056
2072
promotions++;
2057
-
2058
- if (ref == 0u ) {
2059
- // stats_.promotionMoveSuccess.inc();
2060
- auto res = releaseBackToAllocator (*candidate, RemoveContext::kEviction ,
2061
- /* isNascent */ false );
2062
- XDCHECK (res == ReleaseRes::kReleased );
2073
+ removeFromMMContainer (*candidate);
2074
+ XDCHECK (!candidate->isExclusive () && !candidate->isMoving ());
2075
+ // it's safe to recycle the item here as there are no more
2076
+ // references and the item could not been marked as moving
2077
+ // by other thread since it's detached from MMContainer.
2078
+ auto res = releaseBackToAllocator (*candidate, RemoveContext::kEviction ,
2079
+ /* isNascent */ false );
2080
+ XDCHECK (res == ReleaseRes::kReleased );
2081
+ } else {
2082
+ // we failed to allocate a new item, this item is no longer moving
2083
+ auto ref = candidate->unmarkMoving ();
2084
+ if (UNLIKELY (ref == 0 )) {
2085
+ const auto res =
2086
+ releaseBackToAllocator (*candidate,
2087
+ RemoveContext::kNormal , false );
2088
+ XDCHECK (res == ReleaseRes::kReleased );
2089
+ }
2063
2090
}
2091
+
2064
2092
}
2065
-
2066
2093
return promotions;
2067
2094
}
2068
2095
@@ -2173,18 +2200,14 @@ class CacheAllocator : public CacheBase {
2173
2200
std::optional<bool > saveNvmCache ();
2174
2201
void saveRamCache ();
2175
2202
2176
- static bool itemExclusivePredicate (const Item& item) {
2177
- return item.getRefCount () == 0 ;
2203
+ static bool itemSlabMovePredicate (const Item& item) {
2204
+ return item.isMoving () && item. getRefCount () == 0 ;
2178
2205
}
2179
2206
2180
2207
static bool itemExpiryPredicate (const Item& item) {
2181
2208
return item.getRefCount () == 1 && item.isExpired ();
2182
2209
}
2183
2210
2184
- static bool parentEvictForSlabReleasePredicate (const Item& item) {
2185
- return item.getRefCount () == 1 && !item.isExclusive ();
2186
- }
2187
-
2188
2211
std::unique_ptr<Deserializer> createDeserializer ();
2189
2212
2190
2213
// Execute func on each item. `func` can throw exception but must ensure
@@ -2234,8 +2257,9 @@ class CacheAllocator : public CacheBase {
2234
2257
return memoryTierConfigs.size ();
2235
2258
}
2236
2259
2237
- bool addWaitContextForMovingItem (
2238
- folly::StringPiece key, std::shared_ptr<WaitContext<ReadHandle>> waiter);
2260
+ WriteHandle handleWithWaitContextForMovingItem (Item& item);
2261
+
2262
+ size_t wakeUpWaiters (folly::StringPiece key, WriteHandle&& handle);
2239
2263
2240
2264
class MoveCtx {
2241
2265
public:
@@ -2260,6 +2284,8 @@ class CacheAllocator : public CacheBase {
2260
2284
waiters.push_back (std::move (waiter));
2261
2285
}
2262
2286
2287
+ size_t numWaiters () const { return waiters.size (); }
2288
+
2263
2289
private:
2264
2290
// notify all pending waiters that are waiting for the fetch.
2265
2291
void wakeUpWaiters () {
0 commit comments