@@ -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 };
@@ -3024,7 +3025,8 @@ bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
3024
3025
// a regular item or chained item is synchronized with any potential
3025
3026
// user-side mutation.
3026
3027
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
3028
3030
if (!oldItem.isChainedItem ()) {
3029
3031
syncObj = config_.movingSync (oldItem.getKey ());
3030
3032
} else {
@@ -3122,47 +3124,51 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
3122
3124
Item* evicted;
3123
3125
if (item.isChainedItem ()) {
3124
3126
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
- }
3136
3127
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 ()) {
3141
3140
continue ;
3142
3141
}
3143
3142
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
+ }
3149
3149
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 ;
3155
3154
}
3156
- head = head->getNext (compressor_);
3157
- }
3158
3155
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
+ }
3161
3168
}
3162
3169
}
3163
3170
3164
3171
evicted = &expectedParent;
3165
-
3166
3172
token = createPutToken (*evicted);
3167
3173
if (evicted->markForEviction ()) {
3168
3174
// unmark the child so it will be freed
@@ -3173,6 +3179,9 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
3173
3179
// no other reader can be added to the waiters list
3174
3180
wakeUpWaiters (*evicted, {});
3175
3181
} 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
3176
3185
continue ;
3177
3186
}
3178
3187
} else {
@@ -3204,7 +3213,17 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
3204
3213
XDCHECK (evicted->getRefCount () == 0 );
3205
3214
const auto res =
3206
3215
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
+
3208
3227
return ;
3209
3228
}
3210
3229
}
@@ -3252,11 +3271,15 @@ bool CacheAllocator<CacheTrait>::markMovingForSlabRelease(
3252
3271
bool itemFreed = true ;
3253
3272
bool markedMoving = false ;
3254
3273
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) {
3256
3276
// Since this callback is executed, the item is not yet freed
3257
3277
itemFreed = false ;
3258
3278
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)) {
3260
3283
markedMoving = true ;
3261
3284
}
3262
3285
};
0 commit comments