>From d1e821c9ade4128c23672133da264af4e11c1cba Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Wed, 26 Jun 2019 03:32:51 +0200 Subject: [PATCH 2/3] tls tests: Add tests for destructors and races. * tests/test-tls.c: Include glthread/lock.h. (test_tls_dtorcheck1, test_tls_dtorcheck2, test_tls_racecheck): New functions. (main): Invoke them. * modules/tls-tests (Depends-on): Add lock. --- ChangeLog | 9 ++ modules/tls-tests | 1 + tests/test-tls.c | 333 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 337 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9e6d4da..3a0b8d6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,14 @@ 2019-06-25 Bruno Haible + tls tests: Add tests for destructors and races. + * tests/test-tls.c: Include glthread/lock.h. + (test_tls_dtorcheck1, test_tls_dtorcheck2, test_tls_racecheck): New + functions. + (main): Invoke them. + * modules/tls-tests (Depends-on): Add lock. + +2019-06-25 Bruno Haible + windows-tls: Implement TLS key destructors for native Windows. * lib/windows-tls.h (glwthread_tls_process_destructors): New declaration. diff --git a/modules/tls-tests b/modules/tls-tests index b93f673..aec1935 100644 --- a/modules/tls-tests +++ b/modules/tls-tests @@ -3,6 +3,7 @@ tests/test-tls.c Depends-on: thread +lock stdint yield diff --git a/tests/test-tls.c b/tests/test-tls.c index 0cd6b1d..2ce9f0d 100644 --- a/tests/test-tls.c +++ b/tests/test-tls.c @@ -40,12 +40,6 @@ /* 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 @@ -53,6 +47,7 @@ #include "glthread/tls.h" #include "glthread/thread.h" +#include "glthread/lock.h" #include "glthread/yield.h" #if HAVE_DECL_ALARM @@ -84,6 +79,12 @@ perhaps_yield (void) /* ----------------------- Test thread-local storage ----------------------- */ +/* Number of simultaneous threads. */ +#define THREAD_COUNT 16 + +/* Number of operations performed in each thread. */ +#define REPEAT_COUNT 50000 + #define KEYS_COUNT 4 static gl_tls_key_t mykeys[KEYS_COUNT]; @@ -184,6 +185,307 @@ test_tls (void) } } +#undef KEYS_COUNT +#undef REPEAT_COUNT +#undef THREAD_COUNT + + +/* --------------- Test thread-local storage with destructors --------------- */ + +/* Number of simultaneous threads. */ +#define THREAD_COUNT 10 + +/* Number of keys to allocate in each thread. */ +#define KEYS_COUNT 10 + +gl_lock_define_initialized(static, sumlock) +static uintptr_t sum; + +static void +inc_sum (uintptr_t value) +{ + gl_lock_lock (sumlock); + sum += value; + gl_lock_unlock (sumlock); +} + +static void +destructor0 (void *value) +{ + if ((((uintptr_t) value - 1) % 10) != 0) + abort (); + inc_sum ((uintptr_t) value); +} + +static void +destructor1 (void *value) +{ + if ((((uintptr_t) value - 1) % 10) != 1) + abort (); + inc_sum ((uintptr_t) value); +} + +static void +destructor2 (void *value) +{ + if ((((uintptr_t) value - 1) % 10) != 2) + abort (); + inc_sum ((uintptr_t) value); +} + +static void +destructor3 (void *value) +{ + if ((((uintptr_t) value - 1) % 10) != 3) + abort (); + inc_sum ((uintptr_t) value); +} + +static void +destructor4 (void *value) +{ + if ((((uintptr_t) value - 1) % 10) != 4) + abort (); + inc_sum ((uintptr_t) value); +} + +static void +destructor5 (void *value) +{ + if ((((uintptr_t) value - 1) % 10) != 5) + abort (); + inc_sum ((uintptr_t) value); +} + +static void +destructor6 (void *value) +{ + if ((((uintptr_t) value - 1) % 10) != 6) + abort (); + inc_sum ((uintptr_t) value); +} + +static void +destructor7 (void *value) +{ + if ((((uintptr_t) value - 1) % 10) != 7) + abort (); + inc_sum ((uintptr_t) value); +} + +static void +destructor8 (void *value) +{ + if ((((uintptr_t) value - 1) % 10) != 8) + abort (); + inc_sum ((uintptr_t) value); +} + +static void +destructor9 (void *value) +{ + if ((((uintptr_t) value - 1) % 10) != 9) + abort (); + inc_sum ((uintptr_t) value); +} + +static void (*destructor_table[10]) (void *) = + { + destructor0, + destructor1, + destructor2, + destructor3, + destructor4, + destructor5, + destructor6, + destructor7, + destructor8, + destructor9 + }; + +static gl_tls_key_t dtorcheck_keys[THREAD_COUNT][KEYS_COUNT]; + +/* Worker thread that uses destructors that verify that the destructor belongs + to the right thread. */ +static void * +dtorcheck1_thread (void *arg) +{ + unsigned int id = (unsigned int) (uintptr_t) arg; + gl_tls_key_t *keys = dtorcheck_keys[id]; /* an array of KEYS_COUNT keys */ + int i; + + for (i = 0; i < KEYS_COUNT; i++) + gl_tls_key_init (keys[i], destructor_table[i]); + + for (i = 0; i < KEYS_COUNT; i++) + gl_tls_set (keys[i], (void *) (uintptr_t) (10 * id + i + 1)); + + return NULL; +} + +static void +test_tls_dtorcheck1 (void) +{ + gl_thread_t threads[THREAD_COUNT]; + unsigned int id; + int i; + uintptr_t expected_sum; + + sum = 0; + + /* Spawn the threads. */ + for (id = 0; id < THREAD_COUNT; id++) + threads[id] = gl_thread_create (dtorcheck1_thread, (void *) (uintptr_t) id); + + /* Wait for the threads to terminate. */ + for (id = 0; id < THREAD_COUNT; id++) + gl_thread_join (threads[id], NULL); + + /* Clean up the keys. */ + for (id = 0; id < THREAD_COUNT; id++) + for (i = 0; i < KEYS_COUNT; i++) + gl_tls_key_destroy (dtorcheck_keys[id][i]); + + /* Check that the destructor was invoked for each key. */ + expected_sum = 10 * KEYS_COUNT * (THREAD_COUNT * (THREAD_COUNT - 1) / 2) + + THREAD_COUNT * (KEYS_COUNT * (KEYS_COUNT - 1) / 2) + + THREAD_COUNT * KEYS_COUNT; + if (sum != expected_sum) + abort (); +} + +/* Worker thread that uses destructors that verify that the destructor belongs + to the right key allocated within the thread. */ +static void * +dtorcheck2_thread (void *arg) +{ + unsigned int id = (unsigned int) (uintptr_t) arg; + gl_tls_key_t *keys = dtorcheck_keys[id]; /* an array of KEYS_COUNT keys */ + int i; + + for (i = 0; i < KEYS_COUNT; i++) + gl_tls_key_init (keys[i], destructor_table[id]); + + for (i = 0; i < KEYS_COUNT; i++) + gl_tls_set (keys[i], (void *) (uintptr_t) (10 * i + id + 1)); + + return NULL; +} + +static void +test_tls_dtorcheck2 (void) +{ + gl_thread_t threads[THREAD_COUNT]; + unsigned int id; + int i; + uintptr_t expected_sum; + + sum = 0; + + /* Spawn the threads. */ + for (id = 0; id < THREAD_COUNT; id++) + threads[id] = gl_thread_create (dtorcheck2_thread, (void *) (uintptr_t) id); + + /* Wait for the threads to terminate. */ + for (id = 0; id < THREAD_COUNT; id++) + gl_thread_join (threads[id], NULL); + + /* Clean up the keys. */ + for (id = 0; id < THREAD_COUNT; id++) + for (i = 0; i < KEYS_COUNT; i++) + gl_tls_key_destroy (dtorcheck_keys[id][i]); + + /* Check that the destructor was invoked for each key. */ + expected_sum = 10 * THREAD_COUNT * (KEYS_COUNT * (KEYS_COUNT - 1) / 2) + + KEYS_COUNT * (THREAD_COUNT * (THREAD_COUNT - 1) / 2) + + THREAD_COUNT * KEYS_COUNT; + if (sum != expected_sum) + abort (); +} + +#undef KEYS_COUNT +#undef THREAD_COUNT + + +/* --- Test thread-local storage with with races between init and destroy --- */ + +/* Number of simultaneous threads. */ +#define THREAD_COUNT 10 + +/* Number of keys to allocate in each thread. */ +#define KEYS_COUNT 10 + +/* Number of times to destroy and reallocate a key in each thread. */ +#define REPEAT_COUNT 100000 + +static gl_tls_key_t racecheck_keys[THREAD_COUNT][KEYS_COUNT]; + +/* Worker thread that does many destructions and reallocations of keys, and also + uses destructors that verify that the destructor belongs to the right key. */ +static void * +racecheck_thread (void *arg) +{ + unsigned int id = (unsigned int) (uintptr_t) arg; + gl_tls_key_t *keys = racecheck_keys[id]; /* an array of KEYS_COUNT keys */ + int repeat; + int i; + + dbgprintf ("Worker %p started\n", gl_thread_self_pointer ()); + + for (i = 0; i < KEYS_COUNT; i++) + { + gl_tls_key_init (keys[i], destructor_table[i]); + gl_tls_set (keys[i], (void *) (uintptr_t) (10 * id + i + 1)); + } + + for (repeat = REPEAT_COUNT; repeat > 0; repeat--) + { + i = ((unsigned int) rand () >> 3) % KEYS_COUNT; + dbgprintf ("Worker %p reallocating key %d\n", gl_thread_self_pointer (), i); + gl_tls_key_destroy (keys[i]); + gl_tls_key_init (keys[i], destructor_table[i]); + gl_tls_set (keys[i], (void *) (uintptr_t) (10 * id + i + 1)); + } + + dbgprintf ("Worker %p dying.\n", gl_thread_self_pointer ()); + return NULL; +} + +static void +test_tls_racecheck (void) +{ + gl_thread_t threads[THREAD_COUNT]; + unsigned int id; + int i; + uintptr_t expected_sum; + + sum = 0; + + /* Spawn the threads. */ + for (id = 0; id < THREAD_COUNT; id++) + threads[id] = gl_thread_create (racecheck_thread, (void *) (uintptr_t) id); + + /* Wait for the threads to terminate. */ + for (id = 0; id < THREAD_COUNT; id++) + gl_thread_join (threads[id], NULL); + + /* Clean up the keys. */ + for (id = 0; id < THREAD_COUNT; id++) + for (i = 0; i < KEYS_COUNT; i++) + gl_tls_key_destroy (racecheck_keys[id][i]); + + /* Check that the destructor was invoked for each key. */ + expected_sum = 10 * KEYS_COUNT * (THREAD_COUNT * (THREAD_COUNT - 1) / 2) + + THREAD_COUNT * (KEYS_COUNT * (KEYS_COUNT - 1) / 2) + + THREAD_COUNT * KEYS_COUNT; + if (sum != expected_sum) + abort (); +} + +#undef REPEAT_COUNT +#undef KEYS_COUNT +#undef THREAD_COUNT + /* -------------------------------------------------------------------------- */ @@ -207,6 +509,25 @@ main () test_tls (); printf (" OK\n"); fflush (stdout); + printf ("Starting test_tls_dtorcheck1 ..."); fflush (stdout); + test_tls_dtorcheck1 (); + printf (" OK\n"); fflush (stdout); + + printf ("Starting test_tls_dtorcheck2 ..."); fflush (stdout); + test_tls_dtorcheck2 (); + printf (" OK\n"); fflush (stdout); + + /* This test hangs with the mingw-w64 winpthreads. */ +#if (defined _WIN32 && ! defined __CYGWIN__) && TEST_POSIX_THREADS + fputs ("Skipping test: it is known to hang with the mingw-w64 winpthreads.\n", + stderr); + exit (77); +#else + printf ("Starting test_tls_racecheck ..."); fflush (stdout); + test_tls_racecheck (); + printf (" OK\n"); fflush (stdout); +#endif + return 0; } -- 2.7.4