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

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

DEFINITIONS

This source file includes following definitions.
  1. random_account
  2. check_accounts
  3. gl_lock_define_initialized
  4. lock_checker_thread
  5. test_lock
  6. gl_rwlock_define_initialized
  7. rwlock_checker_thread
  8. test_rwlock
  9. gl_recursive_lock_define_initialized
  10. reclock_mutator_thread
  11. reclock_checker_thread
  12. test_recursive_lock
  13. gl_once_define
  14. once_contender_thread
  15. test_once
  16. main
  17. 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 #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)
     /* [previous][next][first][last][top][bottom][index][help] */
 120 {
 121   return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
 122 }
 123 
 124 static void
 125 check_accounts (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 ()
     /* [previous][next][first][last][top][bottom][index][help] */
 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 ()
     /* [previous][next][first][last][top][bottom][index][help] */
 613 {
 614   fputs ("Skipping test: multithreading not enabled\n", stderr);
 615   return 77;
 616 }
 617 
 618 #endif

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