From e3776bfaa65bf40a7c43917c6985f0d83c1afd76 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Tue, 28 Dec 2010 19:19:25 +0100 Subject: [PATCH] Add function to explicitly free an async context --- async.c | 56 ++++++++++++++++++++++++++++++++++++++++++------------- async.h | 1 + hiredis.c | 3 +-- hiredis.h | 4 ++++ 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