Add mq (message queue) to test infrastructure.

Add mtx (mutex) to test infrastructure, in order to avoid bootstrapping
complications that would result from directly using malloc_mutex.

Rename test infrastructure's thread abstraction from je_thread to thd.

Fix some header ordering issues.
This commit is contained in:
Jason Evans 2013-12-12 14:41:02 -08:00
parent 19609724f9
commit 0f4f1efd94
21 changed files with 441 additions and 131 deletions

View File

@ -103,10 +103,12 @@ DOCS_XML := $(objroot)doc/jemalloc$(install_suffix).xml
DOCS_HTML := $(DOCS_XML:$(objroot)%.xml=$(srcroot)%.html)
DOCS_MAN3 := $(DOCS_XML:$(objroot)%.xml=$(srcroot)%.3)
DOCS := $(DOCS_HTML) $(DOCS_MAN3)
C_TESTLIB_SRCS := $(srcroot)test/src/math.c $(srcroot)test/src/SFMT.c \
$(srcroot)test/src/test.c $(srcroot)test/src/thread.c
C_TESTLIB_SRCS := $(srcroot)test/src/math.c $(srcroot)test/src/mtx.c \
$(srcroot)test/src/SFMT.c $(srcroot)test/src/test.c \
$(srcroot)test/src/thd.c
C_UTIL_INTEGRATION_SRCS := $(srcroot)src/util.c
TESTS_UNIT := $(srcroot)test/unit/bitmap.c $(srcroot)test/unit/math.c \
$(srcroot)test/unit/mq.c $(srcroot)test/unit/mtx.c \
$(srcroot)test/unit/SFMT.c $(srcroot)test/unit/tsd.c
TESTS_INTEGRATION := $(srcroot)test/integration/aligned_alloc.c \
$(srcroot)test/integration/allocated.c \

View File

@ -43,14 +43,14 @@ JEMALLOC_INLINE uint32_t
hash_get_block_32(const uint32_t *p, int i)
{
return p[i];
return (p[i]);
}
JEMALLOC_INLINE uint64_t
hash_get_block_64(const uint64_t *p, int i)
{
return p[i];
return (p[i]);
}
JEMALLOC_INLINE uint32_t
@ -63,7 +63,7 @@ hash_fmix_32(uint32_t h)
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
return (h);
}
JEMALLOC_INLINE uint64_t
@ -76,7 +76,7 @@ hash_fmix_64(uint64_t k)
k *= QU(0xc4ceb9fe1a85ec53LLU);
k ^= k >> 33;
return k;
return (k);
}
JEMALLOC_INLINE uint32_t
@ -127,7 +127,7 @@ hash_x86_32(const void *key, int len, uint32_t seed)
h1 = hash_fmix_32(h1);
return h1;
return (h1);
}
UNUSED JEMALLOC_INLINE void

View File

@ -1,3 +1,5 @@
#ifndef JEMALLOC_INTERNAL_DEFS_H_
#define JEMALLOC_INTERNAL_DEFS_H_
/*
* If JEMALLOC_PREFIX is defined via --with-jemalloc-prefix, it will cause all
* public APIs to be prefixed. This makes it possible, with some care, to use
@ -193,3 +195,5 @@
/* sizeof(intmax_t) == 2^LG_SIZEOF_INTMAX_T. */
#undef LG_SIZEOF_INTMAX_T
#endif /* JEMALLOC_INTERNAL_DEFS_H_ */

View File

@ -348,7 +348,7 @@ a_name##_tsd_get_wrapper(void) \
wrapper = tsd_init_check_recursion( \
&a_name##_tsd_init_head, &block); \
if (wrapper) \
return wrapper; \
return (wrapper); \
wrapper = (a_name##_tsd_wrapper_t *) \
malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t)); \
block.data = wrapper; \

View File

