>From ab0284709c3fafeb51679a309d61b37d7d52dc1d Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Wed, 1 Jul 2020 22:06:45 +0200 Subject: [PATCH 4/4] pthread-spin: Add tests. * tests/test-pthread-spin.c: New file, based on tests/test-lock.c. * modules/pthread-spin-tests: New file. --- ChangeLog | 6 ++ modules/pthread-spin-tests | 20 ++++ tests/test-pthread-spin.c | 231 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 modules/pthread-spin-tests create mode 100644 tests/test-pthread-spin.c diff --git a/ChangeLog b/ChangeLog index cd09023..86c32ed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2020-07-01 Bruno Haible + pthread-spin: Add tests. + * tests/test-pthread-spin.c: New file, based on tests/test-lock.c. + * modules/pthread-spin-tests: New file. + +2020-07-01 Bruno Haible + tests: Reduce code duplication. * tests/atomic-int-posix.h: New file, extracted from tests/test-pthread-mutex.c. diff --git a/modules/pthread-spin-tests b/modules/pthread-spin-tests new file mode 100644 index 0000000..aafe8c7 --- /dev/null +++ b/modules/pthread-spin-tests @@ -0,0 +1,20 @@ +Files: +tests/test-pthread-spin.c +tests/atomic-int-posix.h +tests/macros.h +m4/semaphore.m4 + +Depends-on: +pthread-thread +pthread-mutex +sched_yield + +configure.ac: +AC_CHECK_HEADERS_ONCE([semaphore.h]) +AC_CHECK_DECLS_ONCE([alarm]) +AC_REQUIRE([gl_SEMAPHORE]) + +Makefile.am: +TESTS += test-pthread-spin +check_PROGRAMS += test-pthread-spin +test_pthread_spin_LDADD = $(LDADD) @LIBPMULTITHREAD@ @LIB_SCHED_YIELD@ @LIB_SEMAPHORE@ diff --git a/tests/test-pthread-spin.c b/tests/test-pthread-spin.c new file mode 100644 index 0000000..aec8a96 --- /dev/null +++ b/tests/test-pthread-spin.c @@ -0,0 +1,231 @@ +/* Test of POSIX spin locks. + Copyright (C) 2005, 2008-2020 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible , 2005. */ + +#include + +#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS + +/* Whether to enable locking. + Uncomment this to get a test program without locking, to verify that + it crashes. */ +#define ENABLE_LOCKING 1 + +/* Whether to help the scheduler through explicit sched_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 10 + +/* Number of operations performed in each thread. */ +#define REPEAT_COUNT 50000 + +#include +#include +#include +#include +#include + +#if EXPLICIT_YIELD +# include +#endif + +#if HAVE_DECL_ALARM +# include +# include +#endif + +#include "macros.h" +#include "atomic-int-posix.h" + +#if ENABLE_DEBUGGING +# define dbgprintf printf +#else +# define dbgprintf if (0) printf +#endif + +#if EXPLICIT_YIELD +# define yield() sched_yield () +#else +# define yield() +#endif + +/* Returns a reference to the current thread as a pointer, for debugging. */ +#if defined __MVS__ + /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field. + The first three bytes of this field appear to uniquely identify a + pthread_t, though not necessarily representing a pointer. */ +# define pthread_self_pointer() (*((void **) pthread_self ().__)) +#else +# define pthread_self_pointer() ((void *) (uintptr_t) pthread_self ()) +#endif + +#define ACCOUNT_COUNT 4 + +static int account[ACCOUNT_COUNT]; + +static int +random_account (void) +{ + return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT; +} + +static void +check_accounts (void) +{ + int i, sum; + + sum = 0; + for (i = 0; i < ACCOUNT_COUNT; i++) + sum += account[i]; + if (sum != ACCOUNT_COUNT * 1000) + abort (); +} + + +/* ------------------- Test use like normal locks ------------------- */ + +/* Test normal locks by having several bank accounts and several threads + which shuffle around money between the accounts and another thread + checking that all the money is still there. */ + +static pthread_spinlock_t my_lock; + +static void * +lock_mutator_thread (void *arg) +{ + int repeat; + + for (repeat = REPEAT_COUNT; repeat > 0; repeat--) + { + int i1, i2, value; + + dbgprintf ("Mutator %p before lock\n", pthread_self_pointer ()); + ASSERT (pthread_spin_lock (&my_lock) == 0); + dbgprintf ("Mutator %p after lock\n", pthread_self_pointer ()); + + i1 = random_account (); + i2 = random_account (); + value = ((unsigned int) rand () >> 3) % 10; + account[i1] += value; + account[i2] -= value; + + dbgprintf ("Mutator %p before unlock\n", pthread_self_pointer ()); + ASSERT (pthread_spin_unlock (&my_lock) == 0); + dbgprintf ("Mutator %p after unlock\n", pthread_self_pointer ()); + + dbgprintf ("Mutator %p before check lock\n", pthread_self_pointer ()); + ASSERT (pthread_spin_lock (&my_lock) == 0); + check_accounts (); + ASSERT (pthread_spin_unlock (&my_lock) == 0); + dbgprintf ("Mutator %p after check unlock\n", pthread_self_pointer ()); + + yield (); + } + + dbgprintf ("Mutator %p dying.\n", pthread_self_pointer ()); + return NULL; +} + +static struct atomic_int lock_checker_done; + +static void * +lock_checker_thread (void *arg) +{ + while (get_atomic_int_value (&lock_checker_done) == 0) + { + dbgprintf ("Checker %p before check lock\n", pthread_self_pointer ()); + ASSERT (pthread_spin_lock (&my_lock) == 0); + check_accounts (); + ASSERT (pthread_spin_unlock (&my_lock) == 0); + dbgprintf ("Checker %p after check unlock\n", pthread_self_pointer ()); + + yield (); + } + + dbgprintf ("Checker %p dying.\n", pthread_self_pointer ()); + return NULL; +} + +static void +test_pthread_spin (void) +{ + int i; + pthread_t checkerthread; + pthread_t threads[THREAD_COUNT]; + + /* Initialization. */ + for (i = 0; i < ACCOUNT_COUNT; i++) + account[i] = 1000; + init_atomic_int (&lock_checker_done); + set_atomic_int_value (&lock_checker_done, 0); + + /* Spawn the threads. */ + ASSERT (pthread_create (&checkerthread, NULL, lock_checker_thread, NULL) + == 0); + for (i = 0; i < THREAD_COUNT; i++) + ASSERT (pthread_create (&threads[i], NULL, lock_mutator_thread, NULL) == 0); + + /* Wait for the threads to terminate. */ + for (i = 0; i < THREAD_COUNT; i++) + ASSERT (pthread_join (threads[i], NULL) == 0); + set_atomic_int_value (&lock_checker_done, 1); + ASSERT (pthread_join (checkerthread, NULL) == 0); + check_accounts (); +} + + +/* -------------------------------------------------------------------------- */ + +int +main () +{ +#if HAVE_DECL_ALARM + /* Declare failure if test takes too long, by using default abort + caused by SIGALRM. */ + int alarm_value = 600; + signal (SIGALRM, SIG_DFL); + alarm (alarm_value); +#endif + + ASSERT (pthread_spin_init (&my_lock, 0) == 0); + + printf ("Starting test_pthread_spin ..."); fflush (stdout); + test_pthread_spin (); + printf (" OK\n"); fflush (stdout); + + return 0; +} + +#else + +/* No multithreading available. */ + +#include + +int +main () +{ + fputs ("Skipping test: multithreading not enabled\n", stderr); + return 77; +} + +#endif -- 2.7.4