Maintain backward compatibiliy withour onConnect callback.

In f69fac7690, our async onConnect
callback was improved to take a non-const redisAsyncContext allowing it
to be reentrant.

Unfortunately, this is a breaking change we can't make until hiredis
v2.0.0.

This commit creates a separate callback member and corresponding
function that allows us to use the new functionality, while maintaining
our existing API for legacy code.

Fixes #1086
master
michael-grunder 2022-08-25 12:08:20 -07:00
parent e7afd998f9
commit 6a3e96ad21
4 changed files with 51 additions and 19 deletions

View File

@ -372,6 +372,8 @@ the disconnect callback is a good point to do so.
Setting the connect or disconnect callbacks can only be done once per context. For subsequent calls the Setting the connect or disconnect callbacks can only be done once per context. For subsequent calls the
api will return `REDIS_ERR`. The function to set the callbacks have the following prototype: api will return `REDIS_ERR`. The function to set the callbacks have the following prototype:
```c ```c
/* Alternatively you can use redisAsyncSetConnectCallbackNC which will be passed a non-const
redisAsyncContext* on invocation (e.g. allowing writes to the privdata member). */
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
``` ```

37
async.c
View File

@ -140,6 +140,7 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
ac->ev.scheduleTimer = NULL; ac->ev.scheduleTimer = NULL;
ac->onConnect = NULL; ac->onConnect = NULL;
ac->onConnectNC = NULL;
ac->onDisconnect = NULL; ac->onDisconnect = NULL;
ac->replies.head = NULL; ac->replies.head = NULL;
@ -226,17 +227,34 @@ redisAsyncContext *redisAsyncConnectUnix(const char *path) {
return redisAsyncConnectWithOptions(&options); return redisAsyncConnectWithOptions(&options);
} }
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) { static int
if (ac->onConnect == NULL) { redisAsyncSetConnectCallbackImpl(redisAsyncContext *ac, redisConnectCallback *fn,
redisConnectCallbackNC *fn_nc)
{
/* If either are already set, this is an error */
if (ac->onConnect || ac->onConnectNC)
return REDIS_ERR;
if (fn) {
ac->onConnect = fn; ac->onConnect = fn;
} else if (fn_nc) {
ac->onConnectNC = fn_nc;
}
/* The common way to detect an established connection is to wait for /* The common way to detect an established connection is to wait for
* the first write event to be fired. This assumes the related event * the first write event to be fired. This assumes the related event
* library functions are already set. */ * library functions are already set. */
_EL_ADD_WRITE(ac); _EL_ADD_WRITE(ac);
return REDIS_OK; return REDIS_OK;
} }
return REDIS_ERR;
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
return redisAsyncSetConnectCallbackImpl(ac, fn, NULL);
}
int redisAsyncSetConnectCallbackNC(redisAsyncContext *ac, redisConnectCallbackNC *fn) {
return redisAsyncSetConnectCallbackImpl(ac, NULL, fn);
} }
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) { int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) {
@ -305,14 +323,23 @@ static void __redisRunPushCallback(redisAsyncContext *ac, redisReply *reply) {
static void __redisRunConnectCallback(redisAsyncContext *ac, int status) static void __redisRunConnectCallback(redisAsyncContext *ac, int status)
{ {
if (ac->onConnect) { if (ac->onConnect == NULL && ac->onConnectNC == NULL)
return;
if (!(ac->c.flags & REDIS_IN_CALLBACK)) { if (!(ac->c.flags & REDIS_IN_CALLBACK)) {
ac->c.flags |= REDIS_IN_CALLBACK; ac->c.flags |= REDIS_IN_CALLBACK;
if (ac->onConnect) {
ac->onConnect(ac, status); ac->onConnect(ac, status);
} else {
ac->onConnectNC(ac, status);
}
ac->c.flags &= ~REDIS_IN_CALLBACK; ac->c.flags &= ~REDIS_IN_CALLBACK;
} else { } else {
/* already in callback */ /* already in callback */
if (ac->onConnect) {
ac->onConnect(ac, status); ac->onConnect(ac, status);
} else {
ac->onConnectNC(ac, status);
} }
} }
} }

View File

@ -57,7 +57,8 @@ typedef struct redisCallbackList {
/* Connection callback prototypes */ /* Connection callback prototypes */
typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status); typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
typedef void (redisConnectCallback)(struct redisAsyncContext*, int status); typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status);
typedef void (redisConnectCallbackNC)(struct redisAsyncContext *, int status);
typedef void(redisTimerCallback)(void *timer, void *privdata); typedef void(redisTimerCallback)(void *timer, void *privdata);
/* Context for an async connection to Redis */ /* Context for an async connection to Redis */
@ -93,6 +94,7 @@ typedef struct redisAsyncContext {
/* Called when the first write event was received. */ /* Called when the first write event was received. */
redisConnectCallback *onConnect; redisConnectCallback *onConnect;
redisConnectCallbackNC *onConnectNC;
/* Regular command callbacks */ /* Regular command callbacks */
redisCallbackList replies; redisCallbackList replies;
@ -121,6 +123,7 @@ redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
const char *source_addr); const char *source_addr);
redisAsyncContext *redisAsyncConnectUnix(const char *path); redisAsyncContext *redisAsyncConnectUnix(const char *path);
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
int redisAsyncSetConnectCallbackNC(redisAsyncContext *ac, redisConnectCallbackNC *fn);
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn); redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn);

2
test.c
View File

@ -2039,7 +2039,7 @@ static redisAsyncContext *do_aconnect(struct config config, astest_no testno)
c->data = &astest; c->data = &astest;
c->dataCleanup = asCleanup; c->dataCleanup = asCleanup;
redisPollAttach(c); redisPollAttach(c);
redisAsyncSetConnectCallback(c, connectCallback); redisAsyncSetConnectCallbackNC(c, connectCallback);
redisAsyncSetDisconnectCallback(c, disconnectCallback); redisAsyncSetDisconnectCallback(c, disconnectCallback);
return c; return c;
} }