Skip to content

Instantly share code, notes, and snippets.

@thesecretmaster
Created December 17, 2018 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thesecretmaster/7b3304b9a17112ba809ca788dd51b592 to your computer and use it in GitHub Desktop.
Save thesecretmaster/7b3304b9a17112ba809ca788dd51b592 to your computer and use it in GitHub Desktop.
#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