(root)/
gettext-0.22.4/
gettext-tools/
gnulib-tests/
test-asyncsafe-spin2.c
       1  /* Test of spin locks for communication between threads and signal handlers.
       2     Copyright (C) 2005, 2008-2023 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Bruno Haible <bruno@clisp.org>, 2005.  */
      18  
      19  #include <config.h>
      20  
      21  #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
      22  
      23  /* Whether to enable locking.
      24     Uncomment this to get a test program without locking, to verify that
      25     it crashes.  */
      26  #define ENABLE_LOCKING 1
      27  
      28  /* Whether to help the scheduler through explicit yield().
      29     Uncomment this to see if the operating system has a fair scheduler.  */
      30  #define EXPLICIT_YIELD 1
      31  
      32  /* Whether to print debugging messages.  */
      33  #define ENABLE_DEBUGGING 0
      34  
      35  /* Number of simultaneous threads.  */
      36  #define THREAD_COUNT 10
      37  
      38  /* Number of operations performed in each thread.  */
      39  #if !(defined _WIN32 && ! defined __CYGWIN__) && HAVE_PTHREAD_H && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || __clang_major__ >= 3) && !defined __ibmxl__
      40  
      41  /* The GCC built-ins are known to work fine.  */
      42  # define REPEAT_COUNT 5000
      43  #else
      44  /* This is quite high, because with a smaller count, say 50000, we often get
      45     an "OK" result even with the racy implementation that we pick on Fedora 13
      46     Linux/x86_64 (gcc 4.4).  */
      47  # define REPEAT_COUNT 100000
      48  #endif
      49  
      50  #include <signal.h>
      51  #include <stdint.h>
      52  #include <stdio.h>
      53  #include <stdlib.h>
      54  #include <string.h>
      55  
      56  #include "asyncsafe-spin.h"
      57  #if !ENABLE_LOCKING
      58  # define asyncsafe_spin_init(lock) (void)(lock)
      59  # define asyncsafe_spin_lock(lock, mask, saved_mask) \
      60      ((void)(lock), (void)(mask), (void)(saved_mask))
      61  # define asyncsafe_spin_unlock(lock, saved_mask) \
      62      ((void)(lock), (void)(saved_mask))
      63  # define asyncsafe_spin_destroy(lock) (void)(lock)
      64  #endif
      65  
      66  #include "glthread/lock.h"
      67  #include "glthread/thread.h"
      68  #include "glthread/yield.h"
      69  
      70  #if HAVE_DECL_ALARM
      71  # include <signal.h>
      72  # include <unistd.h>
      73  #endif
      74  
      75  #include "atomic-int-gnulib.h"
      76  
      77  #if ENABLE_DEBUGGING
      78  # define dbgprintf printf
      79  #else
      80  # define dbgprintf if (0) printf
      81  #endif
      82  
      83  #if EXPLICIT_YIELD
      84  # define yield() gl_thread_yield ()
      85  #else
      86  # define yield()
      87  #endif
      88  
      89  static sigset_t signals_to_block;
      90  
      91  #define ACCOUNT_COUNT 4
      92  
      93  static int account[ACCOUNT_COUNT];
      94  
      95  static int
      96  random_account (void)
      97  {
      98    return ((unsigned long) random () >> 3) % ACCOUNT_COUNT;
      99  }
     100  
     101  static void
     102  check_accounts (void)
     103  {
     104    int i, sum;
     105  
     106    sum = 0;
     107    for (i = 0; i < ACCOUNT_COUNT; i++)
     108      sum += account[i];
     109    if (sum != ACCOUNT_COUNT * 1000)
     110      abort ();
     111  }
     112  
     113  
     114  /* ------------------- Test use like normal locks ------------------- */
     115  
     116  /* Test normal locks by having several bank accounts and several threads
     117     which shuffle around money between the accounts and another thread
     118     checking that all the money is still there.  */
     119  
     120  static asyncsafe_spinlock_t my_lock;
     121  
     122  static void *
     123  lock_mutator_thread (void *arg)
     124  {
     125    int repeat;
     126  
     127    for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
     128      {
     129        sigset_t saved_signals;
     130        int i1, i2, value;
     131  
     132        dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
     133        asyncsafe_spin_lock (&my_lock, &signals_to_block, &saved_signals);
     134        dbgprintf ("Mutator %p after  lock\n", gl_thread_self_pointer ());
     135  
     136        i1 = random_account ();
     137        i2 = random_account ();
     138        value = ((unsigned long) random () >> 3) % 10;
     139        account[i1] += value;
     140        account[i2] -= value;
     141  
     142        dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
     143        asyncsafe_spin_unlock (&my_lock, &saved_signals);
     144        dbgprintf ("Mutator %p after  unlock\n", gl_thread_self_pointer ());
     145  
     146        dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
     147        asyncsafe_spin_lock (&my_lock, &signals_to_block, &saved_signals);
     148        check_accounts ();
     149        asyncsafe_spin_unlock (&my_lock, &saved_signals);
     150        dbgprintf ("Mutator %p after  check unlock\n", gl_thread_self_pointer ());
     151  
     152        yield ();
     153      }
     154  
     155    dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
     156    return NULL;
     157  }
     158  
     159  static struct atomic_int lock_checker_done;
     160  
     161  static void *
     162  lock_checker_thread (void *arg)
     163  {
     164    while (get_atomic_int_value (&lock_checker_done) == 0)
     165      {
     166        sigset_t saved_signals;
     167  
     168        dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
     169        asyncsafe_spin_lock (&my_lock, &signals_to_block, &saved_signals);
     170        check_accounts ();
     171        asyncsafe_spin_unlock (&my_lock, &saved_signals);
     172        dbgprintf ("Checker %p after  check unlock\n", gl_thread_self_pointer ());
     173  
     174        yield ();
     175      }
     176  
     177    dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
     178    return NULL;
     179  }
     180  
     181  static void
     182  test_asyncsafe_spin (void)
     183  {
     184    int i;
     185    gl_thread_t checkerthread;
     186    gl_thread_t threads[THREAD_COUNT];
     187  
     188    /* Initialization.  */
     189    for (i = 0; i < ACCOUNT_COUNT; i++)
     190      account[i] = 1000;
     191    init_atomic_int (&lock_checker_done);
     192    set_atomic_int_value (&lock_checker_done, 0);
     193  
     194    /* Spawn the threads.  */
     195    checkerthread = gl_thread_create (lock_checker_thread, NULL);
     196    for (i = 0; i < THREAD_COUNT; i++)
     197      threads[i] = gl_thread_create (lock_mutator_thread, NULL);
     198  
     199    /* Wait for the threads to terminate.  */
     200    for (i = 0; i < THREAD_COUNT; i++)
     201      gl_thread_join (threads[i], NULL);
     202    set_atomic_int_value (&lock_checker_done, 1);
     203    gl_thread_join (checkerthread, NULL);
     204    check_accounts ();
     205  }
     206  
     207  
     208  /* -------------------------------------------------------------------------- */
     209  
     210  int
     211  main ()
     212  {
     213  #if HAVE_DECL_ALARM
     214    /* Declare failure if test takes too long, by using default abort
     215       caused by SIGALRM.  */
     216    int alarm_value = 600;
     217    signal (SIGALRM, SIG_DFL);
     218    alarm (alarm_value);
     219  #endif
     220  
     221    sigemptyset (&signals_to_block);
     222    sigaddset (&signals_to_block, SIGINT);
     223  
     224    asyncsafe_spin_init (&my_lock);
     225  
     226    printf ("Starting test_asyncsafe_spin ..."); fflush (stdout);
     227    test_asyncsafe_spin ();
     228    printf (" OK\n"); fflush (stdout);
     229  
     230    return 0;
     231  }
     232  
     233  #else
     234  
     235  /* No multithreading available.  */
     236  
     237  #include <stdio.h>
     238  
     239  int
     240  main ()
     241  {
     242    fputs ("Skipping test: multithreading not enabled\n", stderr);
     243    return 77;
     244  }
     245  
     246  #endif