@@ -1037,7 +1037,8 @@ CacheAllocator<CacheTrait>::acquire(Item* it) {
1037
1037
1038
1038
SCOPE_FAIL { stats_.numRefcountOverflow .inc (); };
1039
1039
1040
- auto failIfMoving = getNumTiers () > 1 ;
1040
+ // TODO: do not block incRef for child items to avoid deadlock
1041
+ auto failIfMoving = getNumTiers () > 1 && !it->isChainedItem ();
1041
1042
auto incRes = incRef (*it, failIfMoving);
1042
1043
if (LIKELY (incRes == RefcountWithFlags::incResult::incOk)) {
1043
1044
return WriteHandle{it, *this };
@@ -3154,7 +3155,8 @@ bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
3154
3155
// a regular item or chained item is synchronized with any potential
3155
3156
// user-side mutation.
3156
3157
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
3158
3160
if (!oldItem.isChainedItem ()) {
3159
3161
syncObj = config_.movingSync (oldItem.getKey ());
3160
3162
} else {
@@ -3253,47 +3255,51 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
3253
3255
Item* evicted;
3254
3256
if (item.isChainedItem ()) {
3255
3257
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
- }
3267
3258
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 ()) {
3272
3271
continue ;
3273
3272
}
3274
3273
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
+ }
3280
3280
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 ;
3286
3285
}
3287
- head = head->getNext (compressor_);
3288
- }
3289
3286
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
+ }
3292
3299
}
3293
3300
}
3294
3301
3295
3302
evicted = &expectedParent;
3296
-
3297
3303
token = createPutToken (*evicted);
3298
3304
if (evicted->markExclusive ()) {
3299
3305
// unmark the child so it will be freed
@@ -3306,6 +3312,9 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
3306
3312
// no other reader can be added to the waiters list
3307
3313
wakeUpWaiters (*evicted, {});
3308
3314
} 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
3309
3318
continue ;
3310
3319
}
3311
3320
} else {
@@ -3340,7 +3349,17 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
3340
3349
XDCHECK (evicted->getRefCount () == 0 );
3341
3350
const auto res =
3342
3351
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
+
3344
3363
return ;
3345
3364
}
3346
3365
}
@@ -3388,11 +3407,15 @@ bool CacheAllocator<CacheTrait>::markMovingForSlabRelease(
3388
3407
bool itemFreed = true ;
3389
3408
bool markedMoving = false ;
3390
3409
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) {
3392
3412
// Since this callback is executed, the item is not yet freed
3393
3413
itemFreed = false ;
3394
3414
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)) {
3396
3419
markedMoving = true ;
3397
3420
}
3398
3421
};
0 commit comments