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