Add redisConnectFd() and redisFreeKeepFd()

These allows for easier integration of hiredis with external
code that wants to manage its fds, say for instance in a pool.

Closes #223
This commit is contained in:
Eddy Jansson 2014-02-24 11:41:00 +01:00 committed by Matt Stancliff
parent 37d25a392c
commit ae30d58ff9
3 changed files with 47 additions and 5 deletions

View File

@ -1010,6 +1010,13 @@ void redisFree(redisContext *c) {
free(c); free(c);
} }
int redisFreeKeepFd(redisContext *c) {
int fd = c->fd;
c->fd = -1;
redisFree(c);
return fd;
}
/* Connect to a Redis instance. On error the field error in the returned /* Connect to a Redis instance. On error the field error in the returned
* context will be set to the return value of the error function. * context will be set to the return value of the error function.
* When no set of reply functions is given, the default set will be used. */ * When no set of reply functions is given, the default set will be used. */
@ -1093,6 +1100,18 @@ redisContext *redisConnectUnixNonBlock(const char *path) {
return c; return c;
} }
redisContext *redisConnectFd(int fd) {
redisContext *c;
c = redisContextInit();
if (c == NULL)
return NULL;
c->fd = fd;
c->flags |= REDIS_BLOCK | REDIS_CONNECTED;
return c;
}
/* Set read/write timeout on a blocking socket. */ /* Set read/write timeout on a blocking socket. */
int redisSetTimeout(redisContext *c, const struct timeval tv) { int redisSetTimeout(redisContext *c, const struct timeval tv) {
if (c->flags & REDIS_BLOCK) if (c->flags & REDIS_BLOCK)

View File

@ -179,9 +179,11 @@ redisContext *redisConnectBindNonBlock(const char *ip, int port, char *source);
redisContext *redisConnectUnix(const char *path); redisContext *redisConnectUnix(const char *path);
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
redisContext *redisConnectUnixNonBlock(const char *path); redisContext *redisConnectUnixNonBlock(const char *path);
redisContext *redisConnectFd(int fd);
int redisSetTimeout(redisContext *c, const struct timeval tv); int redisSetTimeout(redisContext *c, const struct timeval tv);
int redisEnableKeepAlive(redisContext *c); int redisEnableKeepAlive(redisContext *c);
void redisFree(redisContext *c); void redisFree(redisContext *c);
int redisFreeKeepFd(redisContext *c);
int redisBufferRead(redisContext *c); int redisBufferRead(redisContext *c);
int redisBufferWrite(redisContext *c, int *done); int redisBufferWrite(redisContext *c, int *done);

31
test.c
View File

@ -14,7 +14,8 @@
enum connection_type { enum connection_type {
CONN_TCP, CONN_TCP,
CONN_UNIX CONN_UNIX,
CONN_FD
}; };
struct config { struct config {
@ -64,7 +65,7 @@ static redisContext *select_database(redisContext *c) {
return c; return c;
} }
static void disconnect(redisContext *c) { static int disconnect(redisContext *c, int keep_fd) {
redisReply *reply; redisReply *reply;
/* Make sure we're on DB 9. */ /* Make sure we're on DB 9. */
@ -75,8 +76,11 @@ static void disconnect(redisContext *c) {
assert(reply != NULL); assert(reply != NULL);
freeReplyObject(reply); freeReplyObject(reply);
/* Free the context as well. */ /* Free the context as well, but keep the fd if requested. */
if (keep_fd)
return redisFreeKeepFd(c);
redisFree(c); redisFree(c);
return -1;
} }
static redisContext *connect(struct config config) { static redisContext *connect(struct config config) {
@ -86,6 +90,14 @@ static redisContext *connect(struct config config) {
c = redisConnect(config.tcp.host, config.tcp.port); c = redisConnect(config.tcp.host, config.tcp.port);
} else if (config.type == CONN_UNIX) { } else if (config.type == CONN_UNIX) {
c = redisConnectUnix(config.unix.path); c = redisConnectUnix(config.unix.path);
} else if (config.type == CONN_FD) {
/* Create a dummy connection just to get an fd to inherit */
redisContext *dummy_ctx = redisConnectUnix(config.unix.path);
if (dummy_ctx) {
int fd = disconnect(dummy_ctx, 1);
printf("Connecting to inherited fd %d\n", fd);
c = redisConnectFd(fd);
}
} else { } else {
assert(NULL); assert(NULL);
} }
@ -383,7 +395,7 @@ static void test_blocking_connection(struct config config) {
strcasecmp(reply->element[1]->str,"pong") == 0); strcasecmp(reply->element[1]->str,"pong") == 0);
freeReplyObject(reply); freeReplyObject(reply);
disconnect(c); disconnect(c, 0);
} }
static void test_blocking_io_errors(struct config config) { static void test_blocking_io_errors(struct config config) {
@ -523,7 +535,7 @@ static void test_throughput(struct config config) {
free(replies); free(replies);
printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
disconnect(c); disconnect(c, 0);
} }
// static long __test_callback_flags = 0; // static long __test_callback_flags = 0;
@ -636,6 +648,7 @@ int main(int argc, char **argv) {
} }
}; };
int throughput = 1; int throughput = 1;
int test_inherit_fd = 1;
/* Ignore broken pipe signal (for I/O error tests). */ /* Ignore broken pipe signal (for I/O error tests). */
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
@ -654,6 +667,8 @@ int main(int argc, char **argv) {
cfg.unix.path = argv[0]; cfg.unix.path = argv[0];
} else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) { } else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) {
throughput = 0; throughput = 0;
} else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) {
test_inherit_fd = 0;
} else { } else {
fprintf(stderr, "Invalid argument: %s\n", argv[0]); fprintf(stderr, "Invalid argument: %s\n", argv[0]);
exit(1); exit(1);
@ -678,6 +693,12 @@ int main(int argc, char **argv) {
test_blocking_io_errors(cfg); test_blocking_io_errors(cfg);
if (throughput) test_throughput(cfg); if (throughput) test_throughput(cfg);
if (test_inherit_fd) {
printf("\nTesting against inherited fd (%s):\n", cfg.unix.path);
cfg.type = CONN_FD;
test_blocking_connection(cfg);
}
if (fails) { if (fails) {
printf("*** %d TESTS FAILED ***\n", fails); printf("*** %d TESTS FAILED ***\n", fails);
return 1; return 1;