summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2018-03-30 22:50:21 +0200
committerantirez <antirez@gmail.com>2018-03-30 22:50:21 +0200
commitb85a465c25ac9065a3378bd4b5e8047a606b1f6e (patch)
tree1852b715fba015791de005acbccf96de0f2a1594
parent561039c1258b71a888c64eaa85e671a630312346 (diff)
downloadredis-b85a465c25ac9065a3378bd4b5e8047a606b1f6e.tar.gz
Modules Timer API: timer handling implemented.
-rw-r--r--src/module.c59
1 files changed, 58 insertions, 1 deletions
diff --git a/src/module.c b/src/module.c
index 2ad9e3c7f..4e25f0f37 100644
--- a/src/module.c
+++ b/src/module.c
@@ -4039,7 +4039,7 @@ int RM_GetClusterNodeInfo(RedisModuleCtx *ctx, const char *id, char *ip, char *m
static rax *Timers; /* The radix tree of all the timers sorted by expire. */
long long aeTimer = -1; /* Main event loop (ae.c) timer identifier. */
-typedef int64_t (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data);
+typedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data);
/* The timer descriptor, stored as value in the radix tree. */
typedef struct RedisModuleTimer {
@@ -4048,6 +4048,44 @@ typedef struct RedisModuleTimer {
void *data; /* Private data for the callback. */
} RedisModuleTimer;
+/* This is the timer handler that is called by the main event loop. We schedule
+ * this timer to be called when the nearest of our module timers will expire. */
+int moduleTimerHandler(struct aeEventLoop *eventLoop, long long id, void *clientData) {
+ UNUSED(eventLoop);
+ UNUSED(id);
+ UNUSED(clientData);
+
+ /* To start let's try to fire all the timers already expired. */
+ raxIterator ri;
+ raxStart(&ri,Timers);
+ uint64_t now = ustime();
+ long long next_period = 0;
+ while(1) {
+ raxSeek(&ri,"^",NULL,0);
+ if (!raxNext(&ri)) break;
+ uint64_t expiretime;
+ memcpy(&expiretime,ri.key,sizeof(expiretime));
+ expiretime = ntohu64(expiretime);
+ if (now >= expiretime) {
+ RedisModuleTimer *timer = ri.data;
+ RedisModuleCtx ctx = REDISMODULE_CTX_INIT;
+
+ ctx.module = timer->module;
+ timer->callback(&ctx,timer->data);
+ moduleFreeContext(&ctx);
+ raxRemove(Timers,(unsigned char*)&ri.key,ri.key_len,NULL);
+ zfree(timer);
+ } else {
+ next_period = expiretime-now;
+ break;
+ }
+ }
+ raxStop(&ri);
+
+ /* Reschedule the next timer or cancel it. */
+ return (raxSize(Timers) > 0) ? next_period : AE_NOMORE;
+}
+
/* Create a new timer that will fire after `period` milliseconds, and will call
* the specified function using `data` as argument. The returned timer ID can be
* used to get information from the timer or to stop it before it fires. */
@@ -4071,6 +4109,25 @@ RedisModuleTimerID RM_CreateTimer(RedisModuleCtx *ctx, mstime_t period, RedisMod
/* We need to install the main event loop timer if it's not already
* installed, or we may need to refresh its period if we just installed
* a timer that will expire sooner than any other else. */
+ if (aeTimer != -1) {
+ raxIterator ri;
+ raxStart(&ri,Timers);
+ raxSeek(&ri,"^",NULL,0);
+ raxNext(&ri);
+ if (memcmp(ri.key,&key,sizeof(key)) == 0) {
+ /* This is the first key, we need to re-install the timer according
+ * to the just added event. */
+ aeDeleteTimeEvent(server.el,aeTimer);
+ aeTimer = -1;
+ }
+ raxStop(&ri);
+ }
+
+ /* If we have no main timer (the old one was invalidated, or this is the
+ * first module timer we have), install one. */
+ if (aeTimer == -1)
+ aeTimer = aeCreateTimeEvent(server.el,period,moduleTimerHandler,NULL,NULL);
+
return key;
}