summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormichael-grunder <michael.grunder@gmail.com>2022-08-25 12:08:20 -0700
committermichael-grunder <michael.grunder@gmail.com>2022-08-26 10:14:47 -0700
commit6a3e96ad2149584e441b0e8a5827dc6c2624035b (patch)
tree04fd2496251b711109b73046270f1231a61eb662
parente7afd998f9ce3e6dfaa5e6534f779cab6a1c5a7b (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.md2
-rw-r--r--async.c55
-rw-r--r--async.h5
-rw-r--r--test.c8
4 files changed, 51 insertions, 19 deletions
diff --git a/README.md b/README.md
index b0b2aff..ff50d13 100644
--- a/README.md
+++ b/README.md
@@ -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);
```
diff --git a/async.c b/async.c
index 73b7980..ae097df 100644
--- a/async.c
+++ b/async.c
@@ -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);
}
}
}
diff --git a/async.h b/async.h
index 690b31f..4f94660 100644
--- a/async.h
+++ b/async.h
@@ -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);
diff --git a/test.c b/test.c
index fccaca4..b901752 100644
--- a/test.c
+++ b/test.c
@@ -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);