@@ -988,7 +988,8 @@ CacheAllocator<CacheTrait>::acquire(Item* it) {
988988
989989 SCOPE_FAIL { stats_.numRefcountOverflow .inc (); };
990990
991- auto failIfMoving = getNumTiers () > 1 ;
991+ // TODO: do not block incRef for child items to avoid deadlock
992+ auto failIfMoving = getNumTiers () > 1 && !it->isChainedItem ();
992993 auto incRes = incRef (*it, failIfMoving);
993994 if (LIKELY (incRes == RefcountWithFlags::incResult::incOk)) {
994995 return WriteHandle{it, *this };
@@ -3024,7 +3025,8 @@ bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
30243025 // a regular item or chained item is synchronized with any potential
30253026 // user-side mutation.
30263027 std::unique_ptr<SyncObj> syncObj;
3027- if (config_.movingSync ) {
3028+ if (config_.movingSync && getNumTiers () == 1 ) {
3029+ // TODO: use moving-bit synchronization for single tier as well
30283030 if (!oldItem.isChainedItem ()) {
30293031 syncObj = config_.movingSync (oldItem.getKey ());
30303032 } else {
@@ -3122,47 +3124,51 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
31223124 Item* evicted;
31233125 if (item.isChainedItem ()) {
31243126 auto & expectedParent = item.asChainedItem ().getParentItem (compressor_);
3125- const std::string parentKey = expectedParent.getKey ().str ();
3126- auto l = chainedItemLocks_.lockExclusive (parentKey);
3127-
3128- // check if the child is still in mmContainer and the expected parent is
3129- // valid under the chained item lock.
3130- if (expectedParent.getKey () != parentKey || !item.isInMMContainer () ||
3131- item.isOnlyMoving () ||
3132- &expectedParent != &item.asChainedItem ().getParentItem (compressor_) ||
3133- !expectedParent.isAccessible () || !expectedParent.hasChainedItem ()) {
3134- continue ;
3135- }
31363127
3137- // search if the child is present in the chain
3138- {
3139- auto parentHandle = findInternal (parentKey);
3140- if (!parentHandle || parentHandle != &expectedParent) {
3128+ if (getNumTiers () == 1 ) {
3129+ // TODO: unify this with multi-tier implementation
3130+ // right now, taking a chained item lock here would lead to deadlock
3131+ const std::string parentKey = expectedParent.getKey ().str ();
3132+ auto l = chainedItemLocks_.lockExclusive (parentKey);
3133+
3134+ // check if the child is still in mmContainer and the expected parent is
3135+ // valid under the chained item lock.
3136+ if (expectedParent.getKey () != parentKey || !item.isInMMContainer () ||
3137+ item.isOnlyMoving () ||
3138+ &expectedParent != &item.asChainedItem ().getParentItem (compressor_) ||
3139+ !expectedParent.isAccessible () || !expectedParent.hasChainedItem ()) {
31413140 continue ;
31423141 }
31433142
3144- ChainedItem* head = nullptr ;
3145- { // scope for the handle
3146- auto headHandle = findChainedItem (expectedParent);
3147- head = headHandle ? &headHandle->asChainedItem () : nullptr ;
3148- }
3143+ // search if the child is present in the chain
3144+ {
3145+ auto parentHandle = findInternal (parentKey);
3146+ if (!parentHandle || parentHandle != &expectedParent) {
3147+ continue ;
3148+ }
31493149
3150- bool found = false ;
3151- while (head) {
3152- if (head == &item) {
3153- found = true ;
3154- break ;
3150+ ChainedItem* head = nullptr ;
3151+ { // scope for the handle
3152+ auto headHandle = findChainedItem (expectedParent);
3153+ head = headHandle ? &headHandle->asChainedItem () : nullptr ;
31553154 }
3156- head = head->getNext (compressor_);
3157- }
31583155
3159- if (!found) {
3160- continue ;
3156+ bool found = false ;
3157+ while (head) {
3158+ if (head == &item) {
3159+ found = true ;
3160+ break ;
3161+ }
3162+ head = head->getNext (compressor_);
3163+ }
3164+
3165+ if (!found) {
3166+ continue ;
3167+ }
31613168 }
31623169 }
31633170
31643171 evicted = &expectedParent;
3165-
31663172 token = createPutToken (*evicted);
31673173 if (evicted->markForEviction ()) {
31683174 // unmark the child so it will be freed
@@ -3173,6 +3179,9 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
31733179 // no other reader can be added to the waiters list
31743180 wakeUpWaiters (*evicted, {});
31753181 } else {
3182+ // TODO: potential deadlock with markUseful for parent item
3183+ // for now, we do not block any reader on child items but this
3184+ // should probably be fixed
31763185 continue ;
31773186 }
31783187 } else {
@@ -3204,7 +3213,17 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
32043213 XDCHECK (evicted->getRefCount () == 0 );
32053214 const auto res =
32063215 releaseBackToAllocator (*evicted, RemoveContext::kEviction , false );
3207- XDCHECK (res == ReleaseRes::kReleased );
3216+
3217+ if (getNumTiers () == 1 ) {
3218+ XDCHECK (res == ReleaseRes::kReleased );
3219+ } else {
3220+ const bool isAlreadyFreed =
3221+ !markMovingForSlabRelease (ctx, &item, throttler);
3222+ if (!isAlreadyFreed) {
3223+ continue ;
3224+ }
3225+ }
3226+
32083227 return ;
32093228 }
32103229}
@@ -3252,11 +3271,15 @@ bool CacheAllocator<CacheTrait>::markMovingForSlabRelease(
32523271 bool itemFreed = true ;
32533272 bool markedMoving = false ;
32543273 TierId tid = getTierId (alloc);
3255- const auto fn = [&markedMoving, &itemFreed](void * memory) {
3274+ auto numTiers = getNumTiers ();
3275+ const auto fn = [&markedMoving, &itemFreed, numTiers](void * memory) {
32563276 // Since this callback is executed, the item is not yet freed
32573277 itemFreed = false ;
32583278 Item* item = static_cast <Item*>(memory);
3259- if (item->markMoving (false )) {
3279+ // TODO: for chained items, moving bit is only used to avoid
3280+ // freeing the item prematurely
3281+ auto failIfRefNotZero = numTiers > 1 && !item->isChainedItem ();
3282+ if (item->markMoving (failIfRefNotZero)) {
32603283 markedMoving = true ;
32613284 }
32623285 };
0 commit comments