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:
parent
f469dd8ca6
commit
74a6e48a3d
@ -8438,7 +8438,12 @@ void moduleBlockedClientTimedOut(client *c, int from_module) {
|
|||||||
if (!from_module)
|
if (!from_module)
|
||||||
prev_error_replies = server.stat_total_error_replies;
|
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);
|
moduleFreeContext(&ctx);
|
||||||
|
|
||||||
if (!from_module)
|
if (!from_module)
|
||||||
|
@ -629,16 +629,23 @@ static void timer_callback(RedisModuleCtx *ctx, void *data)
|
|||||||
RedisModule_FreeThreadSafeContext(reply_ctx);
|
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)
|
int unblock_by_timer(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||||
{
|
{
|
||||||
if (argc != 2)
|
if (argc != 3)
|
||||||
return RedisModule_WrongArity(ctx);
|
return RedisModule_WrongArity(ctx);
|
||||||
|
|
||||||
long long period;
|
long long period;
|
||||||
|
long long timeout;
|
||||||
if (RedisModule_StringToLongLong(argv[1],&period) != REDISMODULE_OK)
|
if (RedisModule_StringToLongLong(argv[1],&period) != REDISMODULE_OK)
|
||||||
return RedisModule_ReplyWithError(ctx,"ERR invalid period");
|
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);
|
RedisModule_CreateTimer(ctx, period, timer_callback, bc);
|
||||||
return REDISMODULE_OK;
|
return REDISMODULE_OK;
|
||||||
}
|
}
|
||||||
|
@ -278,7 +278,17 @@ foreach call_type {nested normal} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test {Unblock by timer} {
|
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" {
|
test "Unload the module - blockedclient" {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user