@@ -987,7 +987,8 @@ CacheAllocator<CacheTrait>::acquire(Item* it) {
987987
988988 SCOPE_FAIL { stats_.numRefcountOverflow .inc (); };
989989
990- auto failIfMoving = getNumTiers () > 1 ;
990+ // TODO: do not block incRef for child items to avoid deadlock
991+ auto failIfMoving = getNumTiers () > 1 && !it->isChainedItem ();
991992 auto incRes = incRef (*it, failIfMoving);
992993 if (LIKELY (incRes == RefcountWithFlags::incResult::incOk)) {
993994 return WriteHandle{it, *this };
@@ -3051,7 +3052,8 @@ bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
30513052 // a regular item or chained item is synchronized with any potential
30523053 // user-side mutation.
30533054 std::unique_ptr<SyncObj> syncObj;
3054- if (config_.movingSync ) {
3055+ if (config_.movingSync && getNumTiers () == 1 ) {
3056+ // TODO: use moving-bit synchronization for single tier as well
30553057 if (!oldItem.isChainedItem ()) {
30563058 syncObj = config_.movingSync (oldItem.getKey ());
30573059 } else {
@@ -3149,47 +3151,51 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
31493151 Item* evicted;
31503152 if (item.isChainedItem ()) {
31513153 auto & expectedParent = item.asChainedItem ().getParentItem (compressor_);
3152- const std::string parentKey = expectedParent.getKey ().str ();
3153- auto l = chainedItemLocks_.lockExclusive (parentKey);
3154-
3155- // check if the child is still in mmContainer and the expected parent is
3156- // valid under the chained item lock.
3157- if (expectedParent.getKey () != parentKey || !item.isInMMContainer () ||
3158- item.isOnlyMoving () ||
3159- &expectedParent != &item.asChainedItem ().getParentItem (compressor_) ||
3160- !expectedParent.isAccessible () || !expectedParent.hasChainedItem ()) {
3161- continue ;
3162- }
31633154
3164- // search if the child is present in the chain
3165- {
3166- auto parentHandle = findInternal (parentKey);
3167- if (!parentHandle || parentHandle != &expectedParent) {
3155+ if (getNumTiers () == 1 ) {
3156+ // TODO: unify this with multi-tier implementation
3157+ // right now, taking a chained item lock here would lead to deadlock
3158+ const std::string parentKey = expectedParent.getKey ().str ();
3159+ auto l = chainedItemLocks_.lockExclusive (parentKey);
3160+
3161+ // check if the child is still in mmContainer and the expected parent is
3162+ // valid under the chained item lock.
3163+ if (expectedParent.getKey () != parentKey || !item.isInMMContainer () ||
3164+ item.isOnlyMoving () ||
3165+ &expectedParent != &item.asChainedItem ().getParentItem (compressor_) ||
3166+ !expectedParent.isAccessible () || !expectedParent.hasChainedItem ()) {
31683167 continue ;
31693168 }
31703169
3171- ChainedItem* head = nullptr ;
3172- { // scope for the handle
3173- auto headHandle = findChainedItem (expectedParent);
3174- head = headHandle ? &headHandle->asChainedItem () : nullptr ;
3175- }
3170+ // search if the child is present in the chain
3171+ {
3172+ auto parentHandle = findInternal (parentKey);
3173+ if (!parentHandle || parentHandle != &expectedParent) {
3174+ continue ;
3175+ }
31763176
3177- bool found = false ;
3178- while (head) {
3179- if (head == &item) {
3180- found = true ;
3181- break ;
3177+ ChainedItem* head = nullptr ;
3178+ { // scope for the handle
3179+ auto headHandle = findChainedItem (expectedParent);
3180+ head = headHandle ? &headHandle->asChainedItem () : nullptr ;
31823181 }
3183- head = head->getNext (compressor_);
3184- }
31853182
3186- if (!found) {
3187- continue ;
3183+ bool found = false ;
3184+ while (head) {
3185+ if (head == &item) {
3186+ found = true ;
3187+ break ;
3188+ }
3189+ head = head->getNext (compressor_);
3190+ }
3191+
3192+ if (!found) {
3193+ continue ;
3194+ }
31883195 }
31893196 }
31903197
31913198 evicted = &expectedParent;
3192-
31933199 token = createPutToken (*evicted);
31943200 if (evicted->markForEviction ()) {
31953201 // unmark the child so it will be freed
@@ -3200,6 +3206,9 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
32003206 // no other reader can be added to the waiters list
32013207 wakeUpWaiters (*evicted, {});
32023208 } else {
3209+ // TODO: potential deadlock with markUseful for parent item
3210+ // for now, we do not block any reader on child items but this
3211+ // should probably be fixed
32033212 continue ;
32043213 }
32053214 } else {
@@ -3231,7 +3240,17 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
32313240 XDCHECK (evicted->getRefCount () == 0 );
32323241 const auto res =
32333242 releaseBackToAllocator (*evicted, RemoveContext::kEviction , false );
3234- XDCHECK (res == ReleaseRes::kReleased );
3243+
3244+ if (getNumTiers () == 1 ) {
3245+ XDCHECK (res == ReleaseRes::kReleased );
3246+ } else {
3247+ const bool isAlreadyFreed =
3248+ !markMovingForSlabRelease (ctx, &item, throttler);
3249+ if (!isAlreadyFreed) {
3250+ continue ;
3251+ }
3252+ }
3253+
32353254 return ;
32363255 }
32373256}
@@ -3279,11 +3298,15 @@ bool CacheAllocator<CacheTrait>::markMovingForSlabRelease(
32793298 bool itemFreed = true ;
32803299 bool markedMoving = false ;
32813300 TierId tid = getTierId (alloc);
3282- const auto fn = [&markedMoving, &itemFreed](void * memory) {
3301+ auto numTiers = getNumTiers ();
3302+ const auto fn = [&markedMoving, &itemFreed, numTiers](void * memory) {
32833303 // Since this callback is executed, the item is not yet freed
32843304 itemFreed = false ;
32853305 Item* item = static_cast <Item*>(memory);
3286- if (item->markMoving (false )) {
3306+ // TODO: for chained items, moving bit is only used to avoid
3307+ // freeing the item prematurely
3308+ auto failIfRefNotZero = numTiers > 1 && !item->isChainedItem ();
3309+ if (item->markMoving (failIfRefNotZero)) {
32873310 markedMoving = true ;
32883311 }
32893312 };
0 commit comments