Skip to content

Commit e013126

Browse files
committed
Prevent deadlock in RM_ThreadSafeContextLock() when triggered as part of a module callback in a server thread
1 parent cd9514f commit e013126

File tree

2 files changed

+22
-2
lines changed

2 files changed

+22
-2
lines changed

src/module.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4895,14 +4895,17 @@ void RM_FreeThreadSafeContext(RedisModuleCtx *ctx) {
48954895
zfree(ctx);
48964896
}
48974897

4898+
static bool g_fModuleThread = false;
48984899
/* Acquire the server lock before executing a thread safe API call.
48994900
* This is not needed for `RedisModule_Reply*` calls when there is
49004901
* a blocked client connected to the thread safe context. */
49014902
void RM_ThreadSafeContextLock(RedisModuleCtx *ctx) {
49024903
UNUSED(ctx);
4903-
moduleAcquireGIL(FALSE /*fServerThread*/);
4904-
if (serverTL == nullptr)
4904+
if (serverTL == nullptr) {
49054905
serverTL = &g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN]; // arbitrary module threads get the main thread context
4906+
g_fModuleThread = true;
4907+
}
4908+
moduleAcquireGIL(FALSE /*fServerThread*/);
49064909
}
49074910

49084911
/* Release the server lock after a thread safe API call was executed. */
@@ -4911,10 +4914,20 @@ void RM_ThreadSafeContextUnlock(RedisModuleCtx *ctx) {
49114914
moduleReleaseGIL(FALSE /*fServerThread*/);
49124915
}
49134916

4917+
// A module may be triggered synchronously in a non-module context. In this scenario we don't lock again
4918+
// as the server thread acquisition is sufficient. If we did try to lock we would deadlock
4919+
static bool FModuleCallBackLock(bool fServerThread)
4920+
{
4921+
return !fServerThread && aeThreadOwnsLock() && !g_fModuleThread && s_cAcquisitionsServer > 0;
4922+
}
49144923
void moduleAcquireGIL(int fServerThread) {
49154924
std::unique_lock<std::mutex> lock(s_mutex);
49164925
int *pcheck = fServerThread ? &s_cAcquisitionsModule : &s_cAcquisitionsServer;
49174926

4927+
if (FModuleCallBackLock(fServerThread)) {
4928+
return;
4929+
}
4930+
49184931
while (*pcheck > 0)
49194932
s_cv.wait(lock);
49204933

@@ -4932,6 +4945,10 @@ void moduleAcquireGIL(int fServerThread) {
49324945
void moduleReleaseGIL(int fServerThread) {
49334946
std::unique_lock<std::mutex> lock(s_mutex);
49344947

4948+
if (FModuleCallBackLock(fServerThread)) {
4949+
return;
4950+
}
4951+
49354952
if (fServerThread)
49364953
{
49374954
--s_cAcquisitionsServer;

tests/modules/hooks.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* POSSIBILITY OF SUCH DAMAGE.
3131
*/
3232

33+
#define REDISMODULE_EXPERIMENTAL_API 1
3334
#include "redismodule.h"
3435
#include <stdio.h>
3536
#include <string.h>
@@ -143,10 +144,12 @@ void flushdbCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void
143144
{
144145
REDISMODULE_NOT_USED(e);
145146

147+
RedisModule_ThreadSafeContextLock(ctx);
146148
RedisModuleFlushInfo *fi = data;
147149
char *keyname = (sub == REDISMODULE_SUBEVENT_FLUSHDB_START) ?
148150
"flush-start" : "flush-end";
149151
LogNumericEvent(ctx, keyname, fi->dbnum);
152+
RedisModule_ThreadSafeContextUnlock(ctx);
150153
}
151154

152155
void roleChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)

0 commit comments

Comments
 (0)