@@ -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 };
@@ -3050,7 +3051,8 @@ bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
30503051 // a regular item or chained item is synchronized with any potential
30513052 // user-side mutation.
30523053 std::unique_ptr<SyncObj> syncObj;
3053- if (config_.movingSync ) {
3054+ if (config_.movingSync && getNumTiers () == 1 ) {
3055+ // TODO: use moving-bit synchronization for single tier as well
30543056 if (!oldItem.isChainedItem ()) {
30553057 syncObj = config_.movingSync (oldItem.getKey ());
30563058 } else {
@@ -3148,47 +3150,51 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
31483150 Item* evicted;
31493151 if (item.isChainedItem ()) {
31503152 auto & expectedParent = item.asChainedItem ().getParentItem (compressor_);
3151- const std::string parentKey = expectedParent.getKey ().str ();
3152- auto l = chainedItemLocks_.lockExclusive (parentKey);
3153-
3154- // check if the child is still in mmContainer and the expected parent is
3155- // valid under the chained item lock.
3156- if (expectedParent.getKey () != parentKey || !item.isInMMContainer () ||
3157- item.isOnlyMoving () ||
3158- &expectedParent != &item.asChainedItem ().getParentItem (compressor_) ||
3159- !expectedParent.isAccessible () || !expectedParent.hasChainedItem ()) {
3160- continue ;
3161- }
31623153
3163- // search if the child is present in the chain
3164- {
3165- auto parentHandle = findInternal (parentKey);
3166- if (!parentHandle || parentHandle != &expectedParent) {
3154+ if (getNumTiers () == 1 ) {
3155+ // TODO: unify this with multi-tier implementation
3156+ // right now, taking a chained item lock here would lead to deadlock
3157+ const std::string parentKey = expectedParent.getKey ().str ();
3158+ auto l = chainedItemLocks_.lockExclusive (parentKey);
3159+
3160+ // check if the child is still in mmContainer and the expected parent is
3161+ // valid under the chained item lock.
3162+ if (expectedParent.getKey () != parentKey || !item.isInMMContainer () ||
3163+ item.isOnlyMoving () ||
3164+ &expectedParent != &item.asChainedItem ().getParentItem (compressor_) ||
3165+ !expectedParent.isAccessible () || !expectedParent.hasChainedItem ()) {
31673166 continue ;
31683167 }
31693168
3170- ChainedItem* head = nullptr ;
3171- { // scope for the handle
3172- auto headHandle = findChainedItem (expectedParent);
3173- head = headHandle ? &headHandle->asChainedItem () : nullptr ;
3174- }
3169+ // search if the child is present in the chain
3170+ {
3171+ auto parentHandle = findInternal (parentKey);
3172+ if (!parentHandle || parentHandle != &expectedParent) {
3173+ continue ;
3174+ }
31753175
3176- bool found = false ;
3177- while (head) {
3178- if (head == &item) {
3179- found = true ;
3180- break ;
3176+ ChainedItem* head = nullptr ;
3177+ { // scope for the handle
3178+ auto headHandle = findChainedItem (expectedParent);
3179+ head = headHandle ? &headHandle->asChainedItem () : nullptr ;
31813180 }
3182- head = head->getNext (compressor_);
3183- }
31843181
3185- if (!found) {
3186- continue ;
3182+ bool found = false ;
3183+ while (head) {
3184+ if (head == &item) {
3185+ found = true ;
3186+ break ;
3187+ }
3188+ head = head->getNext (compressor_);
3189+ }
3190+
3191+ if (!found) {
3192+ continue ;
3193+ }
31873194 }
31883195 }
31893196
31903197 evicted = &expectedParent;
3191-
31923198 token = createPutToken (*evicted);
31933199 if (evicted->markForEviction ()) {
31943200 // unmark the child so it will be freed
@@ -3199,6 +3205,9 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
31993205 // no other reader can be added to the waiters list
32003206 wakeUpWaiters (*evicted, {});
32013207 } else {
3208+ // TODO: potential deadlock with markUseful for parent item
3209+ // for now, we do not block any reader on child items but this
3210+ // should probably be fixed
32023211 continue ;
32033212 }
32043213 } else {
@@ -3230,7 +3239,17 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
32303239 XDCHECK (evicted->getRefCount () == 0 );
32313240 const auto res =
32323241 releaseBackToAllocator (*evicted, RemoveContext::kEviction , false );
3233- XDCHECK (res == ReleaseRes::kReleased );
3242+
3243+ if (getNumTiers () == 1 ) {
3244+ XDCHECK (res == ReleaseRes::kReleased );
3245+ } else {
3246+ const bool isAlreadyFreed =
3247+ !markMovingForSlabRelease (ctx, &item, throttler);
3248+ if (!isAlreadyFreed) {
3249+ continue ;
3250+ }
3251+ }
3252+
32343253 return ;
32353254 }
32363255}
@@ -3278,11 +3297,15 @@ bool CacheAllocator<CacheTrait>::markMovingForSlabRelease(
32783297 bool itemFreed = true ;
32793298 bool markedMoving = false ;
32803299 TierId tid = getTierId (alloc);
3281- const auto fn = [&markedMoving, &itemFreed](void * memory) {
3300+ auto numTiers = getNumTiers ();
3301+ const auto fn = [&markedMoving, &itemFreed, numTiers](void * memory) {
32823302 // Since this callback is executed, the item is not yet freed
32833303 itemFreed = false ;
32843304 Item* item = static_cast <Item*>(memory);
3285- if (item->markMoving (false )) {
3305+ // TODO: for chained items, moving bit is only used to avoid
3306+ // freeing the item prematurely
3307+ auto failIfRefNotZero = numTiers > 1 && !item->isChainedItem ();
3308+ if (item->markMoving (failIfRefNotZero)) {
32863309 markedMoving = true ;
32873310 }
32883311 };
0 commit comments