@ -1,8 +1,10 @@
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <inttypes.h>
#include <math.h>
#include <string.h>
#ifdef _WIN32
# include <windows.h>
@ -45,6 +47,10 @@
#include "test/jemalloc_test_defs.h"
#ifdef JEMALLOC_OSSPIN
# include <libkern/OSAtomic.h>
#endif
#if defined(HAVE_ALTIVEC) && !defined(__APPLE__)
# include <altivec.h>
#endif
@ -75,13 +81,13 @@
# define JEMALLOC_N(n) @private_namespace@##n
# include "jemalloc/internal/private_namespace.h"
# include <stdarg.h>
# include <errno.h>
# define JEMALLOC_H_TYPES
# define JEMALLOC_H_STRUCTS
# define JEMALLOC_H_EXTERNS
# define JEMALLOC_H_INLINES
# include "jemalloc/internal/util.h"
# include "jemalloc/internal/qr.h"
# include "jemalloc/internal/ql.h"
# undef JEMALLOC_H_TYPES
# undef JEMALLOC_H_STRUCTS
# undef JEMALLOC_H_EXTERNS
@ -124,7 +130,9 @@
* Common test utilities.
*/
#include "test/math.h"
#include "test/mtx.h"
#include "test/mq.h"
#include "test/test.h"
#include "test/thread.h"
#include "test/thd.h"
#define MEXP 19937
#include "test/SFMT.h"

View File

@ -1,3 +1,5 @@
#include "jemalloc/internal/jemalloc_internal_defs.h"
/* For use by SFMT. */
#undef HAVE_SSE2
#undef HAVE_ALTIVEC

110
test/include/test/mq.h Normal file
View File

@ -0,0 +1,110 @@
/*
* Simple templated message queue implementation that relies on only mutexes for
* synchronization (which reduces portability issues). Given the following
* setup:
*
* typedef struct mq_msg_s mq_msg_t;
* struct mq_msg_s {
* mq_msg(mq_msg_t) link;
* [message data]
* };
* mq_gen(, mq_, mq_t, mq_msg_t, link)
*
* The API is as follows:
*
* bool mq_init(mq_t *mq);
* void mq_fini(mq_t *mq);
* unsigned mq_count(mq_t *mq);
* mq_msg_t *mq_tryget(mq_t *mq);
* mq_msg_t *mq_get(mq_t *mq);
* void mq_put(mq_t *mq, mq_msg_t *msg);
*
* The message queue linkage embedded in each message is to be treated as
* externally opaque (no need to initialize or clean up externally). mq_fini()
* does not perform any cleanup of messages, since it knows nothing of their
* payloads.
*/
#define mq_msg(a_mq_msg_type) ql_elm(a_mq_msg_type)
#define mq_gen(a_attr, a_prefix, a_mq_type, a_mq_msg_type, a_field) \
typedef struct { \
mtx_t lock; \
ql_head(a_mq_msg_type) msgs; \
unsigned count; \
} a_mq_type; \
a_attr bool \
a_prefix##init(a_mq_type *mq) { \
\
if (mtx_init(&mq->lock)) \
return (true); \
ql_new(&mq->msgs); \
mq->count = 0; \
return (false); \
} \
a_attr void \
a_prefix##fini(a_mq_type *mq) \
{ \
\
mtx_fini(&mq->lock); \
} \
a_attr unsigned \
a_prefix##count(a_mq_type *mq) \
{ \
unsigned count; \
\
mtx_lock(&mq->lock); \
count = mq->count; \
mtx_unlock(&mq->lock); \
return (count); \
} \
a_attr a_mq_msg_type * \
a_prefix##tryget(a_mq_type *mq) \
{ \
a_mq_msg_type *msg; \
\
mtx_lock(&mq->lock); \
msg = ql_first(&mq->msgs); \
if (msg != NULL) { \
ql_head_remove(&mq->msgs, a_mq_msg_type, a_field); \
mq->count--; \
} \
mtx_unlock(&mq->lock); \
return (msg); \
} \
a_attr a_mq_msg_type * \
a_prefix##get(a_mq_type *mq) \
{ \
a_mq_msg_type *msg; \
struct timespec timeout; \
\
msg = a_prefix##tryget(mq); \
if (msg != NULL) \
return (msg); \
\
timeout.tv_sec = 0; \
timeout.tv_nsec = 1; \
while (true) { \
nanosleep(&timeout, NULL); \
msg = a_prefix##tryget(mq); \
if (msg != NULL) \
return (msg); \
if (timeout.tv_sec == 0) { \
/* Double sleep time, up to max 1 second. */ \
timeout.tv_nsec <<= 1; \
if (timeout.tv_nsec >= 1000*1000*1000) { \
timeout.tv_sec = 1; \
timeout.tv_nsec = 0; \
} \
} \
} \
} \
a_attr void \
a_prefix##put(a_mq_type *mq, a_mq_msg_type *msg) \
{ \
\
mtx_lock(&mq->lock); \
ql_elm_new(msg, a_field); \
ql_tail_insert(&mq->msgs, msg, a_field); \
mq->count++; \
mtx_unlock(&mq->lock); \
}

