From 29942c4446fa475ae3d08a60eb294bcee749c706 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Thu, 25 Aug 2005 12:40:25 +0000 Subject: [PATCH] Tests for module 'tls'. --- ChangeLog | 5 + modules/tls-tests | 20 +++ tests/test-tls.c | 327 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 352 insertions(+) create mode 100644 modules/tls-tests create mode 100644 tests/test-tls.c diff --git a/ChangeLog b/ChangeLog index 2cb6d04719..38a6935c61 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2005-08-24 Bruno Haible + + * modules/tls-tests: New file. + * tests/test-tls.c: New file, from GNU gettext. + 2005-08-24 Bruno Haible * modules/lock-tests: New file. diff --git a/modules/tls-tests b/modules/tls-tests new file mode 100644 index 0000000000..8824febff4 --- /dev/null +++ b/modules/tls-tests @@ -0,0 +1,20 @@ +Files: +tests/test-tls.c + +Depends-on: + +configure.ac: +dnl Checks for special libraries for the tests/test-tls test. +dnl On some systems, sched_yield is in librt, rather than in libpthread. +LIBSCHED= +if test $gl_threads_api = posix; then + AC_CHECK_LIB(rt, sched_yield, [LIBSCHED=-lrt]) +fi +AC_SUBST([LIBSCHED]) + +Makefile.am: +TESTS += test-tls +noinst_PROGRAMS += test-tls +test_tls_SOURCES = test-tls.c +test_tls_LDADD = @LIBMULTITHREAD@ @LIBSCHED@ $(LDADD) + diff --git a/tests/test-tls.c b/tests/test-tls.c new file mode 100644 index 0000000000..8bdc243350 --- /dev/null +++ b/tests/test-tls.c @@ -0,0 +1,327 @@ +/* Test of thread-local storage in multithreaded situations. + Copyright (C) 2005 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. */ + +/* Written by Bruno Haible , 2005. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if USE_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WIN32_THREADS + +#if USE_POSIX_THREADS +# define TEST_POSIX_THREADS 1 +#endif +#if USE_SOLARIS_THREADS +# define TEST_SOLARIS_THREADS 1 +#endif +#if USE_PTH_THREADS +# define TEST_PTH_THREADS 1 +#endif +#if USE_WIN32_THREADS +# define TEST_WIN32_THREADS 1 +#endif + +/* Whether to help the scheduler through explicit yield(). + Uncomment this to see if the operating system has a fair scheduler. */ +#define EXPLICIT_YIELD 1 + +/* Whether to print debugging messages. */ +#define ENABLE_DEBUGGING 0 + +/* Number of simultaneous threads. */ +#define THREAD_COUNT 16 + +/* Number of operations performed in each thread. */ +#define REPEAT_COUNT 50000 + +#include +#include +#include + +#include "tls.h" + +#if ENABLE_DEBUGGING +# define dbgprintf printf +#else +# define dbgprintf if (0) printf +#endif + +#if TEST_POSIX_THREADS +# include +# include +typedef pthread_t gl_thread_t; +static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg) +{ + pthread_t thread; + if (pthread_create (&thread, NULL, func, arg) != 0) + abort (); + return thread; +} +static inline void gl_thread_join (gl_thread_t thread) +{ + void *retval; + if (pthread_join (thread, &retval) != 0) + abort (); +} +static inline void gl_thread_yield (void) +{ + sched_yield (); +} +static inline void * gl_thread_self (void) +{ + return (void *) pthread_self (); +} +#endif +#if TEST_PTH_THREADS +# include +typedef pth_t gl_thread_t; +static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg) +{ + pth_t thread = pth_spawn (NULL, func, arg); + if (thread == NULL) + abort (); + return thread; +} +static inline void gl_thread_join (gl_thread_t thread) +{ + if (!pth_join (thread, NULL)) + abort (); +} +static inline void gl_thread_yield (void) +{ + pth_yield (NULL); +} +static inline void * gl_thread_self (void) +{ + return pth_self (); +} +#endif +#if TEST_SOLARIS_THREADS +# include +typedef thread_t gl_thread_t; +static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg) +{ + thread_t thread; + if (thr_create (NULL, 0, func, arg, 0, &thread) != 0) + abort (); + return thread; +} +static inline void gl_thread_join (gl_thread_t thread) +{ + void *retval; + if (thr_join (thread, NULL, &retval) != 0) + abort (); +} +static inline void gl_thread_yield (void) +{ + thr_yield (); +} +static inline void * gl_thread_self (void) +{ + return (void *) thr_self (); +} +#endif +#if TEST_WIN32_THREADS +# include +typedef HANDLE gl_thread_t; +/* Use a wrapper function, instead of adding WINAPI through a cast. */ +struct wrapper_args { void * (*func) (void *); void *arg; }; +static DWORD WINAPI wrapper_func (void *varg) +{ + struct wrapper_args *warg = (struct wrapper_args *)varg; + void * (*func) (void *) = warg->func; + void *arg = warg->arg; + free (warg); + func (arg); + return 0; +} +static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg) +{ + struct wrapper_args *warg = + (struct wrapper_args *) malloc (sizeof (struct wrapper_args)); + if (warg == NULL) + abort (); + warg->func = func; + warg->arg = arg; + { + DWORD thread_id; + HANDLE thread = + CreateThread (NULL, 100000, wrapper_func, warg, 0, &thread_id); + if (thread == NULL) + abort (); + return thread; + } +} +static inline void gl_thread_join (gl_thread_t thread) +{ + if (WaitForSingleObject (thread, INFINITE) == WAIT_FAILED) + abort (); + if (!CloseHandle (thread)) + abort (); +} +static inline void gl_thread_yield (void) +{ + Sleep (0); +} +static inline void * gl_thread_self (void) +{ + return (void *) GetCurrentThreadId (); +} +#endif +#if EXPLICIT_YIELD +# define yield() gl_thread_yield () +#else +# define yield() +#endif + +static inline void +perhaps_yield (void) +{ + /* Call yield () only with a certain probability, otherwise with GNU Pth + the sequence of thread activations is too predictable. */ + if ((((unsigned int) rand () >> 3) % 4) == 0) + yield (); +} + +#define KEYS_COUNT 4 + +static gl_tls_key_t mykeys[KEYS_COUNT]; + +static void * +worker_thread (void *arg) +{ + unsigned int id = (unsigned int) (unsigned long) arg; + int i, j, repeat; + unsigned int values[KEYS_COUNT]; + + dbgprintf ("Worker %p started\n", gl_thread_self ()); + + /* Initialize the per-thread storage. */ + for (i = 0; i < KEYS_COUNT; i++) + { + values[i] = (((unsigned int) rand() >> 3) % 1000000) * THREAD_COUNT + id; + /* Hopefully no arithmetic overflow. */ + if ((values[i] % THREAD_COUNT) != id) + abort (); + } + perhaps_yield (); + + /* Verify that the initial value is NULL. */ + dbgprintf ("Worker %p before initial verify\n", gl_thread_self ()); + for (i = 0; i < KEYS_COUNT; i++) + if (gl_tls_get (mykeys[i]) != NULL) + abort (); + dbgprintf ("Worker %p after initial verify\n", gl_thread_self ()); + perhaps_yield (); + + /* Initialize the per-thread storage. */ + dbgprintf ("Worker %p before first tls_set\n", gl_thread_self ()); + for (i = 0; i < KEYS_COUNT; i++) + { + unsigned int *ptr = (unsigned int *) malloc (sizeof (unsigned int)); + *ptr = values[i]; + gl_tls_set (mykeys[i], ptr); + } + dbgprintf ("Worker %p after first tls_set\n", gl_thread_self ()); + perhaps_yield (); + + /* Shuffle around the pointers. */ + for (repeat = REPEAT_COUNT; repeat > 0; repeat--) + { + dbgprintf ("Worker %p doing value swapping\n", gl_thread_self ()); + i = ((unsigned int) rand() >> 3) % KEYS_COUNT; + j = ((unsigned int) rand() >> 3) % KEYS_COUNT; + if (i != j) + { + void *vi = gl_tls_get (mykeys[i]); + void *vj = gl_tls_get (mykeys[j]); + + gl_tls_set (mykeys[i], vj); + gl_tls_set (mykeys[j], vi); + } + perhaps_yield (); + } + + /* Verify that all the values are from this thread. */ + dbgprintf ("Worker %p before final verify\n", gl_thread_self ()); + for (i = 0; i < KEYS_COUNT; i++) + if ((*(unsigned int *) gl_tls_get (mykeys[i]) % THREAD_COUNT) != id) + abort (); + dbgprintf ("Worker %p after final verify\n", gl_thread_self ()); + perhaps_yield (); + + dbgprintf ("Worker %p dying.\n", gl_thread_self ()); + return NULL; +} + +void +test_tls (void) +{ + int pass, i; + + for (pass = 0; pass < 2; pass++) + { + gl_thread_t threads[THREAD_COUNT]; + + if (pass == 0) + for (i = 0; i < KEYS_COUNT; i++) + gl_tls_key_init (mykeys[i], free); + else + for (i = KEYS_COUNT - 1; i >= 0; i--) + gl_tls_key_init (mykeys[i], free); + + /* Spawn the threads. */ + for (i = 0; i < THREAD_COUNT; i++) + threads[i] = gl_thread_create (worker_thread, NULL); + + /* Wait for the threads to terminate. */ + for (i = 0; i < THREAD_COUNT; i++) + gl_thread_join (threads[i]); + + for (i = 0; i < KEYS_COUNT; i++) + gl_tls_key_destroy (mykeys[i]); + } +} + +int +main () +{ +#if TEST_PTH_THREADS + if (!pth_init ()) + abort (); +#endif + + printf ("Starting test_tls ..."); fflush (stdout); + test_tls (); + printf (" OK\n"); fflush (stdout); + + return 0; +} + +#else + +/* No multithreading available. */ + +int +main () +{ + return 77; +} + +#endif -- 2.30.2