]> git.ipfire.org Git - thirdparty/gnulib.git/commitdiff
spin: Add tests.
authorBruno Haible <bruno@clisp.org>
Wed, 14 May 2025 11:57:28 +0000 (13:57 +0200)
committerBruno Haible <bruno@clisp.org>
Wed, 14 May 2025 11:57:28 +0000 (13:57 +0200)
* 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.

ChangeLog
modules/spin-tests [new file with mode: 0644]
tests/test-spin1.c [new file with mode: 0644]
tests/test-spin2.c [new file with mode: 0644]

index 6c37f8b1a4e1a3a60ad13419999673b2fa3ffe1e..b903bacf5ed1c200763c0aacd600f0d795d9ecbe 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2025-05-14  Bruno Haible  <bruno@clisp.org>
 
+       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 (file)
index 0000000..56a29ee
--- /dev/null
@@ -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 (file)
index 0000000..5b82925
--- /dev/null
@@ -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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2020.  */
+
+#include <config.h>
+
+/* 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 (file)
index 0000000..b3baf3c
--- /dev/null
@@ -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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.  */
+
+#include <config.h>
+
+#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 <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 <signal.h>
+# include <unistd.h>
+#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 <stdio.h>
+
+int
+main ()
+{
+  fputs ("Skipping test: multithreading not enabled\n", stderr);
+  return 77;
+}
+
+#endif