(root)/
coreutils-9.4/
gnulib-tests/
test-lock.c
       1  /* Test of locking in multithreaded situations.
       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  #if USE_ISOC_THREADS
      24  # define TEST_ISOC_THREADS 1
      25  #endif
      26  #if USE_POSIX_THREADS
      27  # define TEST_POSIX_THREADS 1
      28  #endif
      29  #if USE_ISOC_AND_POSIX_THREADS
      30  # define TEST_ISOC_AND_POSIX_THREADS 1
      31  #endif
      32  #if USE_WINDOWS_THREADS
      33  # define TEST_WINDOWS_THREADS 1
      34  #endif
      35  
      36  /* Whether to enable locking.
      37     Uncomment this to get a test program without locking, to verify that
      38     it crashes.  */
      39  #define ENABLE_LOCKING 1
      40  
      41  /* Which tests to perform.
      42     Uncomment some of these, to verify that all tests crash if no locking
      43     is enabled.  */
      44  #define DO_TEST_LOCK 1
      45  #define DO_TEST_RWLOCK 1
      46  #define DO_TEST_RECURSIVE_LOCK 1
      47  #define DO_TEST_ONCE 1
      48  
      49  /* Whether to help the scheduler through explicit yield().
      50     Uncomment this to see if the operating system has a fair scheduler.  */
      51  #define EXPLICIT_YIELD 1
      52  
      53  /* Whether to print debugging messages.  */
      54  #define ENABLE_DEBUGGING 0
      55  
      56  /* Number of simultaneous threads.  */
      57  #define THREAD_COUNT 10
      58  
      59  /* Number of operations performed in each thread.
      60     This is quite high, because with a smaller count, say 5000, we often get
      61     an "OK" result even without ENABLE_LOCKING (on Linux/x86).  */
      62  #define REPEAT_COUNT 50000
      63  
      64  #include <stdint.h>
      65  #include <stdio.h>
      66  #include <stdlib.h>
      67  #include <string.h>
      68  
      69  #if !ENABLE_LOCKING
      70  # undef USE_ISOC_THREADS
      71  # undef USE_POSIX_THREADS
      72  # undef USE_ISOC_AND_POSIX_THREADS
      73  # undef USE_WINDOWS_THREADS
      74  #endif
      75  #include "glthread/lock.h"
      76  
      77  #if !ENABLE_LOCKING
      78  # if TEST_ISOC_THREADS
      79  #  define USE_ISOC_THREADS 1
      80  # endif
      81  # if TEST_POSIX_THREADS
      82  #  define USE_POSIX_THREADS 1
      83  # endif
      84  # if TEST_ISOC_AND_POSIX_THREADS
      85  #  define USE_ISOC_AND_POSIX_THREADS 1
      86  # endif
      87  # if TEST_WINDOWS_THREADS
      88  #  define USE_WINDOWS_THREADS 1
      89  # endif
      90  #endif
      91  
      92  #include "glthread/thread.h"
      93  #include "glthread/yield.h"
      94  
      95  #if HAVE_DECL_ALARM
      96  # include <signal.h>
      97  # include <unistd.h>
      98  #endif
      99  
     100  #include "atomic-int-gnulib.h"
     101  
     102  #if ENABLE_DEBUGGING
     103  # define dbgprintf printf
     104  #else
     105  # define dbgprintf if (0) printf
     106  #endif
     107  
     108  #if EXPLICIT_YIELD
     109  # define yield() gl_thread_yield ()
     110  #else
     111  # define yield()
     112  #endif
     113  
     114  #define ACCOUNT_COUNT 4
     115  
     116  static int account[ACCOUNT_COUNT];
     117  
     118  static int
     119  random_account (void)
     120  {
     121    return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
     122  }
     123  
     124  static void
     125  check_accounts (void)
     126  {
     127    int i, sum;
     128  
     129    sum = 0;
     130    for (i = 0; i < ACCOUNT_COUNT; i++)
     131      sum += account[i];
     132    if (sum != ACCOUNT_COUNT * 1000)
     133      abort ();
     134  }
     135  
     136  
     137  /* ------------------- Test normal (non-recursive) locks ------------------- */
     138  
     139  /* Test normal locks by having several bank accounts and several threads
     140     which shuffle around money between the accounts and another thread
     141     checking that all the money is still there.  */
     142  
     143  gl_lock_define_initialized(static, my_lock)
     144  
     145  static void *
     146  lock_mutator_thread (void *arg)
     147  {
     148    int repeat;
     149  
     150    for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
     151      {
     152        int i1, i2, value;
     153  
     154        dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
     155        gl_lock_lock (my_lock);
     156        dbgprintf ("Mutator %p after  lock\n", gl_thread_self_pointer ());
     157  
     158        i1 = random_account ();
     159        i2 = random_account ();
     160        value = ((unsigned int) rand () >> 3) % 10;
     161        account[i1] += value;
     162        account[i2] -= value;
     163  
     164        dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
     165        gl_lock_unlock (my_lock);
     166        dbgprintf ("Mutator %p after  unlock\n", gl_thread_self_pointer ());
     167  
     168        dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
     169        gl_lock_lock (my_lock);
     170        check_accounts ();
     171        gl_lock_unlock (my_lock);
     172        dbgprintf ("Mutator %p after  check unlock\n", gl_thread_self_pointer ());
     173  
     174        yield ();
     175      }
     176  
     177    dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
     178    return NULL;
     179  }
     180  
     181  static struct atomic_int lock_checker_done;
     182  
     183  static void *
     184  lock_checker_thread (void *arg)
     185  {
     186    while (get_atomic_int_value (&lock_checker_done) == 0)
     187      {
     188        dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
     189        gl_lock_lock (my_lock);
     190        check_accounts ();
     191        gl_lock_unlock (my_lock);
     192        dbgprintf ("Checker %p after  check unlock\n", gl_thread_self_pointer ());
     193  
     194        yield ();
     195      }
     196  
     197    dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
     198    return NULL;
     199  }
     200  
     201  static void
     202  test_lock (void)
     203  {
     204    int i;
     205    gl_thread_t checkerthread;
     206    gl_thread_t threads[THREAD_COUNT];
     207  
     208    /* Initialization.  */
     209    for (i = 0; i < ACCOUNT_COUNT; i++)
     210      account[i] = 1000;
     211    init_atomic_int (&lock_checker_done);
     212    set_atomic_int_value (&lock_checker_done, 0);
     213  
     214    /* Spawn the threads.  */
     215    checkerthread = gl_thread_create (lock_checker_thread, NULL);
     216    for (i = 0; i < THREAD_COUNT; i++)
     217      threads[i] = gl_thread_create (lock_mutator_thread, NULL);
     218  
     219    /* Wait for the threads to terminate.  */
     220    for (i = 0; i < THREAD_COUNT; i++)
     221      gl_thread_join (threads[i], NULL);
     222    set_atomic_int_value (&lock_checker_done, 1);
     223    gl_thread_join (checkerthread, NULL);
     224    check_accounts ();
     225  }
     226  
     227  
     228  /* ----------------- Test read-write (non-recursive) locks ----------------- */
     229  
     230  /* Test read-write locks by having several bank accounts and several threads
     231     which shuffle around money between the accounts and several other threads
     232     that check that all the money is still there.  */
     233  
     234  gl_rwlock_define_initialized(static, my_rwlock)
     235  
     236  static void *
     237  rwlock_mutator_thread (void *arg)
     238  {
     239    int repeat;
     240  
     241    for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
     242      {
     243        int i1, i2, value;
     244  
     245        dbgprintf ("Mutator %p before wrlock\n", gl_thread_self_pointer ());
     246        gl_rwlock_wrlock (my_rwlock);
     247        dbgprintf ("Mutator %p after  wrlock\n", gl_thread_self_pointer ());
     248  
     249        i1 = random_account ();
     250        i2 = random_account ();
     251        value = ((unsigned int) rand () >> 3) % 10;
     252        account[i1] += value;
     253        account[i2] -= value;
     254  
     255        dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
     256        gl_rwlock_unlock (my_rwlock);
     257        dbgprintf ("Mutator %p after  unlock\n", gl_thread_self_pointer ());
     258  
     259        yield ();
     260      }
     261  
     262    dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
     263    return NULL;
     264  }
     265  
     266  static struct atomic_int rwlock_checker_done;
     267  
     268  static void *
     269  rwlock_checker_thread (void *arg)
     270  {
     271    while (get_atomic_int_value (&rwlock_checker_done) == 0)
     272      {
     273        dbgprintf ("Checker %p before check rdlock\n", gl_thread_self_pointer ());
     274        gl_rwlock_rdlock (my_rwlock);
     275        check_accounts ();
     276        gl_rwlock_unlock (my_rwlock);
     277        dbgprintf ("Checker %p after  check unlock\n", gl_thread_self_pointer ());
     278  
     279        yield ();
     280      }
     281  
     282    dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
     283    return NULL;
     284  }
     285  
     286  static void
     287  test_rwlock (void)
     288  {
     289    int i;
     290    gl_thread_t checkerthreads[THREAD_COUNT];
     291    gl_thread_t threads[THREAD_COUNT];
     292  
     293    /* Initialization.  */
     294    for (i = 0; i < ACCOUNT_COUNT; i++)
     295      account[i] = 1000;
     296    init_atomic_int (&rwlock_checker_done);
     297    set_atomic_int_value (&rwlock_checker_done, 0);
     298  
     299    /* Spawn the threads.  */
     300    for (i = 0; i < THREAD_COUNT; i++)
     301      checkerthreads[i] = gl_thread_create (rwlock_checker_thread, NULL);
     302    for (i = 0; i < THREAD_COUNT; i++)
     303      threads[i] = gl_thread_create (rwlock_mutator_thread, NULL);
     304  
     305    /* Wait for the threads to terminate.  */
     306    for (i = 0; i < THREAD_COUNT; i++)
     307      gl_thread_join (threads[i], NULL);
     308    set_atomic_int_value (&rwlock_checker_done, 1);
     309    for (i = 0; i < THREAD_COUNT; i++)
     310      gl_thread_join (checkerthreads[i], NULL);
     311    check_accounts ();
     312  }
     313  
     314  
     315  /* -------------------------- Test recursive locks -------------------------- */
     316  
     317  /* Test recursive locks by having several bank accounts and several threads
     318     which shuffle around money between the accounts (recursively) and another
     319     thread checking that all the money is still there.  */
     320  
     321  gl_recursive_lock_define_initialized(static, my_reclock)
     322  
     323  static void
     324  recshuffle (void)
     325  {
     326    int i1, i2, value;
     327  
     328    dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
     329    gl_recursive_lock_lock (my_reclock);
     330    dbgprintf ("Mutator %p after  lock\n", gl_thread_self_pointer ());
     331  
     332    i1 = random_account ();
     333    i2 = random_account ();
     334    value = ((unsigned int) rand () >> 3) % 10;
     335    account[i1] += value;
     336    account[i2] -= value;
     337  
     338    /* Recursive with probability 0.5.  */
     339    if (((unsigned int) rand () >> 3) % 2)
     340      recshuffle ();
     341  
     342    dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
     343    gl_recursive_lock_unlock (my_reclock);
     344    dbgprintf ("Mutator %p after  unlock\n", gl_thread_self_pointer ());
     345  }
     346  
     347  static void *
     348  reclock_mutator_thread (void *arg)
     349  {
     350    int repeat;
     351  
     352    for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
     353      {
     354        recshuffle ();
     355  
     356        dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
     357        gl_recursive_lock_lock (my_reclock);
     358        check_accounts ();
     359        gl_recursive_lock_unlock (my_reclock);
     360        dbgprintf ("Mutator %p after  check unlock\n", gl_thread_self_pointer ());
     361  
     362        yield ();
     363      }
     364  
     365    dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
     366    return NULL;
     367  }
     368  
     369  static struct atomic_int reclock_checker_done;
     370  
     371  static void *
     372  reclock_checker_thread (void *arg)
     373  {
     374    while (get_atomic_int_value (&reclock_checker_done) == 0)
     375      {
     376        dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
     377        gl_recursive_lock_lock (my_reclock);
     378        check_accounts ();
     379        gl_recursive_lock_unlock (my_reclock);
     380        dbgprintf ("Checker %p after  check unlock\n", gl_thread_self_pointer ());
     381  
     382        yield ();
     383      }
     384  
     385    dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
     386    return NULL;
     387  }
     388  
     389  static void
     390  test_recursive_lock (void)
     391  {
     392    int i;
     393    gl_thread_t checkerthread;
     394    gl_thread_t threads[THREAD_COUNT];
     395  
     396    /* Initialization.  */
     397    for (i = 0; i < ACCOUNT_COUNT; i++)
     398      account[i] = 1000;
     399    init_atomic_int (&reclock_checker_done);
     400    set_atomic_int_value (&reclock_checker_done, 0);
     401  
     402    /* Spawn the threads.  */
     403    checkerthread = gl_thread_create (reclock_checker_thread, NULL);
     404    for (i = 0; i < THREAD_COUNT; i++)
     405      threads[i] = gl_thread_create (reclock_mutator_thread, NULL);
     406  
     407    /* Wait for the threads to terminate.  */
     408    for (i = 0; i < THREAD_COUNT; i++)
     409      gl_thread_join (threads[i], NULL);
     410    set_atomic_int_value (&reclock_checker_done, 1);
     411    gl_thread_join (checkerthread, NULL);
     412    check_accounts ();
     413  }
     414  
     415  
     416  /* ------------------------ Test once-only execution ------------------------ */
     417  
     418  /* Test once-only execution by having several threads attempt to grab a
     419     once-only task simultaneously (triggered by releasing a read-write lock).  */
     420  
     421  gl_once_define(static, fresh_once)
     422  static int ready[THREAD_COUNT];
     423  static gl_lock_t ready_lock[THREAD_COUNT];
     424  #if ENABLE_LOCKING
     425  static gl_rwlock_t fire_signal[REPEAT_COUNT];
     426  #else
     427  static volatile int fire_signal_state;
     428  #endif
     429  static gl_once_t once_control;
     430  static int performed;
     431  gl_lock_define_initialized(static, performed_lock)
     432  
     433  static void
     434  once_execute (void)
     435  {
     436    gl_lock_lock (performed_lock);
     437    performed++;
     438    gl_lock_unlock (performed_lock);
     439  }
     440  
     441  static void *
     442  once_contender_thread (void *arg)
     443  {
     444    int id = (int) (intptr_t) arg;
     445    int repeat;
     446  
     447    for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
     448      {
     449        /* Tell the main thread that we're ready.  */
     450        gl_lock_lock (ready_lock[id]);
     451        ready[id] = 1;
     452        gl_lock_unlock (ready_lock[id]);
     453  
     454        if (repeat == REPEAT_COUNT)
     455          break;
     456  
     457        dbgprintf ("Contender %p waiting for signal for round %d\n",
     458                   gl_thread_self_pointer (), repeat);
     459  #if ENABLE_LOCKING
     460        /* Wait for the signal to go.  */
     461        gl_rwlock_rdlock (fire_signal[repeat]);
     462        /* And don't hinder the others (if the scheduler is unfair).  */
     463        gl_rwlock_unlock (fire_signal[repeat]);
     464  #else
     465        /* Wait for the signal to go.  */
     466        while (fire_signal_state <= repeat)
     467          yield ();
     468  #endif
     469        dbgprintf ("Contender %p got the     signal for round %d\n",
     470                   gl_thread_self_pointer (), repeat);
     471  
     472        /* Contend for execution.  */
     473        gl_once (once_control, once_execute);
     474      }
     475  
     476    return NULL;
     477  }
     478  
     479  static void
     480  test_once (void)
     481  {
     482    int i, repeat;
     483    gl_thread_t threads[THREAD_COUNT];
     484  
     485    /* Initialize all variables.  */
     486    for (i = 0; i < THREAD_COUNT; i++)
     487      {
     488        ready[i] = 0;
     489        gl_lock_init (ready_lock[i]);
     490      }
     491  #if ENABLE_LOCKING
     492    for (i = 0; i < REPEAT_COUNT; i++)
     493      gl_rwlock_init (fire_signal[i]);
     494  #else
     495    fire_signal_state = 0;
     496  #endif
     497  
     498  #if ENABLE_LOCKING
     499    /* Block all fire_signals.  */
     500    for (i = REPEAT_COUNT-1; i >= 0; i--)
     501      gl_rwlock_wrlock (fire_signal[i]);
     502  #endif
     503  
     504    /* Spawn the threads.  */
     505    for (i = 0; i < THREAD_COUNT; i++)
     506      threads[i] =
     507        gl_thread_create (once_contender_thread, (void *) (intptr_t) i);
     508  
     509    for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
     510      {
     511        /* Wait until every thread is ready.  */
     512        dbgprintf ("Main thread before synchronizing for round %d\n", repeat);
     513        for (;;)
     514          {
     515            int ready_count = 0;
     516            for (i = 0; i < THREAD_COUNT; i++)
     517              {
     518                gl_lock_lock (ready_lock[i]);
     519                ready_count += ready[i];
     520                gl_lock_unlock (ready_lock[i]);
     521              }
     522            if (ready_count == THREAD_COUNT)
     523              break;
     524            yield ();
     525          }
     526        dbgprintf ("Main thread after  synchronizing for round %d\n", repeat);
     527  
     528        if (repeat > 0)
     529          {
     530            /* Check that exactly one thread executed the once_execute()
     531               function.  */
     532            if (performed != 1)
     533              abort ();
     534          }
     535  
     536        if (repeat == REPEAT_COUNT)
     537          break;
     538  
     539        /* Preparation for the next round: Initialize once_control.  */
     540        memcpy (&once_control, &fresh_once, sizeof (gl_once_t));
     541  
     542        /* Preparation for the next round: Reset the performed counter.  */
     543        performed = 0;
     544  
     545        /* Preparation for the next round: Reset the ready flags.  */
     546        for (i = 0; i < THREAD_COUNT; i++)
     547          {
     548            gl_lock_lock (ready_lock[i]);
     549            ready[i] = 0;
     550            gl_lock_unlock (ready_lock[i]);
     551          }
     552  
     553        /* Signal all threads simultaneously.  */
     554        dbgprintf ("Main thread giving signal for round %d\n", repeat);
     555  #if ENABLE_LOCKING
     556        gl_rwlock_unlock (fire_signal[repeat]);
     557  #else
     558        fire_signal_state = repeat + 1;
     559  #endif
     560      }
     561  
     562    /* Wait for the threads to terminate.  */
     563    for (i = 0; i < THREAD_COUNT; i++)
     564      gl_thread_join (threads[i], NULL);
     565  }
     566  
     567  
     568  /* -------------------------------------------------------------------------- */
     569  
     570  int
     571  main ()
     572  {
     573  #if HAVE_DECL_ALARM
     574    /* Declare failure if test takes too long, by using default abort
     575       caused by SIGALRM.  */
     576    int alarm_value = 600;
     577    signal (SIGALRM, SIG_DFL);
     578    alarm (alarm_value);
     579  #endif
     580  
     581  #if DO_TEST_LOCK
     582    printf ("Starting test_lock ..."); fflush (stdout);
     583    test_lock ();
     584    printf (" OK\n"); fflush (stdout);
     585  #endif
     586  #if DO_TEST_RWLOCK
     587    printf ("Starting test_rwlock ..."); fflush (stdout);
     588    test_rwlock ();
     589    printf (" OK\n"); fflush (stdout);
     590  #endif
     591  #if DO_TEST_RECURSIVE_LOCK
     592    printf ("Starting test_recursive_lock ..."); fflush (stdout);
     593    test_recursive_lock ();
     594    printf (" OK\n"); fflush (stdout);
     595  #endif
     596  #if DO_TEST_ONCE
     597    printf ("Starting test_once ..."); fflush (stdout);
     598    test_once ();
     599    printf (" OK\n"); fflush (stdout);
     600  #endif
     601  
     602    return 0;
     603  }
     604  
     605  #else
     606  
     607  /* No multithreading available.  */
     608  
     609  #include <stdio.h>
     610  
     611  int
     612  main ()
     613  {
     614    fputs ("Skipping test: multithreading not enabled\n", stderr);
     615    return 77;
     616  }
     617  
     618  #endif