summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Noordhuis <pcnoordhuis@gmail.com>2010-12-28 19:19:25 +0100
committerPieter Noordhuis <pcnoordhuis@gmail.com>2010-12-28 19:19:25 +0100
commite3776bfaa65bf40a7c43917c6985f0d83c1afd76 (patch)
tree989ddb4b32e447514c1f48870a037094977c646f
parent3d76f3fe02c8fa4eac1bb2fb266897f69f159ee8 (diff)
Add function to explicitly free an async context
-rw-r--r--async.c56
-rw-r--r--async.h1
-rw-r--r--hiredis.c3
-rw-r--r--hiredis.h4
4 files changed, 49 insertions, 15 deletions
diff --git a/async.c b/async.c
index 6cece18..6d8c18e 100644
--- a/async.c
+++ b/async.c
@@ -151,6 +151,39 @@ static int __redisShiftCallback(redisCallbackList *list, redisCallback *target)
return REDIS_ERR;
}
+/* Helper function to free the context. */
+static void __redisAsyncFree(redisAsyncContext *ac) {
+ redisContext *c = &(ac->c);
+
+ /* Clear callback list */
+ while (__redisShiftCallback(&ac->replies,NULL) == REDIS_OK);
+
+ /* Signal event lib to clean up */
+ if (ac->evCleanup) ac->evCleanup(ac->_adapter_data);
+
+ /* Execute callback with proper status */
+ if (ac->onDisconnect && (c->flags & REDIS_CONNECTED))
+ ac->onDisconnect(ac, (ac->err == 0) ? REDIS_OK : REDIS_ERR);
+
+ /* Cleanup self */
+ redisFree(c);
+}
+
+/* Free's the async context. When REDIS_CONNECTED is set, it could only have
+ * been called from a command callback so we need to let control return to
+ * redisProcessCallbacks() before free'ing can continue.
+ *
+ * When REDIS_CONNECTED is not set, the first write event has not yet fired and
+ * we can free immediately. */
+void redisAsyncFree(redisAsyncContext *ac) {
+ redisContext *c = &(ac->c);
+ if (c->flags & REDIS_CONNECTED) {
+ c->flags |= REDIS_FREEING;
+ } else {
+ __redisAsyncFree(ac);
+ }
+}
+
/* Tries to do a clean disconnect from Redis, meaning it stops new commands
* from being issued, but tries to flush the output buffer and execute
* callbacks for all remaining replies.
@@ -167,13 +200,11 @@ void redisAsyncDisconnect(redisAsyncContext *ac) {
static void __redisAsyncDisconnect(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisCallback cb;
- int status;
/* Make sure error is accessible if there is any */
__redisAsyncCopyError(ac);
- status = (ac->err == 0) ? REDIS_OK : REDIS_ERR;
- if (status == REDIS_OK) {
+ if (ac->err == 0) {
/* When the connection is cleanly disconnected, there should not
* be pending callbacks. */
assert(__redisShiftCallback(&ac->replies,NULL) == REDIS_ERR);
@@ -188,14 +219,7 @@ static void __redisAsyncDisconnect(redisAsyncContext *ac) {
}
}
- /* Signal event lib to clean up */
- if (ac->evCleanup) ac->evCleanup(ac->_adapter_data);
-
- /* Execute callback with proper status */
- if (ac->onDisconnect) ac->onDisconnect(ac,status);
-
- /* Cleanup self */
- redisFree(c);
+ __redisAsyncFree(ac);
}
void redisProcessCallbacks(redisAsyncContext *ac) {
@@ -225,6 +249,12 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
} else {
c->fn->freeObject(reply);
}
+
+ /* Proceed with free'ing when redisAsyncFree() was called. */
+ if (c->flags & REDIS_FREEING) {
+ __redisAsyncFree(ac);
+ return;
+ }
}
/* Disconnect when there was an error reading the reply */
@@ -281,8 +311,8 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
redisContext *c = &(ac->c);
redisCallback cb;
- /* Don't accept new commands when the connection is lazily closed. */
- if (c->flags & REDIS_DISCONNECTING) return REDIS_ERR;
+ /* Don't accept new commands when the connection is about to be closed. */
+ if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR;
__redisAppendCommand(c,cmd,len);
/* Store callback */
diff --git a/async.h b/async.h
index 3ebea4e..9819e0d 100644
--- a/async.h
+++ b/async.h
@@ -95,6 +95,7 @@ int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFun
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
void redisAsyncDisconnect(redisAsyncContext *ac);
+void redisAsyncFree(redisAsyncContext *ac);
/* Handle read/write events */
void redisAsyncHandleRead(redisAsyncContext *ac);
diff --git a/hiredis.c b/hiredis.c
index d4cad7c..970b45a 100644
--- a/hiredis.c
+++ b/hiredis.c
@@ -809,8 +809,7 @@ static redisContext *redisContextInit() {
}
void redisFree(redisContext *c) {
- /* Disconnect before free'ing if not yet disconnected. */
- if (c->flags & REDIS_CONNECTED)
+ if (c->fd > 0)
close(c->fd);
if (c->errstr != NULL)
sdsfree(c->errstr);
diff --git a/hiredis.h b/hiredis.h
index 1412a34..4e6e8a3 100644
--- a/hiredis.h
+++ b/hiredis.h
@@ -64,6 +64,10 @@
* should be terminated once all replies have been read. */
#define REDIS_DISCONNECTING 0x4
+/* Flag specific to the async API which means that the context should be clean
+ * up as soon as possible. */
+#define REDIS_FREEING 0x8
+
#define REDIS_REPLY_STRING 1
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3