@@ -1696,12 +1696,20 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
16961696 chainedItem_
16971697 ? &toRecycle_->asChainedItem ().getParentItem (compressor_)
16981698 : nullptr ;
1699+ auto l_ = chainedItem_
1700+ ? chainedItemLocks_.tryLockExclusive (toRecycleParent_->getKey ())
1701+ : chainedItemLocks_.tryLockExclusive (toRecycle_->getKey ());
1702+ if (chainedItem_ && !l_) {
1703+ ++itr;
1704+ continue ;
1705+ }
16991706 Item* candidate_;
17001707 Item* syncItem_;
17011708 // sync on the parent item for chained items to move to next tier
17021709 if (!lastTier && chainedItem_) {
17031710 syncItem_ = toRecycleParent_;
17041711 candidate_ = toRecycle_;
1712+ XDCHECK (l_);
17051713 } else if (lastTier && chainedItem_) {
17061714 candidate_ = toRecycleParent_;
17071715 syncItem_ = toRecycleParent_;
@@ -1756,7 +1764,6 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
17561764
17571765 XDCHECK (toRecycle);
17581766 XDCHECK (candidate);
1759-
17601767 auto evictedToNext = lastTier ? nullptr
17611768 : tryEvictToNextMemoryTier (*candidate, false );
17621769 if (!evictedToNext) {
@@ -1921,9 +1928,9 @@ template <typename CacheTrait>
19211928typename CacheAllocator<CacheTrait>::WriteHandle
19221929CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
19231930 TierId tid, PoolId pid, Item& item, bool fromBgThread) {
1924- XDCHECK (item.isMoving () || item. isChainedItem ());
1925- XDCHECK (item. getRefCount () == 0 ||
1926- (item. isChainedItem () && item. getRefCount () == 1 ));
1931+ if (item.isInMMContainer ()) {
1932+ return WriteHandle{}; // the parent item changed
1933+ }
19271934 if (item.isExpired ()) {
19281935 accessContainer_->remove (item);
19291936 item.unmarkMoving ();
@@ -1943,6 +1950,7 @@ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
19431950 XDCHECK (item.isInMMContainer ()); // parent changed
19441951 return WriteHandle{};
19451952 }
1953+ XDCHECK (item.isChainedItem () && item.getRefCount () == 1 );
19461954 // safe to acquire handle for a moving Item
19471955 auto incRes = incRef (*parentItem, false );
19481956 XDCHECK (incRes == RefcountWithFlags::incResult::incOk);
@@ -1952,6 +1960,10 @@ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
19521960 allocateChainedItemInternalTier (parentHandle,
19531961 item.getSize (), nextTier);
19541962 } else {
1963+ // this assert can fail if parent changed -
1964+ // if item is no longer chained item (how does that happen??)
1965+ XDCHECK (item.isMoving ());
1966+ XDCHECK (item.getRefCount () == 0 );
19551967 newItemHdl = allocateInternalTier (nextTier, pid,
19561968 item.getKey (),
19571969 item.getSize (),
@@ -3242,7 +3254,7 @@ typename RefcountWithFlags::Value CacheAllocator<CacheTrait>::unmarkMovingAndWak
32423254template <typename CacheTrait>
32433255bool CacheAllocator<CacheTrait>::moveForSlabRelease(
32443256 const SlabReleaseContext& ctx, Item& oldItem, util::Throttler& throttler) {
3245- if (!config_.moveCb ) {
3257+ if (!config_.moveCb || oldItem. isChainedItem () ) {
32463258 return false ;
32473259 }
32483260
@@ -3467,16 +3479,19 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
34673479 auto startTime = util::getCurrentTimeSec ();
34683480
34693481 while (true ) {
3470- XDCHECK (item.isMoving ());
3482+ // we can't rely on an item being marked moving because
3483+ // it may have previously been a chained item
34713484 stats_.numEvictionAttempts .inc ();
34723485
34733486 if (shutDownInProgress_) {
3474- auto ref = unmarkMovingAndWakeUpWaiters (item, {});
3475- allocator_[getTierId (item)]->abortSlabRelease (ctx);
3476- throw exception::SlabReleaseAborted (
3477- folly::sformat (" Slab Release aborted while trying to evict"
3478- " Item: {} Pool: {}, Class: {}." ,
3479- item.toString (), ctx.getPoolId (), ctx.getClassId ()));
3487+ if (item.isMoving ()) {
3488+ auto ref = unmarkMovingAndWakeUpWaiters (item, {});
3489+ allocator_[getTierId (item)]->abortSlabRelease (ctx);
3490+ throw exception::SlabReleaseAborted (
3491+ folly::sformat (" Slab Release aborted while trying to evict"
3492+ " Item: {} Pool: {}, Class: {}." ,
3493+ item.toString (), ctx.getPoolId (), ctx.getClassId ()));
3494+ }
34803495 }
34813496 throttleWith (throttler, [&] {
34823497 XLOGF (WARN,
@@ -3508,69 +3523,85 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
35083523 if (item.isChainedItem ()) {
35093524 auto & expectedParent = item.asChainedItem ().getParentItem (compressor_);
35103525
3511- if (getNumTiers () == 1 ) {
3512- // TODO: unify this with multi-tier implementation
3513- // right now, taking a chained item lock here would lead to deadlock
3514- const std::string parentKey = expectedParent.getKey ().str ();
3515- auto l = chainedItemLocks_.lockExclusive (parentKey);
3516-
3517- // check if the child is still in mmContainer and the expected parent is
3518- // valid under the chained item lock.
3519- if (expectedParent.getKey () != parentKey || !item.isInMMContainer () ||
3520- item.isOnlyMoving () ||
3521- &expectedParent != &item.asChainedItem ().getParentItem (compressor_) ||
3522- !expectedParent.isAccessible () || !expectedParent.hasChainedItem ()) {
3526+ const std::string parentKey = expectedParent.getKey ().str ();
3527+ auto l = chainedItemLocks_.tryLockExclusive (parentKey);
3528+
3529+ // check if the child is still in mmContainer and the expected parent is
3530+ // valid under the chained item lock.
3531+ // if (!l) {
3532+ // continue;
3533+ // } else if (
3534+ // || expectedParent.getKey() != parentKey ||
3535+ // &expectedParent != &item.asChainedItem().getParentItem(compressor_) ||
3536+ // !expectedParent.isAccessible() || !expectedParent.hasChainedItem()) {
3537+ // }
3538+ if (!l) {
3539+ continue ;
3540+ } else if (expectedParent.getKey () != parentKey) {
3541+ continue ;
3542+ } else if (&expectedParent != &item.asChainedItem ().getParentItem (compressor_)) {
3543+ continue ;
3544+ } else if (!expectedParent.isAccessible ()) {
3545+ continue ;
3546+ } else if (!expectedParent.hasChainedItem ()) {
3547+ continue ;
3548+ }
3549+
3550+ // search if the child is present in the chain
3551+ {
3552+ auto parentHandle = findInternal (parentKey);
3553+ if (!parentHandle || parentHandle != &expectedParent) {
35233554 continue ;
35243555 }
35253556
3526- // search if the child is present in the chain
3527- {
3528- auto parentHandle = findInternal (parentKey);
3529- if (!parentHandle || parentHandle != &expectedParent) {
3530- continue ;
3531- }
3532-
3533- ChainedItem* head = nullptr ;
3534- { // scope for the handle
3535- auto headHandle = findChainedItem (expectedParent);
3536- head = headHandle ? &headHandle->asChainedItem () : nullptr ;
3537- }
3557+ ChainedItem* head = nullptr ;
3558+ { // scope for the handle
3559+ auto headHandle = findChainedItem (expectedParent);
3560+ head = headHandle ? &headHandle->asChainedItem () : nullptr ;
3561+ }
35383562
3539- bool found = false ;
3540- while (head) {
3541- if (head == &item) {
3542- found = true ;
3543- break ;
3544- }
3545- head = head->getNext (compressor_);
3563+ bool found = false ;
3564+ while (head) {
3565+ if (head == &item) {
3566+ found = true ;
3567+ break ;
35463568 }
3569+ head = head->getNext (compressor_);
3570+ }
35473571
3548- if (!found) {
3549- continue ;
3550- }
3572+ if (!found) {
3573+ continue ;
35513574 }
35523575 }
3576+
35533577
35543578 evicted = &expectedParent;
35553579 token = createPutToken (*evicted);
35563580 if (evicted->markForEviction ()) {
35573581 // unmark the child so it will be freed
3558- item.unmarkMoving ();
3582+ // TODO entire chain just gets evicted since moveForSlabRelease
3583+ // returns false
3584+ XDCHECK (!item.isMoving ());
35593585 unlinkItemForEviction (*evicted);
35603586 // wake up any readers that wait for the move to complete
35613587 // it's safe to do now, as we have the item marked exclusive and
35623588 // no other reader can be added to the waiters list
35633589 wakeUpWaiters (*evicted, {});
35643590 } else {
3565- // TODO: potential deadlock with markUseful for parent item
3566- // for now, we do not block any reader on child items but this
3567- // should probably be fixed
35683591 continue ;
35693592 }
35703593 } else {
35713594 evicted = &item;
35723595
35733596 token = createPutToken (*evicted);
3597+ if (!evicted->isMoving ()) {
3598+ // this previously could have been a chained
3599+ // item so we need to ensure that it is moving
3600+ if (!evicted->markMoving ()) {
3601+ continue ;
3602+ }
3603+ }
3604+ XDCHECK (evicted->isMoving ());
35743605 if (evicted->markForEvictionWhenMoving ()) {
35753606 unlinkItemForEviction (*evicted);
35763607 wakeUpWaiters (*evicted, {});
@@ -3655,8 +3686,7 @@ bool CacheAllocator<CacheTrait>::markMovingForSlabRelease(
36553686 bool itemFreed = true ;
36563687 bool markedMoving = false ;
36573688 TierId tid = getTierId (alloc);
3658- auto numTiers = getNumTiers ();
3659- const auto fn = [&markedMoving, &itemFreed, numTiers](void * memory) {
3689+ const auto fn = [&markedMoving, &itemFreed](void * memory) {
36603690 // Since this callback is executed, the item is not yet freed
36613691 itemFreed = false ;
36623692 Item* item = static_cast <Item*>(memory);
0 commit comments