21
test/include/test/mtx.h Normal file
View File

@ -0,0 +1,21 @@
/*
* mtx is a slightly simplified version of malloc_mutex. This code duplication
* is unfortunate, but there are allocator bootstrapping considerations that
* would leak into the test infrastructure if malloc_mutex were used directly
* in tests.
*/
typedef struct {
#ifdef _WIN32
CRITICAL_SECTION lock;
#elif (defined(JEMALLOC_OSSPIN))
OSSpinLock lock;
#else
pthread_mutex_t lock;
#endif
} mtx_t;
bool mtx_init(mtx_t *mtx);
void mtx_fini(mtx_t *mtx);
void mtx_lock(mtx_t *mtx);
void mtx_unlock(mtx_t *mtx);

9
test/include/test/thd.h Normal file
View File

@ -0,0 +1,9 @@
/* Abstraction layer for threading in tests */
#ifdef _WIN32
typedef HANDLE thd_t;
#else
typedef pthread_t thd_t;
#endif
void thd_create(thd_t *thd, void *(*proc)(void *), void *arg);
void thd_join(thd_t thd, void **ret);

View File

@ -1,9 +0,0 @@
/* Abstraction layer for threading in tests */
#ifdef _WIN32
typedef HANDLE je_thread_t;
#else
typedef pthread_t je_thread_t;
#endif
void je_thread_create(je_thread_t *thread, void *(*proc)(void *), void *arg);
void je_thread_join(je_thread_t thread, void **ret);

View File

