Skip to content

Commit

Permalink
Fix module assertion crash when timer and timeout are unlocked in the…
Browse files Browse the repository at this point in the history
… same event loop
  • Loading branch information
enjoy-binbin committed Jan 31, 2024
1 parent 74a6e48 commit 36264d1
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/blocked.c
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,11 @@ static void moduleUnblockClientOnKey(client *c, robj *key) {
* we want to remove the pending flag to indicate we already responded to the
* command with timeout reply. */
void unblockClientOnTimeout(client *c) {
if (c->bstate.btype == BLOCKED_MODULE) {
/* The client has been unlocked (in the moduleUnblocked list), return ASAP. */
if (moduleClientUnblocked(c)) return;
}

replyToBlockedClientTimedOut(c);
if (c->flags & CLIENT_PENDING_COMMAND)
c->flags &= ~CLIENT_PENDING_COMMAND;
Expand Down
7 changes: 7 additions & 0 deletions src/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -7698,6 +7698,13 @@ void RM_LatencyAddSample(const char *event, mstime_t latency) {
* https://redis.io/topics/modules-blocking-ops.
* -------------------------------------------------------------------------- */

/* Returns 1 if the client already in the moduleUnblocked list, 0 otherwise. */
int moduleClientUnblocked(client *c) {
RedisModuleBlockedClient *bc = c->bstate.module_blocked_handle;

return bc->unblocked == 1;
}

/* This is called from blocked.c in order to unblock a client: may be called
* for multiple reasons while the client is in the middle of being blocked
* because the client is terminated, but is also called for cleanup when a
Expand Down
1 change: 1 addition & 0 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -2532,6 +2532,7 @@ const char *moduleTypeModuleName(moduleType *mt);
const char *moduleNameFromCommand(struct redisCommand *cmd);
void moduleFreeContext(struct RedisModuleCtx *ctx);
void moduleCallCommandUnblockedHandler(client *c);
int moduleClientUnblocked(client *c);
void unblockClientFromModule(client *c);
void moduleHandleBlockedClients(void);
void moduleBlockedClientTimedOut(client *c, int from_module);
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/moduleapi/blockedclient.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,12 @@ foreach call_type {nested normal} {
after 120
$rd close
}

test {block time is equal to timer period} {
# These time is equal, they will be unlocked in the same event loop,
# when the client is unlock, we will get the OK reply from timer.
assert_match "OK" [r unblock_by_timer 100 100]
}

test "Unload the module - blockedclient" {
assert_equal {OK} [r module unload blockedclient]
Expand Down

0 comments on commit 36264d1

Please sign in to comment.