@@ -1037,7 +1037,8 @@ CacheAllocator<CacheTrait>::acquire(Item* it) {
10371037
10381038 SCOPE_FAIL { stats_.numRefcountOverflow .inc (); };
10391039
1040- auto failIfMoving = getNumTiers () > 1 ;
1040+ // TODO: do not block incRef for child items to avoid deadlock
1041+ auto failIfMoving = getNumTiers () > 1 && !it->isChainedItem ();
10411042 auto incRes = incRef (*it, failIfMoving);
10421043 if (LIKELY (incRes == RefcountWithFlags::incResult::incOk)) {
10431044 return WriteHandle{it, *this };
@@ -3154,7 +3155,8 @@ bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
31543155 // a regular item or chained item is synchronized with any potential
31553156 // user-side mutation.
31563157 std::unique_ptr<SyncObj> syncObj;
3157- if (config_.movingSync ) {
3158+ if (config_.movingSync && getNumTiers () == 1 ) {
3159+ // TODO: use moving-bit synchronization for single tier as well
31583160 if (!oldItem.isChainedItem ()) {
31593161 syncObj = config_.movingSync (oldItem.getKey ());
31603162 } else {
@@ -3253,47 +3255,51 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
32533255 Item* evicted;
32543256 if (item.isChainedItem ()) {
32553257 auto & expectedParent = item.asChainedItem ().getParentItem (compressor_);
3256- const std::string parentKey = expectedParent.getKey ().str ();
3257- auto l = chainedItemLocks_.lockExclusive (parentKey);
3258-
3259- // check if the child is still in mmContainer and the expected parent is
3260- // valid under the chained item lock.
3261- if (expectedParent.getKey () != parentKey || !item.isInMMContainer () ||
3262- item.isOnlyMoving () ||
3263- &expectedParent != &item.asChainedItem ().getParentItem (compressor_) ||
3264- !expectedParent.isAccessible () || !expectedParent.hasChainedItem ()) {
3265- continue ;
3266- }
32673258
3268- // search if the child is present in the chain
3269- {
3270- auto parentHandle = findInternal (parentKey);
3271- if (!parentHandle || parentHandle != &expectedParent) {
3259+ if (getNumTiers () == 1 ) {
3260+ // TODO: unify this with multi-tier implementation
3261+ // right now, taking a chained item lock here would lead to deadlock
3262+ const std::string parentKey = expectedParent.getKey ().str ();
3263+ auto l = chainedItemLocks_.lockExclusive (parentKey);
3264+
3265+ // check if the child is still in mmContainer and the expected parent is
3266+ // valid under the chained item lock.
3267+ if (expectedParent.getKey () != parentKey || !item.isInMMContainer () ||
3268+ item.isOnlyMoving () ||
3269+ &expectedParent != &item.asChainedItem ().getParentItem (compressor_) ||
3270+ !expectedParent.isAccessible () || !expectedParent.hasChainedItem ()) {
32723271 continue ;
32733272 }
32743273
3275- ChainedItem* head = nullptr ;
3276- { // scope for the handle
3277- auto headHandle = findChainedItem (expectedParent);
3278- head = headHandle ? &headHandle->asChainedItem () : nullptr ;
3279- }
3274+ // search if the child is present in the chain
3275+ {
3276+ auto parentHandle = findInternal (parentKey);
3277+ if (!parentHandle || parentHandle != &expectedParent) {
3278+ continue ;
3279+ }
32803280
3281- bool found = false ;
3282- while (head) {
3283- if (head == &item) {
3284- found = true ;
3285- break ;
3281+ ChainedItem* head = nullptr ;
3282+ { // scope for the handle
3283+ auto headHandle = findChainedItem (expectedParent);
3284+ head = headHandle ? &headHandle->asChainedItem () : nullptr ;
32863285 }
3287- head = head->getNext (compressor_);
3288- }
32893286
3290- if (!found) {
3291- continue ;
3287+ bool found = false ;
3288+ while (head) {
3289+ if (head == &item) {
3290+ found = true ;
3291+ break ;
3292+ }
3293+ head = head->getNext (compressor_);
3294+ }
3295+
3296+ if (!found) {
3297+ continue ;
3298+ }
32923299 }
32933300 }
32943301
32953302 evicted = &expectedParent;
3296-
32973303 token = createPutToken (*evicted);
32983304 if (evicted->markExclusive ()) {
32993305 // unmark the child so it will be freed
@@ -3306,6 +3312,9 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
33063312 // no other reader can be added to the waiters list
33073313 wakeUpWaiters (*evicted, {});
33083314 } else {
3315+ // TODO: potential deadlock with markUseful for parent item
3316+ // for now, we do not block any reader on child items but this
3317+ // should probably be fixed
33093318 continue ;
33103319 }
33113320 } else {
@@ -3340,7 +3349,17 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
33403349 XDCHECK (evicted->getRefCount () == 0 );
33413350 const auto res =
33423351 releaseBackToAllocator (*evicted, RemoveContext::kEviction , false );
3343- XDCHECK (res == ReleaseRes::kReleased );
3352+
3353+ if (getNumTiers () == 1 ) {
3354+ XDCHECK (res == ReleaseRes::kReleased );
3355+ } else {
3356+ const bool isAlreadyFreed =
3357+ !markMovingForSlabRelease (ctx, &item, throttler);
3358+ if (!isAlreadyFreed) {
3359+ continue ;
3360+ }
3361+ }
3362+
33443363 return ;
33453364 }
33463365}
@@ -3388,11 +3407,15 @@ bool CacheAllocator<CacheTrait>::markMovingForSlabRelease(
33883407 bool itemFreed = true ;
33893408 bool markedMoving = false ;
33903409 TierId tid = getTierId (alloc);
3391- const auto fn = [&markedMoving, &itemFreed](void * memory) {
3410+ auto numTiers = getNumTiers ();
3411+ const auto fn = [&markedMoving, &itemFreed, numTiers](void * memory) {
33923412 // Since this callback is executed, the item is not yet freed
33933413 itemFreed = false ;
33943414 Item* item = static_cast <Item*>(memory);
3395- if (item->markMoving (false )) {
3415+ // TODO: for chained items, moving bit is only used to avoid
3416+ // freeing the item prematurely
3417+ auto failIfRefNotZero = numTiers > 1 && !item->isChainedItem ();
3418+ if (item->markMoving (failIfRefNotZero)) {
33963419 markedMoving = true ;
33973420 }
33983421 };
0 commit comments