@ -3,7 +3,7 @@
#define NTHREADS 10
void *
je_thread_start(void *arg)
thd_start(void *arg)
{
unsigned thread_ind = (unsigned)(uintptr_t)arg;
unsigned arena_ind;
@ -36,16 +36,16 @@ je_thread_start(void *arg)
TEST_BEGIN(test_ALLOCM_ARENA)
{
je_thread_t threads[NTHREADS];
thd_t thds[NTHREADS];
unsigned i;
for (i = 0; i < NTHREADS; i++) {
je_thread_create(&threads[i], je_thread_start,
thd_create(&thds[i], thd_start,
(void *)(uintptr_t)i);
}
for (i = 0; i < NTHREADS; i++)
je_thread_join(threads[i], NULL);
thd_join(thds[i], NULL);
}
TEST_END

View File

@ -9,7 +9,7 @@ static const bool config_stats =
;
void *
je_thread_start(void *arg)
thd_start(void *arg)
{
int err;
void *p;
@ -98,16 +98,16 @@ label_ENOENT:
TEST_BEGIN(test_main_thread)
{
je_thread_start(NULL);
thd_start(NULL);
}
TEST_END
TEST_BEGIN(test_subthread)
{
je_thread_t thread;
thd_t thd;
je_thread_create(&thread, je_thread_start, NULL);
je_thread_join(thread, NULL);
thd_create(&thd, thd_start, NULL);
thd_join(thd, NULL);
}
TEST_END

View File

@ -1,50 +0,0 @@
/*
* This header should be included by tests, rather than directly including
* jemalloc/jemalloc.h, because --with-install-suffix may cause the header to
* have a different name.
*/
#include "jemalloc/jemalloc@install_suffix@.h"
#include "jemalloc/internal/jemalloc_internal.h"
/* Abstraction layer for threading in tests. */
#ifdef _WIN32
#include <windows.h>
typedef HANDLE je_thread_t;
void
je_thread_create(je_thread_t *thread, void *(*proc)(void *), void *arg)
{
LPTHREAD_START_ROUTINE routine = (LPTHREAD_START_ROUTINE)proc;
*thread = CreateThread(NULL, 0, routine, arg, 0, NULL);
if (*thread == NULL)
test_fail("Error in CreateThread()\n");
}
void
je_thread_join(je_thread_t thread, void **ret)
{
WaitForSingleObject(thread, INFINITE);
}
#else
#include <pthread.h>
typedef pthread_t je_thread_t;
void
je_thread_create(je_thread_t *thread, void *(*proc)(void *), void *arg)
{
if (pthread_create(thread, NULL, proc, arg) != 0)
test_fail("Error in pthread_create()\n");
}
void
je_thread_join(je_thread_t thread, void **ret)
{
pthread_join(thread, ret);
}
#endif

View File

@ -3,7 +3,7 @@
#define NTHREADS 10
void *
je_thread_start(void *arg)
thd_start(void *arg)
{
unsigned main_arena_ind = *(unsigned *)arg;
void *p;
@ -43,7 +43,7 @@ TEST_BEGIN(test_thread_arena)
unsigned arena_ind;
size_t size;
int err;
je_thread_t threads[NTHREADS];
thd_t thds[NTHREADS];
unsigned i;
p = malloc(1);
@ -58,13 +58,13 @@ TEST_BEGIN(test_thread_arena)
}
for (i = 0; i < NTHREADS; i++) {
je_thread_create(&threads[i], je_thread_start,
thd_create(&thds[i], thd_start,
(void *)&arena_ind);
}
for (i = 0; i < NTHREADS; i++) {
intptr_t join_ret;
je_thread_join(threads[i], (void *)&join_ret);
thd_join(thds[i], (void *)&join_ret);
assert_zd_eq(join_ret, 0, "Unexpected thread join error");
}
}

View File

@ -9,7 +9,7 @@ static const bool config_tcache =
;
void *
je_thread_start(void *arg)
thd_start(void *arg)
{
int err;
size_t sz;
@ -86,16 +86,16 @@ label_ENOENT:
TEST_BEGIN(test_main_thread)
{
je_thread_start(NULL);
thd_start(NULL);
}
TEST_END
TEST_BEGIN(test_subthread)
{
je_thread_t thread;
thd_t thd;
je_thread_create(&thread, je_thread_start, NULL);
je_thread_join(thread, NULL);
thd_create(&thd, thd_start, NULL);
thd_join(thd, NULL);
}
TEST_END

62
test/src/mtx.c Normal file
View File

@ -0,0 +1,62 @@
#include "test/jemalloc_test.h"
bool
mtx_init(mtx_t *mtx)
{
#ifdef _WIN32
if (!InitializeCriticalSectionAndSpinCount(&mtx->lock, _CRT_SPINCOUNT))
return (true);
#elif (defined(JEMALLOC_OSSPIN))
mtx->lock = 0;
#else
pthread_mutexattr_t attr;
if (pthread_mutexattr_init(&attr) != 0)
return (true);
pthread_mutexattr_settype(&attr, MALLOC_MUTEX_TYPE);
if (pthread_mutex_init(&mtx->lock, &attr) != 0) {
pthread_mutexattr_destroy(&attr);
return (true);
}
pthread_mutexattr_destroy(&attr);
#endif
return (false);
}
void
mtx_fini(mtx_t *mtx)
{
#ifdef _WIN32
#elif (defined(JEMALLOC_OSSPIN))
#else
pthread_mutex_destroy(&mtx->lock);
#endif
}
void
mtx_lock(mtx_t *mtx)
{
#ifdef _WIN32
EnterCriticalSection(&mtx->lock);
#elif (defined(JEMALLOC_OSSPIN))
OSSpinLockLock(&mtx->lock);
#else
pthread_mutex_lock(&mtx->lock);
#endif
}
void
mtx_unlock(mtx_t *mtx)
{
#ifdef _WIN32
LeaveCriticalSection(&mtx->lock);
#elif (defined(JEMALLOC_OSSPIN))
OSSpinLockUnlock(&mtx->lock);
#else
pthread_mutex_unlock(&mtx->lock);
#endif
}

35
test/src/thd.c Normal file
View File

@ -0,0 +1,35 @@
#include "test/jemalloc_test.h"
#ifdef _WIN32
void
thd_create(thd_t *thd, void *(*proc)(void *), void *arg)
{
LPTHREAD_START_ROUTINE routine = (LPTHREAD_START_ROUTINE)proc;
*thd = CreateThread(NULL, 0, routine, arg, 0, NULL);
if (*thd == NULL)
test_fail("Error in CreateThread()\n");
}
void
thd_join(thd_t thd, void **ret)
{
WaitForSingleObject(thd, INFINITE);
}
#else
void
thd_create(thd_t *thd, void *(*proc)(void *), void *arg)
{
if (pthread_create(thd, NULL, proc, arg) != 0)
test_fail("Error in pthread_create()\n");
}
void
thd_join(thd_t thd, void **ret)
{
pthread_join(thd, ret);
}
#endif

View File

@ -1,35 +0,0 @@
#include "test/jemalloc_test.h"
#ifdef _WIN32
void
je_thread_create(je_thread_t *thread, void *(*proc)(void *), void *arg)
{
LPTHREAD_START_ROUTINE routine = (LPTHREAD_START_ROUTINE)proc;
*thread = CreateThread(NULL, 0, routine, arg, 0, NULL);
if (*thread == NULL)
test_fail("Error in CreateThread()\n");
}
void
je_thread_join(je_thread_t thread, void **ret)
{
WaitForSingleObject(thread, INFINITE);
}
#else
void
je_thread_create(je_thread_t *thread, void *(*proc)(void *), void *arg)
{
if (pthread_create(thread, NULL, proc, arg) != 0)
test_fail("Error in pthread_create()\n");
}
void
je_thread_join(je_thread_t thread, void **ret)
{
pthread_join(thread, ret);
}
#endif

91
test/unit/mq.c Normal file
View File

@ -0,0 +1,91 @@
#include "test/jemalloc_test.h"
#define NSENDERS 3
#define NMSGS 100000
typedef struct mq_msg_s mq_msg_t;
struct mq_msg_s {
mq_msg(mq_msg_t) link;
};
mq_gen(static, mq_, mq_t, mq_msg_t, link)
TEST_BEGIN(test_mq_basic)
{
mq_t mq;
mq_msg_t msg;
assert_false(mq_init(&mq), "Unexpected mq_init() failure");
assert_u_eq(mq_count(&mq), 0, "mq should be empty");
assert_ptr_null(mq_tryget(&mq),
"mq_tryget() should fail when the queue is empty");
mq_put(&mq, &msg);
assert_u_eq(mq_count(&mq), 1, "mq should contain one message");
assert_ptr_eq(mq_tryget(&mq), &msg, "mq_tryget() should return msg");
mq_put(&mq, &msg);
assert_ptr_eq(mq_get(&mq), &msg, "mq_get() should return msg");
mq_fini(&mq);
}
TEST_END
static void *
thd_receiver_start(void *arg)
{
mq_t *mq = (mq_t *)arg;
unsigned i;
for (i = 0; i < (NSENDERS * NMSGS); i++) {
mq_msg_t *msg = mq_get(mq);
assert_ptr_not_null(msg, "mq_get() should never return NULL");
assert_d_eq(jet_dallocm(msg, 0), ALLOCM_SUCCESS,
"Unexpected dallocm() failure");
}
return (NULL);
}
static void *
thd_sender_start(void *arg)
{
mq_t *mq = (mq_t *)arg;
unsigned i;
for (i = 0; i < NMSGS; i++) {
mq_msg_t *msg;
assert_d_eq(jet_allocm((void **)&msg, NULL, sizeof(mq_msg_t),
0), ALLOCM_SUCCESS, "Unexpected allocm() failure");
mq_put(mq, msg);
}
return (NULL);
}
TEST_BEGIN(test_mq_threaded)
{
mq_t mq;
thd_t receiver;
thd_t senders[NSENDERS];
unsigned i;
assert_false(mq_init(&mq), "Unexpected mq_init() failure");
thd_create(&receiver, thd_receiver_start, (void *)&mq);
for (i = 0; i < NSENDERS; i++)
thd_create(&senders[i], thd_sender_start, (void *)&mq);
thd_join(receiver, NULL);
for (i = 0; i < NSENDERS; i++)
thd_join(senders[i], NULL);
mq_fini(&mq);
}
TEST_END
int
main(void)
{
return (test(
test_mq_basic,
test_mq_threaded));
}

60
test/unit/mtx.c Normal file
View File

@ -0,0 +1,60 @@
#include "test/jemalloc_test.h"
#define NTHREADS 2
#define NINCRS 2000000
TEST_BEGIN(test_mtx_basic)
{
mtx_t mtx;
assert_false(mtx_init(&mtx), "Unexpected mtx_init() failure");
mtx_lock(&mtx);
mtx_unlock(&mtx);
mtx_fini(&mtx);
}
TEST_END
typedef struct {
mtx_t mtx;
unsigned x;
} thd_start_arg_t;
static void *
thd_start(void *varg)
{
thd_start_arg_t *arg = (thd_start_arg_t *)varg;
unsigned i;
for (i = 0; i < NINCRS; i++) {
mtx_lock(&arg->mtx);
arg->x++;
mtx_unlock(&arg->mtx);
}
return (NULL);
}
TEST_BEGIN(test_mtx_race)
{
thd_start_arg_t arg;
thd_t thds[NTHREADS];
unsigned i;
assert_false(mtx_init(&arg.mtx), "Unexpected mtx_init() failure");
arg.x = 0;
for (i = 0; i < NTHREADS; i++)
thd_create(&thds[i], thd_start, (void *)&arg);
for (i = 0; i < NTHREADS; i++)
thd_join(thds[i], NULL);
assert_u_eq(arg.x, NTHREADS * NINCRS,
"Race-related counter corruption");
}
TEST_END
int
main(void)
{
return (test(
test_mtx_basic,
test_mtx_race));
}

View File

@ -22,8 +22,8 @@ malloc_tsd_externs(data, data_t)
malloc_tsd_data(, data, data_t, DATA_INIT)
malloc_tsd_funcs(, data, data_t, DATA_INIT, data_cleanup)
void *
je_thread_start(void *arg)
static void *
thd_start(void *arg)
{
data_t d = (data_t)(uintptr_t)arg;
assert_x_eq(*data_tsd_get(), DATA_INIT,
@ -37,23 +37,23 @@ je_thread_start(void *arg)
assert_x_eq(*data_tsd_get(), (data_t)(uintptr_t)arg,
"Resetting local data should have no effect on tsd");
return NULL;
return (NULL);
}
TEST_BEGIN(test_tsd_main_thread)
{
je_thread_start((void *) 0xa5f3e329);
thd_start((void *) 0xa5f3e329);
}
TEST_END
TEST_BEGIN(test_tsd_sub_thread)
{
je_thread_t thread;
thd_t thd;
data_cleanup_executed = false;
je_thread_create(&thread, je_thread_start, (void *) THREAD_DATA);
je_thread_join(thread, NULL);
thd_create(&thd, thd_start, (void *)THREAD_DATA);
thd_join(thd, NULL);
assert_true(data_cleanup_executed,
"Cleanup function should have executed");
}