From: Bruno Haible Date: Wed, 14 May 2025 11:57:28 +0000 (+0200) Subject: spin: Add tests. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1bf1bea69bb0c590f01d1e312cef8fd71c5dc5e4;p=thirdparty%2Fgnulib.git spin: Add tests. * tests/test-spin1.c: New file, based on tests/test-asyncsafe-spin1.c. * tests/test-spin2.c: New file, based on tests/test-asyncsafe-spin2.c. * modules/spin-tests: New file. --- diff --git a/ChangeLog b/ChangeLog index 6c37f8b1a4..b903bacf5e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2025-05-14 Bruno Haible + spin: Add tests. + * tests/test-spin1.c: New file, based on tests/test-asyncsafe-spin1.c. + * tests/test-spin2.c: New file, based on tests/test-asyncsafe-spin2.c. + * modules/spin-tests: New file. + spin: New module. * lib/glthread/spin.h: New file, based on lib/glthread/lock.h and lib/asyncsafe-spin.h. diff --git a/modules/spin-tests b/modules/spin-tests new file mode 100644 index 0000000000..56a29ee10d --- /dev/null +++ b/modules/spin-tests @@ -0,0 +1,21 @@ +Files: +tests/test-spin1.c +tests/test-spin2.c +tests/atomic-int-gnulib.h +m4/semaphore.m4 + +Depends-on: +thread +lock +yield +random + +configure.ac: +AC_CHECK_HEADERS_ONCE([semaphore.h]) +AC_CHECK_DECLS_ONCE([alarm]) +AC_REQUIRE([gl_SEMAPHORE]) + +Makefile.am: +TESTS += test-spin1 test-spin2 +check_PROGRAMS += test-spin1 test-spin2 +test_spin2_LDADD = $(LDADD) @LIBMULTITHREAD@ @YIELD_LIB@ @LIB_SEMAPHORE@ diff --git a/tests/test-spin1.c b/tests/test-spin1.c new file mode 100644 index 0000000000..5b82925905 --- /dev/null +++ b/tests/test-spin1.c @@ -0,0 +1,52 @@ +/* Test of spin locks in multithreaded situations. + Copyright (C) 2005, 2008-2025 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 , 2020. */ + +#include + +/* Specification. */ +#include "glthread/spin.h" + +gl_spinlock_define_initialized (, global_spin_lock) + +int +main (void) +{ + /* Check a spin-lock initialized through the constant initializer. */ + { + gl_spinlock_lock (global_spin_lock); + gl_spinlock_unlock (global_spin_lock); + } + + /* Check a spin-lock initialized through gl_spinlock_init. */ + { + gl_spinlock_define (, local_spin_lock) + int i; + + gl_spinlock_init (local_spin_lock); + + for (i = 0; i < 10; i++) + { + gl_spinlock_lock (local_spin_lock); + gl_spinlock_unlock (local_spin_lock); + } + + gl_spinlock_destroy (local_spin_lock); + } + + return 0; +} diff --git a/tests/test-spin2.c b/tests/test-spin2.c new file mode 100644 index 0000000000..b3baf3c325 --- /dev/null +++ b/tests/test-spin2.c @@ -0,0 +1,240 @@ +/* Test of spin locks in multithreaded situations. + Copyright (C) 2005, 2008-2025 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 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. */ +#if !(defined _WIN32 && ! defined __CYGWIN__) && HAVE_PTHREAD_H && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || __clang_major__ >= 3) && !defined __ibmxl__ + +/* The GCC built-ins are known to work fine. */ +# define REPEAT_COUNT 5000 +#else +/* This is quite high, because with a smaller count, say 50000, we often get + an "OK" result even with the racy implementation that we pick on Fedora 13 + Linux/x86_64 (gcc 4.4). */ +# define REPEAT_COUNT 100000 +#endif + +#include +#include +#include +#include +#include + +#include "glthread/spin.h" +#if !ENABLE_LOCKING +# undef gl_spinlock_init +# define gl_spinlock_init(lock) (void)(lock) +# undef gl_spinlock_lock +# define gl_spinlock_lock(lock) (void)(lock) +# undef gl_spinlock_unlock +# define gl_spinlock_unlock(lock) (void)(lock) +# undef gl_spinlock_destroy +# define gl_spinlock_destroy(lock) (void)(lock) +#endif + +#include "glthread/lock.h" +#include "glthread/thread.h" +#include "glthread/yield.h" + +#if HAVE_DECL_ALARM +# include +# include +#endif + +#include "atomic-int-gnulib.h" + +#if ENABLE_DEBUGGING +# define dbgprintf printf +#else +# define dbgprintf if (0) printf +#endif + +#if EXPLICIT_YIELD +# define yield() gl_thread_yield () +#else +# define yield() +#endif + +#define ACCOUNT_COUNT 4 + +static int account[ACCOUNT_COUNT]; + +static int +random_account (void) +{ + return ((unsigned long) random () >> 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. */ + +gl_spinlock_define (static, 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", gl_thread_self_pointer ()); + gl_spinlock_lock (my_lock); + dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ()); + + i1 = random_account (); + i2 = random_account (); + value = ((unsigned long) random () >> 3) % 10; + account[i1] += value; + account[i2] -= value; + + dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ()); + gl_spinlock_unlock (my_lock); + dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ()); + + dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ()); + gl_spinlock_lock (my_lock); + check_accounts (); + gl_spinlock_unlock (my_lock); + dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ()); + + yield (); + } + + dbgprintf ("Mutator %p dying.\n", gl_thread_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", gl_thread_self_pointer ()); + gl_spinlock_lock (my_lock); + check_accounts (); + gl_spinlock_unlock (my_lock); + dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ()); + + yield (); + } + + dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ()); + return NULL; +} + +static void +test_asyncsafe_spin (void) +{ + int i; + gl_thread_t checkerthread; + gl_thread_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. */ + checkerthread = gl_thread_create (lock_checker_thread, NULL); + for (i = 0; i < THREAD_COUNT; i++) + threads[i] = gl_thread_create (lock_mutator_thread, NULL); + + /* Wait for the threads to terminate. */ + for (i = 0; i < THREAD_COUNT; i++) + gl_thread_join (threads[i], NULL); + set_atomic_int_value (&lock_checker_done, 1); + gl_thread_join (checkerthread, NULL); + 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 + + gl_spinlock_init (my_lock); + + printf ("Starting test_asyncsafe_spin ..."); fflush (stdout); + test_asyncsafe_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