Unverified Commit 8e0264cf authored by Michael Grunder's avatar Michael Grunder Committed by GitHub
Browse files

Allow users to replace allocator and handle OOM everywhere. (#800)

* Adds an indirection to every allocation/deallocation to allow users to 
  plug in ones of their choosing (use custom functions, jemalloc, etc).

* Gracefully handle OOM everywhere in hiredis.  This should make it possible
  for users of the library to have more flexibility in how they handle such situations.

* Changes `redisReaderTask->elements` from an `int` to a `long long` to prevent
  a possible overflow when transferring the task elements into a `redisReply`.

* Adds a configurable `max elements` member to `redisReader` that defaults to
  2^32 - 1.  This can be set to "unlimited" by setting the value to zero.
parent 83bba659
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
* the include of your alternate allocator if needed (not needed in order * the include of your alternate allocator if needed (not needed in order
* to use the default libc allocator). */ * to use the default libc allocator). */
#define s_malloc malloc #include "alloc.h"
#define s_realloc realloc
#define s_free free #define s_malloc hi_malloc
#define s_realloc hi_realloc
#define s_free hi_free
...@@ -163,18 +163,22 @@ static void opensslDoLock(int mode, int lkid, const char *f, int line) { ...@@ -163,18 +163,22 @@ static void opensslDoLock(int mode, int lkid, const char *f, int line) {
(void)line; (void)line;
} }
static void initOpensslLocks(void) { static int initOpensslLocks(void) {
unsigned ii, nlocks; unsigned ii, nlocks;
if (CRYPTO_get_locking_callback() != NULL) { if (CRYPTO_get_locking_callback() != NULL) {
/* Someone already set the callback before us. Don't destroy it! */ /* Someone already set the callback before us. Don't destroy it! */
return; return REDIS_OK;
} }
nlocks = CRYPTO_num_locks(); nlocks = CRYPTO_num_locks();
ossl_locks = hi_malloc(sizeof(*ossl_locks) * nlocks); ossl_locks = hi_malloc(sizeof(*ossl_locks) * nlocks);
if (ossl_locks == NULL)
return REDIS_ERR;
for (ii = 0; ii < nlocks; ii++) { for (ii = 0; ii < nlocks; ii++) {
sslLockInit(ossl_locks + ii); sslLockInit(ossl_locks + ii);
} }
CRYPTO_set_locking_callback(opensslDoLock); CRYPTO_set_locking_callback(opensslDoLock);
return REDIS_OK;
} }
#endif /* HIREDIS_USE_CRYPTO_LOCKS */ #endif /* HIREDIS_USE_CRYPTO_LOCKS */
...@@ -183,15 +187,20 @@ static void initOpensslLocks(void) { ...@@ -183,15 +187,20 @@ static void initOpensslLocks(void) {
*/ */
static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) { static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
redisSSLContext *rssl;
if (c->privdata) { if (c->privdata) {
__redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated"); __redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated");
return REDIS_ERR; return REDIS_ERR;
} }
c->privdata = calloc(1, sizeof(redisSSLContext));
c->funcs = &redisContextSSLFuncs; rssl = hi_calloc(1, sizeof(redisSSLContext));
redisSSLContext *rssl = c->privdata; if (rssl == NULL) {
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
return REDIS_ERR;
}
c->funcs = &redisContextSSLFuncs;
rssl->ssl_ctx = ssl_ctx; rssl->ssl_ctx = ssl_ctx;
rssl->ssl = ssl; rssl->ssl = ssl;
...@@ -202,12 +211,14 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) { ...@@ -202,12 +211,14 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
ERR_clear_error(); ERR_clear_error();
int rv = SSL_connect(rssl->ssl); int rv = SSL_connect(rssl->ssl);
if (rv == 1) { if (rv == 1) {
c->privdata = rssl;
return REDIS_OK; return REDIS_OK;
} }
rv = SSL_get_error(rssl->ssl, rv); rv = SSL_get_error(rssl->ssl, rv);
if (((c->flags & REDIS_BLOCK) == 0) && if (((c->flags & REDIS_BLOCK) == 0) &&
(rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) { (rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {
c->privdata = rssl;
return REDIS_OK; return REDIS_OK;
} }
...@@ -222,6 +233,8 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) { ...@@ -222,6 +233,8 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
} }
__redisSetError(c, REDIS_ERR_IO, err); __redisSetError(c, REDIS_ERR_IO, err);
} }
hi_free(rssl);
return REDIS_ERR; return REDIS_ERR;
} }
...@@ -241,7 +254,10 @@ int redisSecureConnection(redisContext *c, const char *capath, ...@@ -241,7 +254,10 @@ int redisSecureConnection(redisContext *c, const char *capath,
isInit = 1; isInit = 1;
SSL_library_init(); SSL_library_init();
#ifdef HIREDIS_USE_CRYPTO_LOCKS #ifdef HIREDIS_USE_CRYPTO_LOCKS
initOpensslLocks(); if (initOpensslLocks() == REDIS_ERR) {
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
goto error;
}
#endif #endif
} }
...@@ -290,7 +306,8 @@ int redisSecureConnection(redisContext *c, const char *capath, ...@@ -290,7 +306,8 @@ int redisSecureConnection(redisContext *c, const char *capath,
} }
} }
return redisSSLConnect(c, ssl_ctx, ssl); if (redisSSLConnect(c, ssl_ctx, ssl) == REDIS_OK)
return REDIS_OK;
error: error:
if (ssl) SSL_free(ssl); if (ssl) SSL_free(ssl);
...@@ -330,7 +347,7 @@ static void redisSSLFreeContext(void *privdata){ ...@@ -330,7 +347,7 @@ static void redisSSLFreeContext(void *privdata){
SSL_CTX_free(rsc->ssl_ctx); SSL_CTX_free(rsc->ssl_ctx);
rsc->ssl_ctx = NULL; rsc->ssl_ctx = NULL;
} }
free(rsc); hi_free(rsc);
} }
static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) { static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
......
...@@ -180,43 +180,43 @@ static void test_format_commands(void) { ...@@ -180,43 +180,43 @@ static void test_format_commands(void) {
len = redisFormatCommand(&cmd,"SET foo bar"); len = redisFormatCommand(&cmd,"SET foo bar");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2)); len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd); hi_free(cmd);
test("Format command with %%s string interpolation: "); test("Format command with %%s string interpolation: ");
len = redisFormatCommand(&cmd,"SET %s %s","foo","bar"); len = redisFormatCommand(&cmd,"SET %s %s","foo","bar");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2)); len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd); hi_free(cmd);
test("Format command with %%s and an empty string: "); test("Format command with %%s and an empty string: ");
len = redisFormatCommand(&cmd,"SET %s %s","foo",""); len = redisFormatCommand(&cmd,"SET %s %s","foo","");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 && test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(0+2)); len == 4+4+(3+2)+4+(3+2)+4+(0+2));
free(cmd); hi_free(cmd);
test("Format command with an empty string in between proper interpolations: "); test("Format command with an empty string in between proper interpolations: ");
len = redisFormatCommand(&cmd,"SET %s %s","","foo"); len = redisFormatCommand(&cmd,"SET %s %s","","foo");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len) == 0 && test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(0+2)+4+(3+2)); len == 4+4+(3+2)+4+(0+2)+4+(3+2));
free(cmd); hi_free(cmd);
test("Format command with %%b string interpolation: "); test("Format command with %%b string interpolation: ");
len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"b\0r",(size_t)3); len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"b\0r",(size_t)3);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 && test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2)); len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd); hi_free(cmd);
test("Format command with %%b and an empty string: "); test("Format command with %%b and an empty string: ");
len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"",(size_t)0); len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"",(size_t)0);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 && test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(0+2)); len == 4+4+(3+2)+4+(3+2)+4+(0+2));
free(cmd); hi_free(cmd);
test("Format command with literal %%: "); test("Format command with literal %%: ");
len = redisFormatCommand(&cmd,"SET %% %%"); len = redisFormatCommand(&cmd,"SET %% %%");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 && test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(1+2)+4+(1+2)); len == 4+4+(3+2)+4+(1+2)+4+(1+2));
free(cmd); hi_free(cmd);
/* Vararg width depends on the type. These tests make sure that the /* Vararg width depends on the type. These tests make sure that the
* width is correctly determined using the format and subsequent varargs * width is correctly determined using the format and subsequent varargs
...@@ -227,7 +227,7 @@ static void test_format_commands(void) { ...@@ -227,7 +227,7 @@ static void test_format_commands(void) {
len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \ len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \ test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \
len == 4+5+(12+2)+4+(9+2)); \ len == 4+5+(12+2)+4+(9+2)); \
free(cmd); \ hi_free(cmd); \
} while(0) } while(0)
#define FLOAT_WIDTH_TEST(type) do { \ #define FLOAT_WIDTH_TEST(type) do { \
...@@ -236,7 +236,7 @@ static void test_format_commands(void) { ...@@ -236,7 +236,7 @@ static void test_format_commands(void) {
len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \ len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \ test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \
len == 4+5+(12+2)+4+(9+2)); \ len == 4+5+(12+2)+4+(9+2)); \
free(cmd); \ hi_free(cmd); \
} while(0) } while(0)
INTEGER_WIDTH_TEST("d", int); INTEGER_WIDTH_TEST("d", int);
...@@ -267,13 +267,13 @@ static void test_format_commands(void) { ...@@ -267,13 +267,13 @@ static void test_format_commands(void) {
len = redisFormatCommandArgv(&cmd,argc,argv,NULL); len = redisFormatCommandArgv(&cmd,argc,argv,NULL);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2)); len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd); hi_free(cmd);
test("Format command by passing argc/argv with lengths: "); test("Format command by passing argc/argv with lengths: ");
len = redisFormatCommandArgv(&cmd,argc,argv,lens); len = redisFormatCommandArgv(&cmd,argc,argv,lens);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 && test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(7+2)+4+(3+2)); len == 4+4+(3+2)+4+(7+2)+4+(3+2));
free(cmd); hi_free(cmd);
sds sds_cmd; sds sds_cmd;
...@@ -308,7 +308,7 @@ static void test_append_formatted_commands(struct config config) { ...@@ -308,7 +308,7 @@ static void test_append_formatted_commands(struct config config) {
assert(redisGetReply(c, (void*)&reply) == REDIS_OK); assert(redisGetReply(c, (void*)&reply) == REDIS_OK);
free(cmd); hi_free(cmd);
freeReplyObject(reply); freeReplyObject(reply);
disconnect(c, 0); disconnect(c, 0);
...@@ -418,6 +418,16 @@ static void test_reply_reader(void) { ...@@ -418,6 +418,16 @@ static void test_reply_reader(void) {
freeReplyObject(reply); freeReplyObject(reply);
redisReaderFree(reader); redisReaderFree(reader);
test("Can configure maximum multi-bulk elements: ");
reader = redisReaderCreate();
reader->maxelements = 1024;
redisReaderFeed(reader, "*1025\r\n", 7);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_ERR &&
strcasecmp(reader->errstr, "Multi-bulk length out of range") == 0);
freeReplyObject(reply);
redisReaderFree(reader);
#if LLONG_MAX > SIZE_MAX #if LLONG_MAX > SIZE_MAX
test("Set error when array > SIZE_MAX: "); test("Set error when array > SIZE_MAX: ");
reader = redisReaderCreate(); reader = redisReaderCreate();
...@@ -518,6 +528,46 @@ static void test_free_null(void) { ...@@ -518,6 +528,46 @@ static void test_free_null(void) {
test_cond(reply == NULL); test_cond(reply == NULL);
} }
static void *hi_malloc_fail(size_t size) {
(void)size;
return NULL;
}
static void *hi_calloc_fail(size_t nmemb, size_t size) {
(void)nmemb;
(void)size;
return NULL;
}
static void *hi_realloc_fail(void *ptr, size_t size) {
(void)ptr;
(void)size;
return NULL;
}
static void test_allocator_injection(void) {
hiredisAllocFuncs ha = {
.malloc = hi_malloc_fail,
.calloc = hi_calloc_fail,
.realloc = hi_realloc_fail,
.free = NULL,
};
// Override hiredis allocators
hiredisSetAllocators(&ha);
test("redisContext uses injected allocators: ");
redisContext *c = redisConnect("localhost", 6379);
test_cond(c == NULL);
test("redisReader uses injected allocators: ");
redisReader *reader = redisReaderCreate();
test_cond(reader == NULL);
// Return allocators to default
hiredisResetAllocators();
}
#define HIREDIS_BAD_DOMAIN "idontexist-noreally.com" #define HIREDIS_BAD_DOMAIN "idontexist-noreally.com"
static void test_blocking_connection_errors(void) { static void test_blocking_connection_errors(void) {
redisContext *c; redisContext *c;
...@@ -799,6 +849,18 @@ static void test_invalid_timeout_errors(struct config config) { ...@@ -799,6 +849,18 @@ static void test_invalid_timeout_errors(struct config config) {
redisFree(c); redisFree(c);
} }
/* Wrap malloc to abort on failure so OOM checks don't make the test logic
* harder to follow. */
void *hi_malloc_safe(size_t size) {
void *ptr = hi_malloc(size);
if (ptr == NULL) {
fprintf(stderr, "Error: Out of memory\n");
exit(-1);
}
return ptr;
}
static void test_throughput(struct config config) { static void test_throughput(struct config config) {
redisContext *c = do_connect(config); redisContext *c = do_connect(config);
redisReply **replies; redisReply **replies;
...@@ -810,7 +872,7 @@ static void test_throughput(struct config config) { ...@@ -810,7 +872,7 @@ static void test_throughput(struct config config) {
freeReplyObject(redisCommand(c,"LPUSH mylist foo")); freeReplyObject(redisCommand(c,"LPUSH mylist foo"));
num = 1000; num = 1000;
replies = malloc(sizeof(redisReply*)*num); replies = hi_malloc_safe(sizeof(redisReply*)*num);
t1 = usec(); t1 = usec();
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
replies[i] = redisCommand(c,"PING"); replies[i] = redisCommand(c,"PING");
...@@ -818,10 +880,10 @@ static void test_throughput(struct config config) { ...@@ -818,10 +880,10 @@ static void test_throughput(struct config config) {
} }
t2 = usec(); t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]); for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies); hi_free(replies);
printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0); printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0);
replies = malloc(sizeof(redisReply*)*num); replies = hi_malloc_safe(sizeof(redisReply*)*num);
t1 = usec(); t1 = usec();
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
replies[i] = redisCommand(c,"LRANGE mylist 0 499"); replies[i] = redisCommand(c,"LRANGE mylist 0 499");
...@@ -830,10 +892,10 @@ static void test_throughput(struct config config) { ...@@ -830,10 +892,10 @@ static void test_throughput(struct config config) {
} }
t2 = usec(); t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]); for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies); hi_free(replies);
printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0); printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0);
replies = malloc(sizeof(redisReply*)*num); replies = hi_malloc_safe(sizeof(redisReply*)*num);
t1 = usec(); t1 = usec();
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000); replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000);
...@@ -841,11 +903,11 @@ static void test_throughput(struct config config) { ...@@ -841,11 +903,11 @@ static void test_throughput(struct config config) {
} }
t2 = usec(); t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]); for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies); hi_free(replies);
printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0); printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0);
num = 10000; num = 10000;
replies = malloc(sizeof(redisReply*)*num); replies = hi_malloc_safe(sizeof(redisReply*)*num);
for (i = 0; i < num; i++) for (i = 0; i < num; i++)
redisAppendCommand(c,"PING"); redisAppendCommand(c,"PING");
t1 = usec(); t1 = usec();
...@@ -855,10 +917,10 @@ static void test_throughput(struct config config) { ...@@ -855,10 +917,10 @@ static void test_throughput(struct config config) {
} }
t2 = usec(); t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]); for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies); hi_free(replies);
printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
replies = malloc(sizeof(redisReply*)*num); replies = hi_malloc_safe(sizeof(redisReply*)*num);
for (i = 0; i < num; i++) for (i = 0; i < num; i++)
redisAppendCommand(c,"LRANGE mylist 0 499"); redisAppendCommand(c,"LRANGE mylist 0 499");
t1 = usec(); t1 = usec();
...@@ -869,10 +931,10 @@ static void test_throughput(struct config config) { ...@@ -869,10 +931,10 @@ static void test_throughput(struct config config) {
} }
t2 = usec(); t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]); for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies); hi_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);
replies = malloc(sizeof(redisReply*)*num); replies = hi_malloc_safe(sizeof(redisReply*)*num);
for (i = 0; i < num; i++) for (i = 0; i < num; i++)
redisAppendCommand(c,"INCRBY incrkey %d", 1000000); redisAppendCommand(c,"INCRBY incrkey %d", 1000000);
t1 = usec(); t1 = usec();
...@@ -882,7 +944,7 @@ static void test_throughput(struct config config) { ...@@ -882,7 +944,7 @@ static void test_throughput(struct config config) {
} }
t2 = usec(); t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]); for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies); hi_free(replies);
printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
disconnect(c, 0); disconnect(c, 0);
...@@ -1049,11 +1111,14 @@ int main(int argc, char **argv) { ...@@ -1049,11 +1111,14 @@ int main(int argc, char **argv) {
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
test_unix_socket = access(cfg.unix_sock.path, F_OK) == 0; test_unix_socket = access(cfg.unix_sock.path, F_OK) == 0;
#else #else
/* Unix sockets don't exist in Windows */ /* Unix sockets don't exist in Windows */
test_unix_socket = 0; test_unix_socket = 0;
#endif #endif
test_allocator_injection();
test_format_commands(); test_format_commands();
test_reply_reader(); test_reply_reader();
test_blocking_connection_errors(); test_blocking_connection_errors();
......
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