@@ -988,7 +988,8 @@ CacheAllocator<CacheTrait>::acquire(Item* it) {
988
988
989
989
SCOPE_FAIL { stats_.numRefcountOverflow .inc (); };
990
990
991
- auto failIfMoving = getNumTiers () > 1 ;
991
+ // TODO: do not block incRef for child items to avoid deadlock
992
+ auto failIfMoving = getNumTiers () > 1 && !it->isChainedItem ();
992
993
auto incRes = incRef (*it, failIfMoving);
993
994
if (LIKELY (incRes == RefcountWithFlags::incResult::incOk)) {
994
995
return WriteHandle{it, *this };
@@ -3050,7 +3051,8 @@ bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
3050
3051
// a regular item or chained item is synchronized with any potential
3051
3052
// user-side mutation.
3052
3053
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
3054
3056
if (!oldItem.isChainedItem ()) {
3055
3057
syncObj = config_.movingSync (oldItem.getKey ());
3056
3058
} else {
@@ -3148,47 +3150,51 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
3148
3150
Item* evicted;
3149
3151
if (item.isChainedItem ()) {
3150
3152
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
- }
3162
3153
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 ()) {
3167
3166
continue ;
3168
3167
}
3169
3168
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
+ }
3175
3175
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 ;
3181
3180
}
3182
- head = head->getNext (compressor_);
3183
- }
3184
3181
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
+ }
3187
3194
}
3188
3195
}
3189
3196
3190
3197
evicted = &expectedParent;
3191
-
3192
3198
token = createPutToken (*evicted);
3193
3199
if (evicted->markForEviction ()) {
3194
3200
// unmark the child so it will be freed
@@ -3199,6 +3205,9 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
3199
3205
// no other reader can be added to the waiters list
3200
3206
wakeUpWaiters (*evicted, {});
3201
3207
} 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
3202
3211
continue ;
3203
3212
}
3204
3213
} else {
@@ -3230,7 +3239,17 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
3230
3239
XDCHECK (evicted->getRefCount () == 0 );
3231
3240
const auto res =
3232
3241
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
+
3234
3253
return ;
3235
3254
}
3236
3255
}
@@ -3278,11 +3297,15 @@ bool CacheAllocator<CacheTrait>::markMovingForSlabRelease(
3278
3297
bool itemFreed = true ;
3279
3298
bool markedMoving = false ;
3280
3299
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) {
3282
3302
// Since this callback is executed, the item is not yet freed
3283
3303
itemFreed = false ;
3284
3304
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)) {
3286
3309
markedMoving = true ;
3287
3310
}
3288
3311
};
0 commit comments