From 96510ce86ade991a7dc968587c3c618cc9bf5a88 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 7 Oct 2010 17:48:03 +0200 Subject: [PATCH] Factor out reusable include for libevent --- Makefile | 6 +-- extra/hiredis/libevent.h | 95 ++++++++++++++++++++++++++++++++++++++++ libevent-example.c | 88 +++++-------------------------------- 3 files changed, 110 insertions(+), 79 deletions(-) create mode 100644 extra/hiredis/libevent.h diff --git a/Makefile b/Makefile index 486f897..d3d4f28 100644 --- a/Makefile +++ b/Makefile @@ -58,14 +58,14 @@ hiredis-%: %.o ${DYLIBNAME} test: hiredis-test ./hiredis-test -libevent-example: libevent-example.c ${DYLIBNAME} - $(CC) -o $@ $(CCOPT) $(DEBUG) -L. -lhiredis -levent libevent-example.c +libevent-example: extra/hiredis/libevent.h libevent-example.c ${DYLIBNAME} + $(CC) -o $@ $(CCOPT) $(DEBUG) -I. -Iextra -L. -lhiredis -levent libevent-example.c .c.o: $(CC) -c $(CFLAGS) $(DEBUG) $(COMPILE_TIME) $< clean: - rm -rf ${DYLIBNAME} ${STLIBNAME} $(BINS) *.o *.gcda *.gcno *.gcov + rm -rf ${DYLIBNAME} ${STLIBNAME} $(BINS) libevent-example *.o *.gcda *.gcno *.gcov dep: $(CC) -MM *.c diff --git a/extra/hiredis/libevent.h b/extra/hiredis/libevent.h new file mode 100644 index 0000000..c9bf4f1 --- /dev/null +++ b/extra/hiredis/libevent.h @@ -0,0 +1,95 @@ +#include +#include + +/* Prototype for the error callback. */ +typedef void (redisErrorCallback)(redisContext*); + +/* This struct enables us to pass both the events and the + * redisContext to the read and write handlers. */ +typedef struct redisEvents { + redisContext *context; + redisErrorCallback *err; + struct event rev, wev; +} redisEvents; + +void redisLibEventRead(int fd, short event, void *arg) { + ((void)fd); ((void)event); + redisEvents *e = arg; + + /* Always re-schedule read events */ + event_add(&e->rev,NULL); + + if (redisBufferRead(e->context) == REDIS_ERR) { + /* Handle error. */ + e->err(e->context); + } else { + /* If processing the replies/callbacks results in an error, + * invoke the error callback and abort. */ + if (redisProcessCallbacks(e->context) == REDIS_ERR) { + e->err(e->context); + } + } +} + +void redisLibEventWrite(int fd, short event, void *arg) { + ((void)fd); ((void)event); + redisEvents *e = arg; + int done = 0; + + if (redisBufferWrite(e->context, &done) == REDIS_ERR) { + /* Handle error */ + e->err(e->context); + } else { + /* Schedule write event again when writing is not done. */ + if (!done) { + event_add(&e->wev,NULL); + } else { + event_add(&e->rev,NULL); + } + } +} + +/* Schedule to be notified on a write event, so the outgoing buffer + * can be flushed to the socket. */ +void redisLibEventOnWrite(redisContext *c, void *privdata) { + ((void)c); + redisEvents *e = privdata; + event_add(&e->wev,NULL); +} + +/* Remove event handlers when the context gets disconnected. */ +void redisLibEventOnDisconnect(redisContext *c, void *privdata) { + ((void)c); + redisEvents *e = privdata; + event_del(&e->rev); + event_del(&e->wev); +} + +/* Free the redisEvents struct when the context is free'd. */ +void redisLibEventOnFree(redisContext *c, void *privdata) { + ((void)c); + redisEvents *e = privdata; + free(e); +} + +redisContext *redisLibEventConnect(const char *ip, int port, redisErrorCallback *err) { + redisEvents *e; + redisContext *c = redisConnectNonBlock(ip, port, NULL); + if (c->error != NULL) { + err(c); + return NULL; + } + + /* Create container for context and r/w events */ + e = malloc(sizeof(*e)); + e->context = c; + e->err = err; + + /* Register callbacks and events */ + redisSetDisconnectCallback(e->context, redisLibEventOnDisconnect, e); + redisSetCommandCallback(e->context, redisLibEventOnWrite, e); + redisSetFreeCallback(e->context, redisLibEventOnFree, e); + event_set(&e->rev, e->context->fd, EV_READ, redisLibEventRead, e); + event_set(&e->wev, e->context->fd, EV_WRITE, redisLibEventWrite, e); + return e->context; +} diff --git a/libevent-example.c b/libevent-example.c index d9c8784..e605af2 100644 --- a/libevent-example.c +++ b/libevent-example.c @@ -1,96 +1,32 @@ #include #include -#include #include -#include "hiredis.h" +#include -#define NOT_USED(x) ((void)x) +void getCallback(redisContext *c, redisReply *reply, const void *privdata) { + printf("argv[%s]: %s\n", (const char*)privdata, reply->reply); -/* This struct enables us to pass both the event and the - * redisContext to the read and write handlers. */ -typedef struct redisEvents { - redisContext *context; - struct event rev, wev; -} redisEvents; - -void redisLibEventRead(int fd, short event, void *arg) { - NOT_USED(fd); NOT_USED(event); - redisEvents *e = arg; - - /* Always re-schedule read events */ - event_add(&e->rev,NULL); - - if (redisBufferRead(e->context) == REDIS_ERR) { - /* Handle error. */ - printf("Read error: %s\n", e->context->error); - } else { - /* Check replies. */ - redisProcessCallbacks(e->context); - } + /* Disconnect after receiving the reply to GET */ + redisDisconnect(c); } -void redisLibEventWrite(int fd, short event, void *arg) { - NOT_USED(fd); NOT_USED(event); - redisEvents *e = arg; - int done = 0; +void errorCallback(redisContext *c) { + printf("Error: %s\n", c->error); - if (redisBufferWrite(e->context, &done) == REDIS_ERR) { - /* Handle error */ - printf("Write error: %s\n", e->context->error); - } else { - /* Schedule write event again when writing is not done. */ - if (!done) { - event_add(&e->wev,NULL); - } else { - event_add(&e->rev,NULL); - } - } -} - -/* Schedule to be notified on a write event, so the outgoing buffer - * can be flushed to the socket. */ -void redisLibEventOnWrite(redisContext *c, void *privdata) { - NOT_USED(c); - redisEvents *e = privdata; - event_add(&e->wev,NULL); -} - -/* Free the redisEvents struct when the context is free'd. */ -void redisLibEventOnFree(redisContext *c, void *privdata) { - NOT_USED(c); - redisEvents *e = privdata; - free(e); -} - -redisContext *redisLibEventConnect(const char *ip, int port) { - redisEvents *e = malloc(sizeof(*e)); - e->context = redisConnectNonBlock(ip, port, NULL); - redisSetCommandCallback(e->context, redisLibEventOnWrite, e); - redisSetFreeCallback(e->context, redisLibEventOnFree, e); - event_set(&e->rev, e->context->fd, EV_READ, redisLibEventRead, e); - event_set(&e->wev, e->context->fd, EV_WRITE, redisLibEventWrite, e); - return e->context; -} - -void getCallback(redisContext *c, redisReply *reply, void *privdata) { - NOT_USED(c); NOT_USED(privdata); - printf("argv[end-1]: %s\n", reply->reply); + /* Clean up the context when there was an error */ redisFree(c); - exit(0); } int main (int argc, char **argv) { signal(SIGPIPE, SIG_IGN); event_init(); - redisContext *c = redisLibEventConnect("127.0.0.1", 6379); - if (c->error != NULL) { - printf("Connection error: %s\n", c->error); - return 1; - } + redisContext *c = redisLibEventConnect("127.0.0.1", 6379, errorCallback); + if (c == NULL) return 1; redisCommand(c, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisCommandWithCallback(c, getCallback, NULL, "GET key"); + redisCommandWithCallback(c, getCallback, "end-1", "GET key"); event_dispatch(); + redisFree(c); return 0; }