Fix module unblock crash due to no timeout_callback (#13017)

The block timeout is passed in the test case, but we do not pass
in the timeout_callback, and it will crash when unlocking. In this
case, in moduleBlockedClientTimedOut we will check timeout_callback.
There is the stack:
```
beforeSleep
blockedBeforeSleep
handleBlockedClientsTimeout
checkBlockedClientTimeout
unblockClientOnTimeout
replyToBlockedClientTimedOut
moduleBlockedClientTimedOut
-- timeout_callback is NULL, invalidFunctionWasCalled
bc->timeout_callback(&ctx,(void**)c->argv,c->argc);
```
This commit is contained in:
Binbin 2024-01-31 15:28:50 +08:00 committed by GitHub
parent f469dd8ca6
commit 74a6e48a3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 26 additions and 4 deletions

View File

@ -8438,7 +8438,12 @@ void moduleBlockedClientTimedOut(client *c, int from_module) {
if (!from_module)
prev_error_replies = server.stat_total_error_replies;
bc->timeout_callback(&ctx,(void**)c->argv,c->argc);
if (bc->timeout_callback) {
/* In theory, the user should always pass the timeout handler as an
* argument, but better to be safe than sorry. */
bc->timeout_callback(&ctx,(void**)c->argv,c->argc);
}
moduleFreeContext(&ctx);
if (!from_module)

View File

@ -629,16 +629,23 @@ static void timer_callback(RedisModuleCtx *ctx, void *data)
RedisModule_FreeThreadSafeContext(reply_ctx);
}
/* unblock_by_timer <period_ms> <timeout_ms>
* period_ms is the period of the timer.
* timeout_ms is the blocking timeout. */
int unblock_by_timer(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
if (argc != 2)
if (argc != 3)
return RedisModule_WrongArity(ctx);
long long period;
long long timeout;
if (RedisModule_StringToLongLong(argv[1],&period) != REDISMODULE_OK)
return RedisModule_ReplyWithError(ctx,"ERR invalid period");
if (RedisModule_StringToLongLong(argv[2],&timeout) != REDISMODULE_OK) {
return RedisModule_ReplyWithError(ctx,"ERR invalid timeout");
}
RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, 0);
RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, timeout);
RedisModule_CreateTimer(ctx, period, timer_callback, bc);
return REDISMODULE_OK;
}

View File

@ -278,7 +278,17 @@ foreach call_type {nested normal} {
}
test {Unblock by timer} {
assert_match "OK" [r unblock_by_timer 100]
# When the client is unlock, we will get the OK reply.
assert_match "OK" [r unblock_by_timer 100 0]
}
test {block time is shorter than timer period} {
# This command does not have the reply.
set rd [redis_deferring_client]
$rd unblock_by_timer 100 10
# Wait for the client to unlock.
after 120
$rd close
}
test "Unload the module - blockedclient" {