root/maint/gnulib/tests/test-mtx.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. random_account
  2. check_accounts
  3. lock_mutator_thread
  4. lock_checker_thread
  5. test_mtx_plain
  6. recshuffle
  7. reclock_mutator_thread
  8. reclock_checker_thread
  9. test_mtx_recursive
  10. once_execute
  11. once_contender_thread
  12. test_once
  13. main

   1 /* Test of locking in multithreaded situations.
   2    Copyright (C) 2005, 2008-2021 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 /* Whether to enable locking.
  22    Uncomment this to get a test program without locking, to verify that
  23    it crashes.  */
  24 #define ENABLE_LOCKING 1
  25 
  26 /* Which tests to perform.
  27    Uncomment some of these, to verify that all tests crash if no locking
  28    is enabled.  */
  29 #define DO_TEST_LOCK 1
  30 #define DO_TEST_RECURSIVE_LOCK 1
  31 #define DO_TEST_ONCE 1
  32 
  33 /* Whether to help the scheduler through explicit thrd_yield().
  34    Uncomment this to see if the operating system has a fair scheduler.  */
  35 #define EXPLICIT_YIELD 1
  36 
  37 /* Whether to print debugging messages.  */
  38 #define ENABLE_DEBUGGING 0
  39 
  40 /* Number of simultaneous threads.  */
  41 #define THREAD_COUNT 10
  42 
  43 /* Number of operations performed in each thread.
  44    This is quite high, because with a smaller count, say 5000, we often get
  45    an "OK" result even without ENABLE_LOCKING (on Linux/x86).  */
  46 #define REPEAT_COUNT 50000
  47 
  48 #include <threads.h>
  49 #include <stdint.h>
  50 #include <stdio.h>
  51 #include <stdlib.h>
  52 #include <string.h>
  53 
  54 #include "glthread/lock.h"
  55 
  56 #if HAVE_DECL_ALARM
  57 # include <signal.h>
  58 # include <unistd.h>
  59 #endif
  60 
  61 #include "macros.h"
  62 #include "atomic-int-isoc.h"
  63 
  64 #if ENABLE_DEBUGGING
  65 # define dbgprintf printf
  66 #else
  67 # define dbgprintf if (0) printf
  68 #endif
  69 
  70 #if EXPLICIT_YIELD
  71 # define yield() thrd_yield ()
  72 #else
  73 # define yield()
  74 #endif
  75 
  76 /* Returns a reference to the current thread as a pointer, for debugging.  */
  77 #if defined __MVS__
  78   /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
  79      The first three bytes of this field appear to uniquely identify a
  80      pthread_t, though not necessarily representing a pointer.  */
  81 # define thrd_current_pointer() (*((void **) thrd_current ().__))
  82 #elif defined __sun
  83   /* On Solaris, thrd_t is merely an 'unsigned int'.  */
  84 # define thrd_current_pointer() ((void *) (uintptr_t) thrd_current ())
  85 #else
  86 # define thrd_current_pointer() ((void *) thrd_current ())
  87 #endif
  88 
  89 #define ACCOUNT_COUNT 4
  90 
  91 static int account[ACCOUNT_COUNT];
  92 
  93 static int
  94 random_account (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  95 {
  96   return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
  97 }
  98 
  99 static void
 100 check_accounts (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 101 {
 102   int i, sum;
 103 
 104   sum = 0;
 105   for (i = 0; i < ACCOUNT_COUNT; i++)
 106     sum += account[i];
 107   if (sum != ACCOUNT_COUNT * 1000)
 108     abort ();
 109 }
 110 
 111 
 112 /* ------------------- Test normal (non-recursive) locks ------------------- */
 113 
 114 /* Test normal locks by having several bank accounts and several threads
 115    which shuffle around money between the accounts and another thread
 116    checking that all the money is still there.  */
 117 
 118 static mtx_t my_lock;
 119 
 120 static int
 121 lock_mutator_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 122 {
 123   int repeat;
 124 
 125   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
 126     {
 127       int i1, i2, value;
 128 
 129       dbgprintf ("Mutator %p before lock\n", thrd_current_pointer ());
 130       ASSERT (mtx_lock (&my_lock) == thrd_success);
 131       dbgprintf ("Mutator %p after  lock\n", thrd_current_pointer ());
 132 
 133       i1 = random_account ();
 134       i2 = random_account ();
 135       value = ((unsigned int) rand () >> 3) % 10;
 136       account[i1] += value;
 137       account[i2] -= value;
 138 
 139       dbgprintf ("Mutator %p before unlock\n", thrd_current_pointer ());
 140       ASSERT (mtx_unlock (&my_lock) == thrd_success);
 141       dbgprintf ("Mutator %p after  unlock\n", thrd_current_pointer ());
 142 
 143       dbgprintf ("Mutator %p before check lock\n", thrd_current_pointer ());
 144       ASSERT (mtx_lock (&my_lock) == thrd_success);
 145       check_accounts ();
 146       ASSERT (mtx_unlock (&my_lock) == thrd_success);
 147       dbgprintf ("Mutator %p after  check unlock\n", thrd_current_pointer ());
 148 
 149       yield ();
 150     }
 151 
 152   dbgprintf ("Mutator %p dying.\n", thrd_current_pointer ());
 153   return 0;
 154 }
 155 
 156 static struct atomic_int lock_checker_done;
 157 
 158 static int
 159 lock_checker_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 160 {
 161   while (get_atomic_int_value (&lock_checker_done) == 0)
 162     {
 163       dbgprintf ("Checker %p before check lock\n", thrd_current_pointer ());
 164       ASSERT (mtx_lock (&my_lock) == thrd_success);
 165       check_accounts ();
 166       ASSERT (mtx_unlock (&my_lock) == thrd_success);
 167       dbgprintf ("Checker %p after  check unlock\n", thrd_current_pointer ());
 168 
 169       yield ();
 170     }
 171 
 172   dbgprintf ("Checker %p dying.\n", thrd_current_pointer ());
 173   return 0;
 174 }
 175 
 176 static void
 177 test_mtx_plain (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 178 {
 179   int i;
 180   thrd_t checkerthread;
 181   thrd_t threads[THREAD_COUNT];
 182 
 183   /* Initialization.  */
 184   for (i = 0; i < ACCOUNT_COUNT; i++)
 185     account[i] = 1000;
 186   init_atomic_int (&lock_checker_done);
 187   set_atomic_int_value (&lock_checker_done, 0);
 188 
 189   /* Spawn the threads.  */
 190   ASSERT (thrd_create (&checkerthread, lock_checker_thread, NULL)
 191           == thrd_success);
 192   for (i = 0; i < THREAD_COUNT; i++)
 193     ASSERT (thrd_create (&threads[i], lock_mutator_thread, NULL)
 194             == thrd_success);
 195 
 196   /* Wait for the threads to terminate.  */
 197   for (i = 0; i < THREAD_COUNT; i++)
 198     ASSERT (thrd_join (threads[i], NULL) == thrd_success);
 199   set_atomic_int_value (&lock_checker_done, 1);
 200   ASSERT (thrd_join (checkerthread, NULL) == thrd_success);
 201   check_accounts ();
 202 }
 203 
 204 
 205 /* -------------------------- Test recursive locks -------------------------- */
 206 
 207 /* Test recursive locks by having several bank accounts and several threads
 208    which shuffle around money between the accounts (recursively) and another
 209    thread checking that all the money is still there.  */
 210 
 211 static mtx_t my_reclock;
 212 
 213 static void
 214 recshuffle (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 215 {
 216   int i1, i2, value;
 217 
 218   dbgprintf ("Mutator %p before lock\n", thrd_current_pointer ());
 219   ASSERT (mtx_lock (&my_reclock) == thrd_success);
 220   dbgprintf ("Mutator %p after  lock\n", thrd_current_pointer ());
 221 
 222   i1 = random_account ();
 223   i2 = random_account ();
 224   value = ((unsigned int) rand () >> 3) % 10;
 225   account[i1] += value;
 226   account[i2] -= value;
 227 
 228   /* Recursive with probability 0.5.  */
 229   if (((unsigned int) rand () >> 3) % 2)
 230     recshuffle ();
 231 
 232   dbgprintf ("Mutator %p before unlock\n", thrd_current_pointer ());
 233   ASSERT (mtx_unlock (&my_reclock) == thrd_success);
 234   dbgprintf ("Mutator %p after  unlock\n", thrd_current_pointer ());
 235 }
 236 
 237 static int
 238 reclock_mutator_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 239 {
 240   int repeat;
 241 
 242   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
 243     {
 244       recshuffle ();
 245 
 246       dbgprintf ("Mutator %p before check lock\n", thrd_current_pointer ());
 247       ASSERT (mtx_lock (&my_reclock) == thrd_success);
 248       check_accounts ();
 249       ASSERT (mtx_unlock (&my_reclock) == thrd_success);
 250       dbgprintf ("Mutator %p after  check unlock\n", thrd_current_pointer ());
 251 
 252       yield ();
 253     }
 254 
 255   dbgprintf ("Mutator %p dying.\n", thrd_current_pointer ());
 256   return 0;
 257 }
 258 
 259 static struct atomic_int reclock_checker_done;
 260 
 261 static int
 262 reclock_checker_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 263 {
 264   while (get_atomic_int_value (&reclock_checker_done) == 0)
 265     {
 266       dbgprintf ("Checker %p before check lock\n", thrd_current_pointer ());
 267       ASSERT (mtx_lock (&my_reclock) == thrd_success);
 268       check_accounts ();
 269       ASSERT (mtx_unlock (&my_reclock) == thrd_success);
 270       dbgprintf ("Checker %p after  check unlock\n", thrd_current_pointer ());
 271 
 272       yield ();
 273     }
 274 
 275   dbgprintf ("Checker %p dying.\n", thrd_current_pointer ());
 276   return 0;
 277 }
 278 
 279 static void
 280 test_mtx_recursive (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 281 {
 282   int i;
 283   thrd_t checkerthread;
 284   thrd_t threads[THREAD_COUNT];
 285 
 286   /* Initialization.  */
 287   for (i = 0; i < ACCOUNT_COUNT; i++)
 288     account[i] = 1000;
 289   init_atomic_int (&reclock_checker_done);
 290   set_atomic_int_value (&reclock_checker_done, 0);
 291 
 292   /* Spawn the threads.  */
 293   ASSERT (thrd_create (&checkerthread, reclock_checker_thread, NULL)
 294           == thrd_success);
 295   for (i = 0; i < THREAD_COUNT; i++)
 296     ASSERT (thrd_create (&threads[i], reclock_mutator_thread, NULL)
 297             == thrd_success);
 298 
 299   /* Wait for the threads to terminate.  */
 300   for (i = 0; i < THREAD_COUNT; i++)
 301     ASSERT (thrd_join (threads[i], NULL) == thrd_success);
 302   set_atomic_int_value (&reclock_checker_done, 1);
 303   ASSERT (thrd_join (checkerthread, NULL) == thrd_success);
 304   check_accounts ();
 305 }
 306 
 307 
 308 /* ------------------------ Test once-only execution ------------------------ */
 309 
 310 /* Test once-only execution by having several threads attempt to grab a
 311    once-only task simultaneously (triggered by releasing a read-write lock).  */
 312 
 313 static once_flag fresh_once = ONCE_FLAG_INIT;
 314 static int ready[THREAD_COUNT];
 315 static mtx_t ready_lock[THREAD_COUNT];
 316 #if ENABLE_LOCKING
 317 static gl_rwlock_t fire_signal[REPEAT_COUNT];
 318 #else
 319 static volatile int fire_signal_state;
 320 #endif
 321 static once_flag once_control;
 322 static int performed;
 323 static mtx_t performed_lock;
 324 
 325 static void
 326 once_execute (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 327 {
 328   ASSERT (mtx_lock (&performed_lock) == thrd_success);
 329   performed++;
 330   ASSERT (mtx_unlock (&performed_lock) == thrd_success);
 331 }
 332 
 333 static int
 334 once_contender_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 335 {
 336   int id = (int) (intptr_t) arg;
 337   int repeat;
 338 
 339   for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
 340     {
 341       /* Tell the main thread that we're ready.  */
 342       ASSERT (mtx_lock (&ready_lock[id]) == thrd_success);
 343       ready[id] = 1;
 344       ASSERT (mtx_unlock (&ready_lock[id]) == thrd_success);
 345 
 346       if (repeat == REPEAT_COUNT)
 347         break;
 348 
 349       dbgprintf ("Contender %p waiting for signal for round %d\n",
 350                  thrd_current_pointer (), repeat);
 351 #if ENABLE_LOCKING
 352       /* Wait for the signal to go.  */
 353       gl_rwlock_rdlock (fire_signal[repeat]);
 354       /* And don't hinder the others (if the scheduler is unfair).  */
 355       gl_rwlock_unlock (fire_signal[repeat]);
 356 #else
 357       /* Wait for the signal to go.  */
 358       while (fire_signal_state <= repeat)
 359         yield ();
 360 #endif
 361       dbgprintf ("Contender %p got the     signal for round %d\n",
 362                  thrd_current_pointer (), repeat);
 363 
 364       /* Contend for execution.  */
 365       call_once (&once_control, once_execute);
 366     }
 367 
 368   return 0;
 369 }
 370 
 371 static void
 372 test_once (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 373 {
 374   int i, repeat;
 375   thrd_t threads[THREAD_COUNT];
 376 
 377   /* Initialize all variables.  */
 378   for (i = 0; i < THREAD_COUNT; i++)
 379     {
 380       ready[i] = 0;
 381       ASSERT (mtx_init (&ready_lock[i], mtx_plain) == thrd_success);
 382     }
 383 #if ENABLE_LOCKING
 384   for (i = 0; i < REPEAT_COUNT; i++)
 385     gl_rwlock_init (fire_signal[i]);
 386 #else
 387   fire_signal_state = 0;
 388 #endif
 389 
 390 #if ENABLE_LOCKING
 391   /* Block all fire_signals.  */
 392   for (i = REPEAT_COUNT-1; i >= 0; i--)
 393     gl_rwlock_wrlock (fire_signal[i]);
 394 #endif
 395 
 396   /* Spawn the threads.  */
 397   for (i = 0; i < THREAD_COUNT; i++)
 398     ASSERT (thrd_create (&threads[i],
 399                          once_contender_thread, (void *) (intptr_t) i)
 400             == thrd_success);
 401 
 402   for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
 403     {
 404       /* Wait until every thread is ready.  */
 405       dbgprintf ("Main thread before synchronizing for round %d\n", repeat);
 406       for (;;)
 407         {
 408           int ready_count = 0;
 409           for (i = 0; i < THREAD_COUNT; i++)
 410             {
 411               ASSERT (mtx_lock (&ready_lock[i]) == thrd_success);
 412               ready_count += ready[i];
 413               ASSERT (mtx_unlock (&ready_lock[i]) == thrd_success);
 414             }
 415           if (ready_count == THREAD_COUNT)
 416             break;
 417           yield ();
 418         }
 419       dbgprintf ("Main thread after  synchronizing for round %d\n", repeat);
 420 
 421       if (repeat > 0)
 422         {
 423           /* Check that exactly one thread executed the once_execute()
 424              function.  */
 425           if (performed != 1)
 426             abort ();
 427         }
 428 
 429       if (repeat == REPEAT_COUNT)
 430         break;
 431 
 432       /* Preparation for the next round: Initialize once_control.  */
 433       memcpy (&once_control, &fresh_once, sizeof (once_flag));
 434 
 435       /* Preparation for the next round: Reset the performed counter.  */
 436       performed = 0;
 437 
 438       /* Preparation for the next round: Reset the ready flags.  */
 439       for (i = 0; i < THREAD_COUNT; i++)
 440         {
 441           ASSERT (mtx_lock (&ready_lock[i]) == thrd_success);
 442           ready[i] = 0;
 443           ASSERT (mtx_unlock (&ready_lock[i]) == thrd_success);
 444         }
 445 
 446       /* Signal all threads simultaneously.  */
 447       dbgprintf ("Main thread giving signal for round %d\n", repeat);
 448 #if ENABLE_LOCKING
 449       gl_rwlock_unlock (fire_signal[repeat]);
 450 #else
 451       fire_signal_state = repeat + 1;
 452 #endif
 453     }
 454 
 455   /* Wait for the threads to terminate.  */
 456   for (i = 0; i < THREAD_COUNT; i++)
 457     ASSERT (thrd_join (threads[i], NULL) == thrd_success);
 458 }
 459 
 460 
 461 /* -------------------------------------------------------------------------- */
 462 
 463 int
 464 main ()
     /* [previous][next][first][last][top][bottom][index][help] */
 465 {
 466 #if HAVE_DECL_ALARM
 467   /* Declare failure if test takes too long, by using default abort
 468      caused by SIGALRM.  */
 469   int alarm_value = 600;
 470   signal (SIGALRM, SIG_DFL);
 471   alarm (alarm_value);
 472 #endif
 473 
 474   ASSERT (mtx_init (&my_lock, mtx_plain) == thrd_success);
 475   ASSERT (mtx_init (&my_reclock, mtx_plain | mtx_recursive) == thrd_success);
 476   ASSERT (mtx_init (&performed_lock, mtx_plain) == thrd_success);
 477 
 478 #if DO_TEST_LOCK
 479   printf ("Starting test_mtx_plain ..."); fflush (stdout);
 480   test_mtx_plain ();
 481   printf (" OK\n"); fflush (stdout);
 482 #endif
 483 #if DO_TEST_RECURSIVE_LOCK
 484   printf ("Starting test_mtx_recursive ..."); fflush (stdout);
 485   test_mtx_recursive ();
 486   printf (" OK\n"); fflush (stdout);
 487 #endif
 488 #if DO_TEST_ONCE
 489   printf ("Starting test_once ..."); fflush (stdout);
 490   test_once ();
 491   printf (" OK\n"); fflush (stdout);
 492 #endif
 493 
 494   return 0;
 495 }

/* [previous][next][first][last][top][bottom][index][help] */