summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile.am17
-rw-r--r--cache.c146
-rw-r--r--cache.h44
-rw-r--r--configure.ac9
-rw-r--r--internal_tests.c137
-rw-r--r--memcached.c79
-rw-r--r--memcached.h4
-rw-r--r--testapp.c308
-rw-r--r--thread.c7
10 files changed, 530 insertions, 223 deletions
diff --git a/.gitignore b/.gitignore
index f3d6757..1056ceb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,5 +33,5 @@ memcached-*.tar.gz
doc/protocol-binary-range.txt
doc/protocol-binary.txt
/sizes
-/internal_tests
/version.m4
+/testapp
diff --git a/Makefile.am b/Makefile.am
index af41fe4..1a2b5c6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,9 +1,11 @@
bin_PROGRAMS = memcached
pkginclude_HEADERS = protocol_binary.h
-noinst_PROGRAMS = memcached-debug sizes internal_tests
+noinst_PROGRAMS = memcached-debug sizes testapp
BUILT_SOURCES=
+testapp_SOURCES = testapp.c util.c util.h
+
memcached_SOURCES = memcached.c memcached.h \
hash.c hash.h \
slabs.c slabs.h \
@@ -12,7 +14,12 @@ memcached_SOURCES = memcached.c memcached.h \
thread.c daemon.c \
stats.c stats.h \
util.c util.h \
- trace.h
+ trace.h cache.h
+
+if BUILD_CACHE
+memcached_SOURCES += cache.c
+testapp_SOURCES += cache.c
+endif
if BUILD_SOLARIS_PRIVS
memcached_SOURCES += solaris_priv.c
@@ -28,8 +35,6 @@ memcached_DEPENDENCIES =
memcached_debug_DEPENDENCIES =
CLEANFILES=
-internal_tests_SOURCES = internal_tests.c util.c
-
if BUILD_DTRACE
BUILT_SOURCES += memcached_dtrace.h
CLEANFILES += memcached_dtrace.h
@@ -62,9 +67,9 @@ EXTRA_DIST = doc scripts TODO t memcached.spec memcached_dtrace.d version.m4
MOSTLYCLEANFILES = *.gcov *.gcno *.gcda *.tcov
-test: memcached-debug internal_tests sizes
+test: memcached-debug sizes testapp
$(srcdir)/sizes
- $(srcdir)/internal_tests
+ $(srcdir)/testapp
prove $(srcdir)/t
@if test `basename $(PROFILER)` = "gcov"; then \
for file in memcached_debug-*.gc??; do \
diff --git a/cache.c b/cache.c
new file mode 100644
index 0000000..636cf45
--- /dev/null
+++ b/cache.c
@@ -0,0 +1,146 @@
+/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#ifndef NDEBUG
+#include <signal.h>
+#endif
+
+#include "cache.h"
+
+#ifndef NDEBUG
+const uint64_t redzone_pattern = 0xdeadbeefcafebabe;
+int cache_error = 0;
+#endif
+
+const int initial_pool_size = 64;
+
+cache_t* cache_create(const char *name, size_t bufsize, size_t align,
+ cache_constructor_t* constructor,
+ cache_destructor_t* destructor) {
+ cache_t* ret = calloc(1, sizeof(cache_t));
+ char* nm = strdup(name);
+ void** ptr = calloc(initial_pool_size, bufsize);
+ if (ret == NULL || nm == NULL || ptr == NULL ||
+ pthread_mutex_init(&ret->mutex, NULL) == -1) {
+ free(ret);
+ free(nm);
+ free(ptr);
+ return NULL;
+ }
+
+ ret->name = nm;
+ ret->ptr = ptr;
+ ret->freetotal = initial_pool_size;
+ ret->constructor = constructor;
+ ret->destructor = destructor;
+
+#ifndef NDEBUG
+ ret->bufsize = bufsize + 2 * sizeof(redzone_pattern);
+#else
+ ret->bufsize = bufsize;
+#endif
+
+ return ret;
+}
+
+static inline void* get_object(void *ptr) {
+#ifndef NDEBUG
+ uint64_t *pre = ptr;
+ return pre + 1;
+#endif
+ return ptr;
+}
+
+void cache_destroy(cache_t *cache) {
+ while (cache->freecurr > 0) {
+ void *ptr = cache->ptr[--cache->freecurr];
+ if (cache->destructor) {
+ cache->destructor(get_object(ptr), NULL);
+ }
+ free(ptr);
+ }
+ free(cache->name);
+ free(cache->ptr);
+ pthread_mutex_destroy(&cache->mutex);
+}
+
+void* cache_alloc(cache_t *cache) {
+ void *ret;
+ void *object;
+ pthread_mutex_lock(&cache->mutex);
+ if (cache->freecurr > 0) {
+ ret = cache->ptr[--cache->freecurr];
+ object = get_object(ret);
+ } else {
+ object = ret = malloc(cache->bufsize);
+ if (ret != NULL) {
+ object = get_object(ret);
+
+ if (cache->constructor != NULL &&
+ cache->constructor(object, NULL, 0) != 0) {
+ free(ret);
+ object = NULL;
+ }
+ }
+ }
+ pthread_mutex_unlock(&cache->mutex);
+
+#ifndef NDEBUG
+ if (object != NULL) {
+ /* add a simple form of buffer-check */
+ uint64_t *pre = ret;
+ *pre = redzone_pattern;
+ ret = pre+1;
+ memcpy(((char*)ret) + cache->bufsize - (2 * sizeof(redzone_pattern)),
+ &redzone_pattern, sizeof(redzone_pattern));
+ }
+#endif
+
+ return object;
+}
+
+void cache_free(cache_t *cache, void *ptr) {
+ pthread_mutex_lock(&cache->mutex);
+
+#ifndef NDEBUG
+ /* validate redzone... */
+ if (memcmp(((char*)ptr) + cache->bufsize - (2 * sizeof(redzone_pattern)),
+ &redzone_pattern, sizeof(redzone_pattern)) != 0) {
+ raise(SIGABRT);
+ cache_error = 1;
+ pthread_mutex_unlock(&cache->mutex);
+ return;
+ }
+ uint64_t *pre = ptr;
+ --pre;
+ if (*pre != redzone_pattern) {
+ raise(SIGABRT);
+ cache_error = -1;
+ pthread_mutex_unlock(&cache->mutex);
+ return;
+ }
+ ptr = pre;
+#endif
+ if (cache->freecurr < cache->freetotal) {
+ cache->ptr[cache->freecurr++] = ptr;
+ } else {
+ /* try to enlarge free connections array */
+ size_t newtotal = cache->freetotal * 2;
+ void **new_free = realloc(cache->ptr, sizeof(char *) * newtotal);
+ if (new_free) {
+ cache->freetotal = newtotal;
+ cache->ptr = new_free;
+ cache->ptr[cache->freecurr++] = ptr;
+ } else {
+ if (cache->destructor) {
+ cache->destructor(ptr, NULL);
+ }
+ free(ptr);
+
+ }
+ }
+ pthread_mutex_unlock(&cache->mutex);
+}
+
diff --git a/cache.h b/cache.h
new file mode 100644
index 0000000..f720ee0
--- /dev/null
+++ b/cache.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+#ifndef CACHE_H
+#define CACHE_H
+#include <pthread.h>
+
+#ifdef HAVE_UMEM_H
+#include <umem.h>
+#define cache_t umem_cache_t
+#define cache_alloc(a) umem_cache_alloc(a, UMEM_DEFAULT)
+#define cache_free(a, b) umem_cache_free(a, b)
+#define cache_create(a,b,c,d,e) umem_cache_create((char*)a, b, c, d, e, NULL, NULL, NULL, 0)
+#define cache_destroy(a) umem_cache_destroy(a);
+
+#else
+
+#ifndef NDEBUG
+/* may be used for debug purposes */
+extern int cache_error;
+#endif
+
+typedef int cache_constructor_t(void *, void *, int);
+typedef void cache_destructor_t(void *, void *);
+
+typedef struct {
+ pthread_mutex_t mutex;
+ char *name;
+ void **ptr;
+ size_t bufsize;
+ int freetotal;
+ int freecurr;
+ cache_constructor_t* constructor;
+ cache_destructor_t* destructor;
+} cache_t;
+
+
+cache_t* cache_create(const char *name, size_t bufsize, size_t align,
+ cache_constructor_t* constructor,
+ cache_destructor_t* destructor);
+void cache_destroy(cache_t*);
+void* cache_alloc(cache_t*);
+void cache_free(cache_t*, void*);
+#endif
+
+#endif
diff --git a/configure.ac b/configure.ac
index e96d7b8..65b0252 100644
--- a/configure.ac
+++ b/configure.ac
@@ -230,7 +230,6 @@ dnl ----------------------------------------------------------------------------
AC_SEARCH_LIBS(socket, socket)
AC_SEARCH_LIBS(gethostbyname, nsl)
AC_SEARCH_LIBS(umem_cache_create, umem)
-
AC_HEADER_STDBOOL
AC_C_CONST
@@ -336,6 +335,14 @@ AC_CHECK_FUNCS(setppriv, [
AM_CONDITIONAL([BUILD_SOLARIS_PRIVS],[test "$build_solaris_privs" = "yes"])
+AC_CHECK_HEADER(umem.h, [
+ AC_DEFINE([HAVE_UMEM_H], 1,
+ [Define this if you have umem.h])
+ build_cache=no
+], [build_cache=yes])
+
+AM_CONDITIONAL([BUILD_CACHE], [test "x$build_cache" = "xyes"])
+
AC_ARG_ENABLE(docs,
[AS_HELP_STRING([--disable-docs],[Disable documentation generation])])
diff --git a/internal_tests.c b/internal_tests.c
deleted file mode 100644
index 3429c26..0000000
--- a/internal_tests.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-#undef NDEBUG
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-
-#include "memcached.h"
-
-static void test_safe_strtoull(void);
-static void test_safe_strtoul(void);
-static void test_safe_strtoll(void);
-static void test_safe_strtol(void);
-
-static void test_safe_strtoul() {
- uint32_t val;
- assert(safe_strtoul("123", &val));
- assert(val == 123);
- assert(safe_strtoul("+123", &val));
- assert(val == 123);
- assert(!safe_strtoul("", &val)); // empty
- assert(!safe_strtoul("123BOGUS", &val)); // non-numeric
- /* Not sure what it does, but this works with ICC :/
- assert(!safe_strtoul("92837498237498237498029383", &val)); // out of range
- */
-
- // extremes:
- assert(safe_strtoul("4294967295", &val)); // 2**32 - 1
- assert(val == 4294967295L);
- /* This actually works on 64-bit ubuntu
- assert(!safe_strtoul("4294967296", &val)); // 2**32
- */
- assert(!safe_strtoul("-1", &val)); // negative
-}
-
-
-static void test_safe_strtoull() {
- uint64_t val;
- assert(safe_strtoull("123", &val));
- assert(val == 123);
- assert(safe_strtoull("+123", &val));
- assert(val == 123);
- assert(!safe_strtoull("", &val)); // empty
- assert(!safe_strtoull("123BOGUS", &val)); // non-numeric
- assert(!safe_strtoull("92837498237498237498029383", &val)); // out of range
-
- // extremes:
- assert(safe_strtoull("18446744073709551615", &val)); // 2**64 - 1
- assert(val == 18446744073709551615ULL);
- assert(!safe_strtoull("18446744073709551616", &val)); // 2**64
- assert(!safe_strtoull("-1", &val)); // negative
-}
-
-static void test_safe_strtoll() {
- int64_t val;
- assert(safe_strtoll("123", &val));
- assert(val == 123);
- assert(safe_strtoll("+123", &val));
- assert(val == 123);
- assert(safe_strtoll("-123", &val));
- assert(val == -123);
- assert(!safe_strtoll("", &val)); // empty
- assert(!safe_strtoll("123BOGUS", &val)); // non-numeric
- assert(!safe_strtoll("92837498237498237498029383", &val)); // out of range
-
- // extremes:
- assert(!safe_strtoll("18446744073709551615", &val)); // 2**64 - 1
- assert(safe_strtoll("9223372036854775807", &val)); // 2**63 - 1
- assert(val == 9223372036854775807LL);
- /*
- assert(safe_strtoll("-9223372036854775808", &val)); // -2**63
- assert(val == -9223372036854775808LL);
- */
- assert(!safe_strtoll("-9223372036854775809", &val)); // -2**63 - 1
-
- // We'll allow space to terminate the string. And leading space.
- assert(safe_strtoll(" 123 foo", &val));
- assert(val == 123);
-}
-
-static void test_safe_strtol() {
- int32_t val;
- assert(safe_strtol("123", &val));
- assert(val == 123);
- assert(safe_strtol("+123", &val));
- assert(val == 123);
- assert(safe_strtol("-123", &val));
- assert(val == -123);
- assert(!safe_strtol("", &val)); // empty
- assert(!safe_strtol("123BOGUS", &val)); // non-numeric
- assert(!safe_strtol("92837498237498237498029383", &val)); // out of range
-
- // extremes:
- /* This actually works on 64-bit ubuntu
- assert(!safe_strtol("2147483648", &val)); // (expt 2.0 31.0)
- */
- assert(safe_strtol("2147483647", &val)); // (- (expt 2.0 31) 1)
- assert(val == 2147483647L);
- /* This actually works on 64-bit ubuntu
- assert(!safe_strtol("-2147483649", &val)); // (- (expt -2.0 31) 1)
- */
-
- // We'll allow space to terminate the string. And leading space.
- assert(safe_strtol(" 123 foo", &val));
- assert(val == 123);
-}
-
-static void test_issue_44(void) {
- char pidfile[80];
- char buffer[256];
- sprintf(pidfile, "/tmp/memcached.%d", getpid());
- sprintf(buffer, "./memcached-debug -p 0 -P %s -d", pidfile);
- assert(system(buffer) == 0);
- sleep(1);
- FILE *fp = fopen(pidfile, "r");
- assert(fp);
- assert(fgets(buffer, sizeof(buffer), fp));
- fclose(fp);
- pid_t pid = atol(buffer);
- assert(kill(pid, 0) == 0);
- assert(kill(pid, SIGHUP) == 0);
- sleep(1);
- assert(kill(pid, 0) == 0);
- assert(kill(pid, SIGTERM) == 0);
- assert(remove(pidfile) == 0);
-}
-
-
-int main(int argc, char **argv) {
- test_safe_strtoull();
- test_safe_strtoll();
- test_safe_strtoul();
- test_safe_strtol();
- test_issue_44();
- printf("OK.\n");
- return 0;
-}
diff --git a/memcached.c b/memcached.c
index a2d4c59..4d2bd98 100644
--- a/memcached.c
+++ b/memcached.c
@@ -451,9 +451,7 @@ static void conn_cleanup(conn *c) {
if (c->suffixleft != 0) {
for (; c->suffixleft > 0; c->suffixleft--, c->suffixcurr++) {
- if(suffix_add_to_freelist(*(c->suffixcurr))) {
- free(*(c->suffixcurr));
- }
+ cache_free(c->thread->suffix_cache, *(c->suffixcurr));
}
}
@@ -613,72 +611,6 @@ static void conn_set_state(conn *c, enum conn_states state) {
}
/*
- * Free list management for suffix buffers.
- */
-
-static char **freesuffix;
-static int freesuffixtotal;
-static int freesuffixcurr;
-/* Lock for alternative item suffix freelist */
-static pthread_mutex_t suffix_lock = PTHREAD_MUTEX_INITIALIZER;
-
-static void suffix_init(void) {
- freesuffixtotal = 500;
- freesuffixcurr = 0;
-
- freesuffix = calloc(freesuffixtotal, sizeof(char *));
- if (freesuffix == NULL) {
- fprintf(stderr, "Failed to allocate suffix pool\n");
- }
- return;
-}
-
-/*
- * Returns a suffix buffer from the freelist, if any.
- */
-char *suffix_from_freelist() {
- char *s;
-
- pthread_mutex_lock(&suffix_lock);
- if (freesuffixcurr > 0) {
- s = freesuffix[--freesuffixcurr];
- } else {
- /* If malloc fails, let the logic fall through without spamming
- * STDERR on the server. */
- s = malloc( SUFFIX_SIZE );
- }
- pthread_mutex_unlock(&suffix_lock);
-
- return s;
-}
-
-/*
- * Adds a connection to the freelist. 0 = success. Should call this using
- * suffix_add_to_freelist() for thread safety.
- */
-bool suffix_add_to_freelist(char *s) {
- bool ret = true;
-
- pthread_mutex_lock(&suffix_lock);
- if (freesuffixcurr < freesuffixtotal) {
- freesuffix[freesuffixcurr++] = s;
- ret = false;
- } else {
- /* try to enlarge free connections array */
- char **new_freesuffix = realloc(freesuffix,
- sizeof(char *) * freesuffixtotal * 2);
- if (new_freesuffix) {
- freesuffixtotal *= 2;
- freesuffix = new_freesuffix;
- freesuffix[freesuffixcurr++] = s;
- ret = false;
- }
- }
- pthread_mutex_unlock(&suffix_lock);
- return ret;
-}
-
-/*
* Ensures that there is room for another struct iovec in a connection's
* iov list.
*
@@ -2354,7 +2286,7 @@ static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens,
}
}
- suffix = suffix_from_freelist();
+ suffix = cache_alloc(c->thread->suffix_cache);
if (suffix == NULL) {
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.get_cmds += stats_get_cmds;
@@ -3386,10 +3318,7 @@ static void drive_machine(conn *c) {
}
while (c->suffixleft > 0) {
char *suffix = *(c->suffixcurr);
- if(suffix_add_to_freelist(suffix)) {
- /* Failed to add to freelist, don't leak */
- free(suffix);
- }
+ cache_free(c->thread->suffix_cache, suffix);
c->suffixcurr++;
c->suffixleft--;
}
@@ -4200,8 +4129,6 @@ int main (int argc, char **argv) {
stats_init();
assoc_init();
conn_init();
- /* Hacky suffix buffers. */
- suffix_init();
slabs_init(settings.maxbytes, settings.factor, preallocate);
/*
diff --git a/memcached.h b/memcached.h
index 5011044..9effb59 100644
--- a/memcached.h
+++ b/memcached.h
@@ -15,6 +15,7 @@
#include <pthread.h>
#include "protocol_binary.h"
+#include "cache.h"
/* Maximum length of a key. */
#define KEY_MAX_LENGTH 250
@@ -263,6 +264,7 @@ typedef struct {
int notify_send_fd; /* sending end of notify pipe */
struct thread_stats stats; /* Stats generated by this thread */
struct conn_queue *new_conn_queue; /* queue of new connections to handle */
+ cache_t *suffix_cache; /* suffix cache */
} LIBEVENT_THREAD;
typedef struct conn conn;
@@ -392,8 +394,6 @@ char *add_delta(conn *c, item *item, const int incr, const int64_t delta,
void accept_new_conns(const bool do_accept);
conn *conn_from_freelist(void);
bool conn_add_to_freelist(conn *c);
-char *suffix_from_freelist(void);
-bool suffix_add_to_freelist(char *s);
int is_listen_thread(void);
item *item_alloc(char *key, size_t nkey, int flags, rel_time_t exptime, int nbytes);
char *item_cachedump(const unsigned int slabs_clsid, const unsigned int limit, unsigned int *bytes);
diff --git a/testapp.c b/testapp.c
new file mode 100644
index 0000000..c0013ac
--- /dev/null
+++ b/testapp.c
@@ -0,0 +1,308 @@
+/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+#undef NDEBUG
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "cache.h"
+#include "util.h"
+
+enum test_return { TEST_SKIP, TEST_PASS, TEST_FAIL };
+
+static enum test_return cache_create_test(void)
+{
+ cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
+ NULL, NULL);
+ assert(cache != NULL);
+ cache_destroy(cache);
+ return TEST_PASS;
+}
+
+const uint64_t constructor_pattern = 0xdeadcafebabebeef;
+
+static int cache_constructor(void *buffer, void *notused1, int notused2) {
+ uint64_t *ptr = buffer;
+ *ptr = constructor_pattern;
+ return 0;
+}
+
+static enum test_return cache_constructor_test(void)
+{
+ cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t),
+ cache_constructor, NULL);
+ assert(cache != NULL);
+ uint64_t *ptr = cache_alloc(cache);
+ uint64_t pattern = *ptr;
+ cache_free(cache, ptr);
+ cache_destroy(cache);
+ return (pattern == constructor_pattern) ? TEST_PASS : TEST_FAIL;
+}
+
+static int cache_fail_constructor(void *buffer, void *notused1, int notused2) {
+ return 1;
+}
+
+static enum test_return cache_fail_constructor_test(void)
+{
+ enum test_return ret = TEST_PASS;
+
+ cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t),
+ cache_fail_constructor, NULL);
+ assert(cache != NULL);
+ uint64_t *ptr = cache_alloc(cache);
+ if (ptr != NULL) {
+ ret = TEST_FAIL;
+ }
+ cache_destroy(cache);
+ return ret;
+}
+
+static void *destruct_data = 0;
+
+static void cache_destructor(void *buffer, void *notused) {
+ destruct_data = buffer;
+}
+
+static enum test_return cache_destructor_test(void)
+{
+ cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
+ NULL, cache_destructor);
+ assert(cache != NULL);
+ char *ptr = cache_alloc(cache);
+ cache_free(cache, ptr);
+ cache_destroy(cache);
+
+ return (ptr == destruct_data) ? TEST_PASS : TEST_FAIL;
+}
+
+static enum test_return cache_reuse_test(void)
+{
+ int ii;
+ cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
+ NULL, NULL);
+ char *ptr = cache_alloc(cache);
+ cache_free(cache, ptr);
+ for (ii = 0; ii < 100; ++ii) {
+ char *p = cache_alloc(cache);
+ assert(p == ptr);
+ cache_free(cache, ptr);
+ }
+ cache_destroy(cache);
+ return TEST_PASS;
+}
+
+static enum test_return cache_redzone_test(void)
+{
+#ifndef HAVE_UMEM_H
+ cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
+ NULL, NULL);
+
+ /* Ignore SIGABORT */
+ struct sigaction old_action;
+ struct sigaction action = { .sa_handler = SIG_IGN, .sa_flags = 0};
+ sigemptyset(&action.sa_mask);
+ sigaction(SIGABRT, &action, &old_action);
+
+ /* check memory debug.. */
+ char *p = cache_alloc(cache);
+ char old = *(p - 1);
+ *(p - 1) = 0;
+ cache_free(cache, p);
+ assert(cache_error == -1);
+ *(p - 1) = old;
+
+ p[sizeof(uint32_t)] = 0;
+ cache_free(cache, p);
+ assert(cache_error == 1);
+
+ /* restore signal handler */
+ sigaction(SIGABRT, &old_action, NULL);
+
+ cache_destroy(cache);
+
+ return TEST_PASS;
+#else
+ return TEST_SKIP;
+#endif
+}
+
+static enum test_return test_safe_strtoul(void) {
+ uint32_t val;
+ assert(safe_strtoul("123", &val));
+ assert(val == 123);
+ assert(safe_strtoul("+123", &val));
+ assert(val == 123);
+ assert(!safe_strtoul("", &val)); // empty
+ assert(!safe_strtoul("123BOGUS", &val)); // non-numeric
+ /* Not sure what it does, but this works with ICC :/
+ assert(!safe_strtoul("92837498237498237498029383", &val)); // out of range
+ */
+
+ // extremes:
+ assert(safe_strtoul("4294967295", &val)); // 2**32 - 1
+ assert(val == 4294967295L);
+ /* This actually works on 64-bit ubuntu
+ assert(!safe_strtoul("4294967296", &val)); // 2**32
+ */
+ assert(!safe_strtoul("-1", &val)); // negative
+ return TEST_PASS;
+}
+
+
+static enum test_return test_safe_strtoull(void) {
+ uint64_t val;
+ assert(safe_strtoull("123", &val));
+ assert(val == 123);
+ assert(safe_strtoull("+123", &val));
+ assert(val == 123);
+ assert(!safe_strtoull("", &val)); // empty
+ assert(!safe_strtoull("123BOGUS", &val)); // non-numeric
+ assert(!safe_strtoull("92837498237498237498029383", &val)); // out of range
+
+ // extremes:
+ assert(safe_strtoull("18446744073709551615", &val)); // 2**64 - 1
+ assert(val == 18446744073709551615ULL);
+ assert(!safe_strtoull("18446744073709551616", &val)); // 2**64
+ assert(!safe_strtoull("-1", &val)); // negative
+ return TEST_PASS;
+}
+
+static enum test_return test_safe_strtoll(void) {
+ int64_t val;
+ assert(safe_strtoll("123", &val));
+ assert(val == 123);
+ assert(safe_strtoll("+123", &val));
+ assert(val == 123);
+ assert(safe_strtoll("-123", &val));
+ assert(val == -123);
+ assert(!safe_strtoll("", &val)); // empty
+ assert(!safe_strtoll("123BOGUS", &val)); // non-numeric
+ assert(!safe_strtoll("92837498237498237498029383", &val)); // out of range
+
+ // extremes:
+ assert(!safe_strtoll("18446744073709551615", &val)); // 2**64 - 1
+ assert(safe_strtoll("9223372036854775807", &val)); // 2**63 - 1
+ assert(val == 9223372036854775807LL);
+ /*
+ assert(safe_strtoll("-9223372036854775808", &val)); // -2**63
+ assert(val == -9223372036854775808LL);
+ */
+ assert(!safe_strtoll("-9223372036854775809", &val)); // -2**63 - 1
+
+ // We'll allow space to terminate the string. And leading space.
+ assert(safe_strtoll(" 123 foo", &val));
+ assert(val == 123);
+ return TEST_PASS;
+}
+
+static enum test_return test_safe_strtol(void) {
+ int32_t val;
+ assert(safe_strtol("123", &val));
+ assert(val == 123);
+ assert(safe_strtol("+123", &val));
+ assert(val == 123);
+ assert(safe_strtol("-123", &val));
+ assert(val == -123);
+ assert(!safe_strtol("", &val)); // empty
+ assert(!safe_strtol("123BOGUS", &val)); // non-numeric
+ assert(!safe_strtol("92837498237498237498029383", &val)); // out of range
+
+ // extremes:
+ /* This actually works on 64-bit ubuntu
+ assert(!safe_strtol("2147483648", &val)); // (expt 2.0 31.0)
+ */
+ assert(safe_strtol("2147483647", &val)); // (- (expt 2.0 31) 1)
+ assert(val == 2147483647L);
+ /* This actually works on 64-bit ubuntu
+ assert(!safe_strtol("-2147483649", &val)); // (- (expt -2.0 31) 1)
+ */
+
+ // We'll allow space to terminate the string. And leading space.
+ assert(safe_strtol(" 123 foo", &val));
+ assert(val == 123);
+ return TEST_PASS;
+}
+
+static enum test_return test_issue_44(void) {
+ char pidfile[80];
+ char buffer[256];
+ sprintf(pidfile, "/tmp/memcached.%d", getpid());
+ sprintf(buffer, "./memcached-debug -p 0 -P %s -d", pidfile);
+ assert(system(buffer) == 0);
+ sleep(1);
+ FILE *fp = fopen(pidfile, "r");
+ assert(fp);
+ assert(fgets(buffer, sizeof(buffer), fp));
+ fclose(fp);
+ pid_t pid = atol(buffer);
+ assert(kill(pid, 0) == 0);
+ assert(kill(pid, SIGHUP) == 0);
+ sleep(1);
+ assert(kill(pid, 0) == 0);
+ assert(kill(pid, SIGTERM) == 0);
+ assert(remove(pidfile) == 0);
+
+ return TEST_PASS;
+}
+
+
+
+typedef enum test_return (*TEST_FUNC)(void);
+struct testcase {
+ const char *description;
+ TEST_FUNC function;
+};
+
+struct testcase testcases[] = {
+ { "cache_create", cache_create_test },
+ { "cache_constructor", cache_constructor_test },
+ { "cache_constructor_fail", cache_fail_constructor_test },
+ { "cache_destructor", cache_destructor_test },
+ { "cache_reuse", cache_reuse_test },
+ { "cache_redzone", cache_redzone_test },
+ { "strtol", test_safe_strtol },
+ { "strtoll", test_safe_strtoll },
+ { "strtoul", test_safe_strtoul },
+ { "strtoull", test_safe_strtoull },
+ { "issue_44", test_issue_44 },
+ { NULL, NULL }
+};
+
+int main(int argc, char **argv)
+{
+ int exitcode = 0;
+ int ii;
+ const int paddingsz = 60;
+ char padding[paddingsz + 1];
+ memset(padding, ' ', paddingsz);
+
+ for (ii = 0; testcases[ii].description != NULL; ++ii) {
+ int len = strlen(testcases[ii].description);
+ if (len > paddingsz) {
+ len = paddingsz;
+ }
+ padding[paddingsz - len] = '\0';
+ fprintf(stdout, "%s:%s", testcases[ii].description, padding);
+ padding[paddingsz - len] = ' ';
+ fflush(stdout);
+ enum test_return ret = testcases[ii].function();
+ if (ret == TEST_SKIP) {
+ fprintf(stdout, "[skipped]\n");
+ } else if (ret == TEST_PASS) {
+ fprintf(stdout, "[ok]\n");
+ } else {
+ fprintf(stdout, "[failed]\n");
+ exitcode = 1;
+ }
+ fflush(stdout);
+ }
+
+ return exitcode;
+}
diff --git a/thread.c b/thread.c
index b903709..1cc01c9 100644
--- a/thread.c
+++ b/thread.c
@@ -217,6 +217,13 @@ static void setup_thread(LIBEVENT_THREAD *me) {
perror("Failed to initialize mutex");
exit(EXIT_FAILURE);
}
+
+ me->suffix_cache = cache_create("suffix", SUFFIX_SIZE, sizeof(char*),
+ NULL, NULL);
+ if (me->suffix_cache == NULL) {
+ fprintf(stderr, "Failed to create suffix cache\n");
+ exit(EXIT_FAILURE);
+ }
}