Skip to content

Commit 15cf5ea

Browse files
committed
changed termination of periodic timer to be callback based
1 parent 74a6c8c commit 15cf5ea

File tree

2 files changed

+49
-19
lines changed

2 files changed

+49
-19
lines changed

rmutil/periodic.c

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
typedef struct RMUtilTimer {
77
RMutilTimerFunc cb;
8+
RMUtilTimerTerminationFunc onTerm;
89
void *privdata;
910
struct timespec interval;
1011
pthread_t thread;
@@ -45,16 +46,34 @@ static void *rmutilTimer_Loop(void *ctx) {
4546
// It's up to the user to decide whether automemory is active there
4647
if (rctx) RedisModule_FreeThreadSafeContext(rctx);
4748
}
49+
if (rc == EINVAL) {
50+
perror("Error waiting for condition");
51+
break;
52+
}
53+
}
54+
55+
// call the termination callback if needed
56+
if (tm->onTerm != NULL) {
57+
tm->onTerm(tm->privdata);
4858
}
49-
// RedisModule_Log(tm->redisCtx, "notice", "Timer cancelled");
59+
60+
// free resources associated with the timer
61+
pthread_cond_destroy(&tm->cond);
62+
free(tm);
5063

5164
return NULL;
5265
}
5366

54-
RMUtilTimer *RMUtil_NewPeriodicTimer(RMutilTimerFunc cb, void *privdata, struct timespec interval) {
67+
/* set a new frequency for the timer. This will take effect AFTER the next trigger */
68+
void RMUtilTimer_SetInterval(struct RMUtilTimer *t, struct timespec newInterval) {
69+
t->interval = newInterval;
70+
}
71+
72+
RMUtilTimer *RMUtil_NewPeriodicTimer(RMutilTimerFunc cb, RMUtilTimerTerminationFunc onTerm,
73+
void *privdata, struct timespec interval) {
5574
RMUtilTimer *ret = malloc(sizeof(*ret));
5675
*ret = (RMUtilTimer){
57-
.privdata = privdata, .interval = interval, .cb = cb,
76+
.privdata = privdata, .interval = interval, .cb = cb, .onTerm = onTerm,
5877
};
5978
pthread_cond_init(&ret->cond, NULL);
6079
pthread_mutex_init(&ret->lock, NULL);
@@ -63,14 +82,6 @@ RMUtilTimer *RMUtil_NewPeriodicTimer(RMutilTimerFunc cb, void *privdata, struct
6382
return ret;
6483
}
6584

66-
int RMUtilTimer_Stop(RMUtilTimer *t) {
67-
int rc;
68-
if (0 == (rc = pthread_cond_signal(&t->cond))) {
69-
rc = pthread_join(t->thread, NULL);
70-
}
71-
return rc;
72-
}
73-
74-
void RMUtilTimer_Free(RMUtilTimer *t) {
75-
free(t);
85+
int RMUtilTimer_Terminate(struct RMUtilTimer *t) {
86+
return pthread_cond_signal(&t->cond);
7687
}

rmutil/periodic.h

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,34 @@ struct RMUtilTimer;
1313
* pre-existing private data */
1414
typedef void (*RMutilTimerFunc)(RedisModuleCtx *ctx, void *privdata);
1515

16+
typedef void (*RMUtilTimerTerminationFunc)(void *privdata);
17+
1618
/* Create and start a new periodic timer. Each timer has its own thread and can only be run and
1719
* stopped once. The timer runs `cb` every `interval` with `privdata` passed to the callback. */
18-
struct RMUtilTimer *RMUtil_NewPeriodicTimer(RMutilTimerFunc cb, void *privdata,
19-
struct timespec interval);
20+
struct RMUtilTimer *RMUtil_NewPeriodicTimer(RMutilTimerFunc cb, RMUtilTimerTerminationFunc onTerm,
21+
void *privdata, struct timespec interval);
22+
23+
/* set a new frequency for the timer. This will take effect AFTER the next trigger */
24+
void RMUtilTimer_SetInterval(struct RMUtilTimer *t, struct timespec newInterval);
2025

21-
/* Stop the timer loop. This should return immediately and join the thread */
22-
int RMUtilTimer_Stop(struct RMUtilTimer *t);
26+
/* Stop the timer loop, call the termination callbck to free up any resources linked to the timer,
27+
* and free the timer after stopping.
28+
*
29+
* This function doesn't wait for the thread to terminate, as it may cause a race condition if the
30+
* timer's callback is waiting for the redis global lock.
31+
* Instead you should make sure any resources are freed by the callback after the thread loop is
32+
* finished.
33+
*
34+
* The timer is freed automatically, so the callback doesn't need to do anything about it.
35+
* The callback gets the timer's associated privdata as its argument.
36+
*
37+
* If no callback is specified we do not free up privdata. If privdata is NULL we still call the
38+
* callback, as it may log stuff or free global resources.
39+
*/
40+
int RMUtilTimer_Terminate(struct RMUtilTimer *t);
2341

24-
/* Free the timer context. The caller should be responsible for freeing the private data at this
42+
/* DEPRECATED - do not use this function (well now you can't), use terminate instead
43+
Free the timer context. The caller should be responsible for freeing the private data at this
2544
* point */
26-
void RMUtilTimer_Free(struct RMUtilTimer *t);
45+
// void RMUtilTimer_Free(struct RMUtilTimer *t);
2746
#endif

0 commit comments

Comments
 (0)