-
-
Save thesecretmaster/7b3304b9a17112ba809ca788dd51b592 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define REDISMODULE_EXPERIMENTAL_API | |
#include "redismodule.h" | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <pthread.h> | |
#include <unistd.h> | |
/* To compile this code, run: | |
* gcc -Wall -g -fPIC -lc -lm -Og -std=gnu99 -c -o helloblock.o helloblock.c | |
* ld -o helloblock.so helloblock.o -shared -Bsymbolic -lc -lpcre | |
* To reproduce my error, start the server with the module loaded on port N, and run | |
* timeout 0.1 redis-cli -p <N> "hello.block" "4" "5000" | |
* This command will exit silently, but if you look at the redis logs, you will see | |
* the segfault log there. | |
*/ | |
/* Reply callback for blocking command HELLO.BLOCK */ | |
int HelloBlock_Reply(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { | |
REDISMODULE_NOT_USED(argv); | |
REDISMODULE_NOT_USED(argc); | |
int *myint = RedisModule_GetBlockedClientPrivateData(ctx); | |
return RedisModule_ReplyWithLongLong(ctx,*myint); | |
} | |
/* Timeout callback for blocking command HELLO.BLOCK */ | |
int HelloBlock_Timeout(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { | |
REDISMODULE_NOT_USED(argv); | |
REDISMODULE_NOT_USED(argc); | |
return RedisModule_ReplyWithSimpleString(ctx,"Request timedout"); | |
} | |
/* Private data freeing callback for HELLO.BLOCK command. */ | |
void HelloBlock_FreeData(RedisModuleCtx *ctx, void *privdata) { | |
RedisModule_Free(privdata); | |
} | |
/* The thread entry point that actually executes the blocking part | |
* of the command HELLO.BLOCK. */ | |
void *HelloBlock_ThreadMain(void *arg) { | |
void **targ = arg; | |
RedisModuleBlockedClient *bc = targ[0]; | |
long long delay = (unsigned long)targ[1]; | |
RedisModuleCtx *ctx = targ[2]; | |
long long counter = 0; | |
RedisModule_Free(targ); | |
RedisModule_ThreadSafeContextLock(ctx); | |
char *rkeyname = "fookey"; | |
RedisModuleString *keyname = RedisModule_CreateString(ctx, rkeyname, strlen(rkeyname)); | |
RedisModuleKey *key = RedisModule_OpenKey(ctx, keyname, REDISMODULE_READ); | |
while (delay > counter) { | |
sleep(1); | |
RedisModuleString *val; | |
RedisModule_HashGet(key, REDISMODULE_HASH_CFIELDS, "foosubkey", &val); | |
counter = counter + 1; | |
} | |
RedisModule_CloseKey(key); | |
RedisModule_FreeString(ctx, keyname); | |
RedisModule_ThreadSafeContextUnlock(ctx); | |
int *r = RedisModule_Alloc(sizeof(int)); | |
*r = rand(); | |
RedisModule_UnblockClient(bc,r); | |
return NULL; | |
} | |
/* HELLO.BLOCK <delay> <timeout> -- Block for <count> seconds, then reply with | |
* a random number. Timeout is the command timeout, so that you can test | |
* what happens when the delay is greater than the timeout. */ | |
int HelloBlock_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { | |
if (argc != 3) return RedisModule_WrongArity(ctx); | |
long long delay; | |
long long timeout; | |
if (RedisModule_StringToLongLong(argv[1],&delay) != REDISMODULE_OK) { | |
return RedisModule_ReplyWithError(ctx,"ERR invalid count"); | |
} | |
if (RedisModule_StringToLongLong(argv[2],&timeout) != REDISMODULE_OK) { | |
return RedisModule_ReplyWithError(ctx,"ERR invalid count"); | |
} | |
pthread_t tid; | |
RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx,HelloBlock_Reply,HelloBlock_Timeout,HelloBlock_FreeData,timeout); | |
/* Now that we setup a blocking client, we need to pass the control | |
* to the thread. However we need to pass arguments to the thread: | |
* the delay and a reference to the blocked client handle. */ | |
void **targ = RedisModule_Alloc(sizeof(void*)*2); | |
targ[0] = bc; | |
targ[1] = (void*)(unsigned long) delay; | |
targ[2] = RedisModule_GetThreadSafeContext(bc); | |
if (pthread_create(&tid,NULL,HelloBlock_ThreadMain,targ) != 0) { | |
RedisModule_AbortBlock(bc); | |
return RedisModule_ReplyWithError(ctx,"-ERR Can't start thread"); | |
} | |
return REDISMODULE_OK; | |
} | |
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { | |
if (RedisModule_Init(ctx,"helloworld",1,REDISMODULE_APIVER_1) | |
== REDISMODULE_ERR) return REDISMODULE_ERR; | |
if (RedisModule_CreateCommand(ctx,"hello.block", | |
HelloBlock_RedisCommand,"",0,0,0) == REDISMODULE_ERR) | |
return REDISMODULE_ERR; | |
return REDISMODULE_OK; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment