diff options
author | michael-grunder <michael.grunder@gmail.com> | 2022-08-25 12:08:20 -0700 |
---|---|---|
committer | michael-grunder <michael.grunder@gmail.com> | 2022-08-26 10:14:47 -0700 |
commit | 6a3e96ad2149584e441b0e8a5827dc6c2624035b (patch) | |
tree | 04fd2496251b711109b73046270f1231a61eb662 | |
parent | e7afd998f9ce3e6dfaa5e6534f779cab6a1c5a7b (diff) |
Maintain backward compatibiliy withour onConnect callback.
In f69fac7690fb22a7fc19dba61ef70e5f79ccb2e9, 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
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | async.c | 55 | ||||
-rw-r--r-- | async.h | 5 | ||||
-rw-r--r-- | test.c | 8 |
4 files changed, 51 insertions, 19 deletions
@@ -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 api will return `REDIS_ERR`. The function to set the callbacks have the following prototype: ```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 redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); ``` @@ -140,6 +140,7 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) { ac->ev.scheduleTimer = NULL; ac->onConnect = NULL; + ac->onConnectNC = NULL; ac->onDisconnect = NULL; ac->replies.head = NULL; @@ -226,17 +227,34 @@ redisAsyncContext *redisAsyncConnectUnix(const char *path) { return redisAsyncConnectWithOptions(&options); } -int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) { - if (ac->onConnect == NULL) { - ac->onConnect = fn; +static int +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; - /* The common way to detect an established connection is to wait for - * the first write event to be fired. This assumes the related event - * library functions are already set. */ - _EL_ADD_WRITE(ac); - return REDIS_OK; + if (fn) { + ac->onConnect = fn; + } else if (fn_nc) { + ac->onConnectNC = fn_nc; } - return REDIS_ERR; + + /* The common way to detect an established connection is to wait for + * the first write event to be fired. This assumes the related event + * library functions are already set. */ + _EL_ADD_WRITE(ac); + + return REDIS_OK; +} + +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) { @@ -305,14 +323,23 @@ static void __redisRunPushCallback(redisAsyncContext *ac, redisReply *reply) { static void __redisRunConnectCallback(redisAsyncContext *ac, int status) { - if (ac->onConnect) { - if (!(ac->c.flags & REDIS_IN_CALLBACK)) { - ac->c.flags |= REDIS_IN_CALLBACK; + if (ac->onConnect == NULL && ac->onConnectNC == NULL) + return; + + if (!(ac->c.flags & REDIS_IN_CALLBACK)) { + ac->c.flags |= REDIS_IN_CALLBACK; + if (ac->onConnect) { ac->onConnect(ac, status); - ac->c.flags &= ~REDIS_IN_CALLBACK; } else { - /* already in callback */ + ac->onConnectNC(ac, status); + } + ac->c.flags &= ~REDIS_IN_CALLBACK; + } else { + /* already in callback */ + if (ac->onConnect) { ac->onConnect(ac, status); + } else { + ac->onConnectNC(ac, status); } } } @@ -57,7 +57,8 @@ typedef struct redisCallbackList { /* Connection callback prototypes */ 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); /* Context for an async connection to Redis */ @@ -93,6 +94,7 @@ typedef struct redisAsyncContext { /* Called when the first write event was received. */ redisConnectCallback *onConnect; + redisConnectCallbackNC *onConnectNC; /* Regular command callbacks */ redisCallbackList replies; @@ -121,6 +123,7 @@ redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, const char *source_addr); redisAsyncContext *redisAsyncConnectUnix(const char *path); int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); +int redisAsyncSetConnectCallbackNC(redisAsyncContext *ac, redisConnectCallbackNC *fn); int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn); @@ -2008,7 +2008,7 @@ static redisAsyncContext *do_aconnect(struct config config, astest_no testno) { redisOptions options = {0}; memset(&astest, 0, sizeof(astest)); - + astest.testno = testno; astest.connect_status = astest.disconnect_status = -2; @@ -2039,7 +2039,7 @@ static redisAsyncContext *do_aconnect(struct config config, astest_no testno) c->data = &astest; c->dataCleanup = asCleanup; redisPollAttach(c); - redisAsyncSetConnectCallback(c, connectCallback); + redisAsyncSetConnectCallbackNC(c, connectCallback); redisAsyncSetDisconnectCallback(c, disconnectCallback); return c; } @@ -2058,7 +2058,7 @@ static void test_async_polling(struct config config) { int status; redisAsyncContext *c; struct config defaultconfig = config; - + test("Async connect: "); c = do_aconnect(config, ASTEST_CONNECT); assert(c); @@ -2095,7 +2095,7 @@ static void test_async_polling(struct config config) { test_cond(astest.connect_status == REDIS_ERR); config = defaultconfig; } - + /* Test a ping/pong after connection */ test("Async PING/PONG: "); c = do_aconnect(config, ASTEST_PINGPONG); |