Commit 47e1f771 authored by Pieter Noordhuis's avatar Pieter Noordhuis
Browse files

Refactor internal function flow and add redisAppendCommand* family

parent 298e9325
...@@ -730,15 +730,6 @@ int redisBufferRead(redisContext *c) { ...@@ -730,15 +730,6 @@ int redisBufferRead(redisContext *c) {
return REDIS_OK; return REDIS_OK;
} }
int redisGetReply(redisContext *c, void **reply) {
if (redisReplyReaderGetReply(c->reader,reply) == REDIS_ERR) {
/* Copy the (protocol) error from the reader to the context. */
c->error = sdsnew(((redisReader*)c->reader)->error);
return REDIS_ERR;
}
return REDIS_OK;
}
/* Write the output buffer to the socket. /* Write the output buffer to the socket.
* *
* Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
...@@ -774,69 +765,137 @@ int redisBufferWrite(redisContext *c, int *done) { ...@@ -774,69 +765,137 @@ int redisBufferWrite(redisContext *c, int *done) {
return REDIS_OK; return REDIS_OK;
} }
static int redisCommandWriteBlock(redisContext *c, void **reply, char *str, size_t len) { /* Internal helper function to try and get a reply from the reader,
* or set an error in the context otherwise. */
static int __redisGetReply(redisContext *c, void **reply) {
if (redisReplyReaderGetReply(c->reader,reply) == REDIS_ERR) {
/* Copy the (protocol) error from the reader to the context. */
c->error = sdsnew(((redisReader*)c->reader)->error);
return REDIS_ERR;
}
return REDIS_OK;
}
int redisGetReply(redisContext *c, void **reply) {
int wdone = 0; int wdone = 0;
void *aux = NULL; void *aux = NULL;
assert(c->flags & REDIS_BLOCK);
c->obuf = sdscatlen(c->obuf,str,len);
/* Write until done. */ /* Try to read pending replies */
do { if (__redisGetReply(c,&aux) == REDIS_ERR) {
if (redisBufferWrite(c,&wdone) == REDIS_ERR) return REDIS_ERR;
return REDIS_ERR; } else {
} while (!wdone); /* Return immediately if there was a pending reply */
if (aux != NULL) return REDIS_OK;
}
/* Read until there is a reply. */ /* For the blocking context, flush output buffer and read reply */
do { if (c->flags & REDIS_BLOCK) {
if (redisBufferRead(c) == REDIS_ERR) /* Write until done */
return REDIS_ERR; do {
if (redisGetReply(c,&aux) == REDIS_ERR) if (redisBufferWrite(c,&wdone) == REDIS_ERR)
return REDIS_ERR; return REDIS_ERR;
} while (aux == NULL); } while (!wdone);
/* Set reply object. */ /* Read until there is a reply */
if (reply != NULL) do {
*reply = aux; if (redisBufferRead(c) == REDIS_ERR)
return REDIS_ERR;
if (__redisGetReply(c,&aux) == REDIS_ERR)
return REDIS_ERR;
} while (aux == NULL);
/* Set reply object */
if (reply != NULL) *reply = aux;
}
return REDIS_OK; return REDIS_OK;
} }
static int redisCommandWriteNonBlock(redisContext *c, char *str, size_t len) {
assert(!(c->flags & REDIS_BLOCK)); /* Helper function for the redisAppendCommand* family of functions.
c->obuf = sdscatlen(c->obuf,str,len); *
* Write a formatted command to the output buffer. When this family
* is used, you need to call redisGetReply yourself to retrieve
* the reply (or replies in pub/sub).
*/
void __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
c->obuf = sdscatlen(c->obuf,cmd,len);
/* Fire write callback */ /* Fire write callback */
if (c->cbCommand.fn != NULL) if (c->cbCommand.fn != NULL)
c->cbCommand.fn(c,c->cbCommand.privdata); c->cbCommand.fn(c,c->cbCommand.privdata);
}
return REDIS_OK; void redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
char *cmd;
int len;
len = redisvFormatCommand(&cmd,format,ap);
__redisAppendCommand(c,cmd,len);
free(cmd);
}
void redisAppendCommand(redisContext *c, const char *format, ...) {
va_list ap;
va_start(ap,format);
redisvAppendCommand(c,format,ap);
va_end(ap);
}
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
char *cmd;
int len;
len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
__redisAppendCommand(c,cmd,len);
free(cmd);
} }
/* Write a formatted command to the output buffer. If the given context is /* Helper function for the redisCommand* family of functions.
*
* Write a formatted command to the output buffer. If the given context is
* blocking, immediately read the reply into the "reply" pointer. When the * blocking, immediately read the reply into the "reply" pointer. When the
* context is non-blocking, the "reply" pointer will not be used and the * context is non-blocking, the "reply" pointer will not be used and the
* command is simply appended to the write buffer. * command is simply appended to the write buffer.
* *
* Returns the reply when a reply was succesfully retrieved. Returns NULL * Returns the reply when a reply was succesfully retrieved. Returns NULL
* otherwise. When NULL is returned in a blocking context, the error field * otherwise. When NULL is returned in a blocking context, the error field
* in the context will be set. */ * in the context will be set.
void *redisCommand(redisContext *c, const char *format, ...) { */
va_list ap; static void *__redisCommand(redisContext *c, char *cmd, size_t len) {
void *aux = NULL;
__redisAppendCommand(c,cmd,len);
if (c->flags & REDIS_BLOCK) {
if (redisGetReply(c,&aux) == REDIS_OK)
return aux;
return NULL;
}
return NULL;
}
void *redisvCommand(redisContext *c, const char *format, va_list ap) {
char *cmd; char *cmd;
int len; int len;
void *reply = NULL; void *reply = NULL;
va_start(ap,format);
len = redisvFormatCommand(&cmd,format,ap); len = redisvFormatCommand(&cmd,format,ap);
reply = __redisCommand(c,cmd,len);
free(cmd);
return reply;
}
void *redisCommand(redisContext *c, const char *format, ...) {
va_list ap;
void *reply = NULL;
va_start(ap,format);
reply = redisvCommand(c,format,ap);
va_end(ap); va_end(ap);
return reply;
}
if (c->flags & REDIS_BLOCK) { void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
if (redisCommandWriteBlock(c,&reply,cmd,len) == REDIS_OK) { char *cmd;
free(cmd); int len;
return reply; void *reply = NULL;
} len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
} else { reply = __redisCommand(c,cmd,len);
redisCommandWriteNonBlock(c,cmd,len);
}
free(cmd); free(cmd);
return NULL; return reply;
} }
...@@ -120,6 +120,11 @@ void redisDisconnect(redisContext *c); ...@@ -120,6 +120,11 @@ void redisDisconnect(redisContext *c);
void redisFree(redisContext *c); void redisFree(redisContext *c);
int redisBufferRead(redisContext *c); int redisBufferRead(redisContext *c);
int redisBufferWrite(redisContext *c, int *done); int redisBufferWrite(redisContext *c, int *done);
/* In a blocking context, this function first checks if there are unconsumed
* replies to return and returns one if so. Otherwise, it flushes the output
* buffer to the socket and reads until it has a reply. In a non-blocking
* context, it will return unconsumed replies until there are no more. */
int redisGetReply(redisContext *c, void **reply); int redisGetReply(redisContext *c, void **reply);
/* The disconnect callback is called *immediately* when redisDisconnect() /* The disconnect callback is called *immediately* when redisDisconnect()
...@@ -136,11 +141,19 @@ void redisSetCommandCallback(redisContext *c, redisContextCallbackFn *fn, void * ...@@ -136,11 +141,19 @@ void redisSetCommandCallback(redisContext *c, redisContextCallbackFn *fn, void *
* release resources that depend/use the redisContext that is being free'd. */ * release resources that depend/use the redisContext that is being free'd. */
void redisSetFreeCallback(redisContext *c, redisContextCallbackFn *fn, void *privdata); void redisSetFreeCallback(redisContext *c, redisContextCallbackFn *fn, void *privdata);
/* Issue a command to Redis. In a blocking context, it returns the reply. When /* Write a command to the output buffer. Use these functions in blocking mode
* an error occurs, it returns NULL and you should read redisContext->error * to get a pipeline of commands. */
* to find out what's wrong. In a non-blocking context, it has the same effect void redisvAppendCommand(redisContext *c, const char *format, va_list ap);
* as calling redisCommandWithCallback() with a NULL callback, and will always void redisAppendCommand(redisContext *c, const char *format, ...);
* return NULL. */ void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
/* Issue a command to Redis. In a blocking context, it is identical to calling
* redisAppendCommand, followed by redisGetReply. The function will return
* NULL if there was an error in performing the request, otherwise it will
* return the reply. In a non-blocking context, it is identical to calling
* only redisAppendCommand and will always return NULL. */
void *redisvCommand(redisContext *c, const char *format, va_list ap);
void *redisCommand(redisContext *c, const char *format, ...); void *redisCommand(redisContext *c, const char *format, ...);
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
#endif #endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment