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

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

DEFINITIONS

This source file includes following definitions.
  1. perhaps_yield
  2. worker_thread
  3. test_tss
  4. inc_sum
  5. destructor0
  6. destructor1
  7. destructor2
  8. destructor3
  9. destructor4
  10. destructor5
  11. destructor6
  12. destructor7
  13. destructor8
  14. destructor9
  15. dtorcheck1_thread
  16. test_tss_dtorcheck1
  17. dtorcheck2_thread
  18. test_tss_dtorcheck2
  19. racecheck_thread
  20. test_tss_racecheck
  21. main

   1 /* Test of thread-local storage 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 help the scheduler through explicit thrd_yield().
  22    Uncomment this to see if the operating system has a fair scheduler.  */
  23 #define EXPLICIT_YIELD 1
  24 
  25 /* Whether to print debugging messages.  */
  26 #define ENABLE_DEBUGGING 0
  27 
  28 #include <threads.h>
  29 #include <stdint.h>
  30 #include <stdio.h>
  31 #include <stdlib.h>
  32 #include <string.h>
  33 
  34 #if HAVE_DECL_ALARM
  35 # include <signal.h>
  36 # include <unistd.h>
  37 #endif
  38 
  39 #include "macros.h"
  40 
  41 #if ENABLE_DEBUGGING
  42 # define dbgprintf printf
  43 #else
  44 # define dbgprintf if (0) printf
  45 #endif
  46 
  47 #if EXPLICIT_YIELD
  48 # define yield() thrd_yield ()
  49 #else
  50 # define yield()
  51 #endif
  52 
  53 /* Returns a reference to the current thread as a pointer, for debugging.  */
  54 #if defined __MVS__
  55   /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
  56      The first three bytes of this field appear to uniquely identify a
  57      pthread_t, though not necessarily representing a pointer.  */
  58 # define thrd_current_pointer() (*((void **) thrd_current ().__))
  59 #elif defined __sun
  60   /* On Solaris, thrd_t is merely an 'unsigned int'.  */
  61 # define thrd_current_pointer() ((void *) (uintptr_t) thrd_current ())
  62 #else
  63 # define thrd_current_pointer() ((void *) thrd_current ())
  64 #endif
  65 
  66 static void
  67 perhaps_yield (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  68 {
  69   /* Call yield () only with a certain probability, otherwise the
  70      sequence of thread activations may be too predictable.  */
  71   if ((((unsigned int) rand () >> 3) % 4) == 0)
  72     yield ();
  73 }
  74 
  75 
  76 /* ----------------------- Test thread-local storage ----------------------- */
  77 
  78 /* Number of simultaneous threads.  */
  79 #define THREAD_COUNT 16
  80 
  81 /* Number of operations performed in each thread.  */
  82 #define REPEAT_COUNT 50000
  83 
  84 #define KEYS_COUNT 4
  85 
  86 static tss_t mykeys[KEYS_COUNT];
  87 
  88 static int
  89 worker_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
  90 {
  91   unsigned int id = (unsigned int) (uintptr_t) arg;
  92   int i, j, repeat;
  93   unsigned int values[KEYS_COUNT];
  94 
  95   dbgprintf ("Worker %p started\n", thrd_current_pointer ());
  96 
  97   /* Initialize the per-thread storage.  */
  98   for (i = 0; i < KEYS_COUNT; i++)
  99     {
 100       values[i] = (((unsigned int) rand () >> 3) % 1000000) * THREAD_COUNT + id;
 101       /* Hopefully no arithmetic overflow.  */
 102       if ((values[i] % THREAD_COUNT) != id)
 103         abort ();
 104     }
 105   perhaps_yield ();
 106 
 107   /* Verify that the initial value is NULL.  */
 108   dbgprintf ("Worker %p before initial verify\n", thrd_current_pointer ());
 109   for (i = 0; i < KEYS_COUNT; i++)
 110     if (tss_get (mykeys[i]) != NULL)
 111       abort ();
 112   dbgprintf ("Worker %p after  initial verify\n", thrd_current_pointer ());
 113   perhaps_yield ();
 114 
 115   /* Initialize the per-thread storage.  */
 116   dbgprintf ("Worker %p before first tss_set\n", thrd_current_pointer ());
 117   for (i = 0; i < KEYS_COUNT; i++)
 118     {
 119       unsigned int *ptr = (unsigned int *) malloc (sizeof (unsigned int));
 120       *ptr = values[i];
 121       ASSERT (tss_set (mykeys[i], ptr) == thrd_success);
 122     }
 123   dbgprintf ("Worker %p after  first tss_set\n", thrd_current_pointer ());
 124   perhaps_yield ();
 125 
 126   /* Shuffle around the pointers.  */
 127   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
 128     {
 129       dbgprintf ("Worker %p doing value swapping\n", thrd_current_pointer ());
 130       i = ((unsigned int) rand () >> 3) % KEYS_COUNT;
 131       j = ((unsigned int) rand () >> 3) % KEYS_COUNT;
 132       if (i != j)
 133         {
 134           void *vi = tss_get (mykeys[i]);
 135           void *vj = tss_get (mykeys[j]);
 136 
 137           ASSERT (tss_set (mykeys[i], vj) == thrd_success);
 138           ASSERT (tss_set (mykeys[j], vi) == thrd_success);
 139         }
 140       perhaps_yield ();
 141     }
 142 
 143   /* Verify that all the values are from this thread.  */
 144   dbgprintf ("Worker %p before final verify\n", thrd_current_pointer ());
 145   for (i = 0; i < KEYS_COUNT; i++)
 146     if ((*(unsigned int *) tss_get (mykeys[i]) % THREAD_COUNT) != id)
 147       abort ();
 148   dbgprintf ("Worker %p after  final verify\n", thrd_current_pointer ());
 149   perhaps_yield ();
 150 
 151   dbgprintf ("Worker %p dying.\n", thrd_current_pointer ());
 152   return 0;
 153 }
 154 
 155 static void
 156 test_tss (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 157 {
 158   int pass, i;
 159 
 160   for (pass = 0; pass < 2; pass++)
 161     {
 162       thrd_t threads[THREAD_COUNT];
 163 
 164       if (pass == 0)
 165         for (i = 0; i < KEYS_COUNT; i++)
 166           ASSERT (tss_create (&mykeys[i], free) == thrd_success);
 167       else
 168         for (i = KEYS_COUNT - 1; i >= 0; i--)
 169           ASSERT (tss_create (&mykeys[i], free) == thrd_success);
 170 
 171       /* Spawn the threads.  */
 172       for (i = 0; i < THREAD_COUNT; i++)
 173         ASSERT (thrd_create (&threads[i], worker_thread, (void *) (uintptr_t) i)
 174                 == thrd_success);
 175 
 176       /* Wait for the threads to terminate.  */
 177       for (i = 0; i < THREAD_COUNT; i++)
 178         ASSERT (thrd_join (threads[i], NULL) == thrd_success);
 179 
 180       for (i = 0; i < KEYS_COUNT; i++)
 181         tss_delete (mykeys[i]);
 182     }
 183 }
 184 
 185 #undef KEYS_COUNT
 186 #undef REPEAT_COUNT
 187 #undef THREAD_COUNT
 188 
 189 
 190 /* --------------- Test thread-local storage with destructors --------------- */
 191 
 192 /* Number of simultaneous threads.  */
 193 #define THREAD_COUNT 10
 194 
 195 /* Number of keys to allocate in each thread.  */
 196 #define KEYS_COUNT 10
 197 
 198 static mtx_t sumlock;
 199 static uintptr_t sum;
 200 
 201 static void
 202 inc_sum (uintptr_t value)
     /* [previous][next][first][last][top][bottom][index][help] */
 203 {
 204   ASSERT (mtx_lock (&sumlock) == thrd_success);
 205   sum += value;
 206   ASSERT (mtx_unlock (&sumlock) == thrd_success);
 207 }
 208 
 209 static void
 210 destructor0 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 211 {
 212   if ((((uintptr_t) value - 1) % 10) != 0)
 213     abort ();
 214   inc_sum ((uintptr_t) value);
 215 }
 216 
 217 static void
 218 destructor1 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 219 {
 220   if ((((uintptr_t) value - 1) % 10) != 1)
 221     abort ();
 222   inc_sum ((uintptr_t) value);
 223 }
 224 
 225 static void
 226 destructor2 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 227 {
 228   if ((((uintptr_t) value - 1) % 10) != 2)
 229     abort ();
 230   inc_sum ((uintptr_t) value);
 231 }
 232 
 233 static void
 234 destructor3 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 235 {
 236   if ((((uintptr_t) value - 1) % 10) != 3)
 237     abort ();
 238   inc_sum ((uintptr_t) value);
 239 }
 240 
 241 static void
 242 destructor4 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 243 {
 244   if ((((uintptr_t) value - 1) % 10) != 4)
 245     abort ();
 246   inc_sum ((uintptr_t) value);
 247 }
 248 
 249 static void
 250 destructor5 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 251 {
 252   if ((((uintptr_t) value - 1) % 10) != 5)
 253     abort ();
 254   inc_sum ((uintptr_t) value);
 255 }
 256 
 257 static void
 258 destructor6 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 259 {
 260   if ((((uintptr_t) value - 1) % 10) != 6)
 261     abort ();
 262   inc_sum ((uintptr_t) value);
 263 }
 264 
 265 static void
 266 destructor7 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 267 {
 268   if ((((uintptr_t) value - 1) % 10) != 7)
 269     abort ();
 270   inc_sum ((uintptr_t) value);
 271 }
 272 
 273 static void
 274 destructor8 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 275 {
 276   if ((((uintptr_t) value - 1) % 10) != 8)
 277     abort ();
 278   inc_sum ((uintptr_t) value);
 279 }
 280 
 281 static void
 282 destructor9 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 283 {
 284   if ((((uintptr_t) value - 1) % 10) != 9)
 285     abort ();
 286   inc_sum ((uintptr_t) value);
 287 }
 288 
 289 static void (*destructor_table[10]) (void *) =
 290   {
 291     destructor0,
 292     destructor1,
 293     destructor2,
 294     destructor3,
 295     destructor4,
 296     destructor5,
 297     destructor6,
 298     destructor7,
 299     destructor8,
 300     destructor9
 301   };
 302 
 303 static tss_t dtorcheck_keys[THREAD_COUNT][KEYS_COUNT];
 304 
 305 /* Worker thread that uses destructors that verify that the destructor belongs
 306    to the right thread.  */
 307 static int
 308 dtorcheck1_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 309 {
 310   unsigned int id = (unsigned int) (uintptr_t) arg;
 311   tss_t *keys = dtorcheck_keys[id]; /* an array of KEYS_COUNT keys */
 312   int i;
 313 
 314   for (i = 0; i < KEYS_COUNT; i++)
 315     ASSERT (tss_create (&keys[i], destructor_table[i]) == thrd_success);
 316 
 317   for (i = 0; i < KEYS_COUNT; i++)
 318     ASSERT (tss_set (keys[i], (void *) (uintptr_t) (10 * id + i + 1))
 319             == thrd_success);
 320 
 321   return 0;
 322 }
 323 
 324 static void
 325 test_tss_dtorcheck1 (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 326 {
 327   thrd_t threads[THREAD_COUNT];
 328   unsigned int id;
 329   int i;
 330   uintptr_t expected_sum;
 331 
 332   sum = 0;
 333 
 334   /* Spawn the threads.  */
 335   for (id = 0; id < THREAD_COUNT; id++)
 336     ASSERT (thrd_create (&threads[id], dtorcheck1_thread, (void *) (uintptr_t) id)
 337             == thrd_success);
 338 
 339   /* Wait for the threads to terminate.  */
 340   for (id = 0; id < THREAD_COUNT; id++)
 341     ASSERT (thrd_join (threads[id], NULL) == thrd_success);
 342 
 343   /* Clean up the keys.  */
 344   for (id = 0; id < THREAD_COUNT; id++)
 345     for (i = 0; i < KEYS_COUNT; i++)
 346       tss_delete (dtorcheck_keys[id][i]);
 347 
 348   /* Check that the destructor was invoked for each key.  */
 349   expected_sum = 10 * KEYS_COUNT * (THREAD_COUNT * (THREAD_COUNT - 1) / 2)
 350                  + THREAD_COUNT * (KEYS_COUNT * (KEYS_COUNT - 1) / 2)
 351                  + THREAD_COUNT * KEYS_COUNT;
 352   if (sum != expected_sum)
 353     abort ();
 354 }
 355 
 356 /* Worker thread that uses destructors that verify that the destructor belongs
 357    to the right key allocated within the thread.  */
 358 static int
 359 dtorcheck2_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 360 {
 361   unsigned int id = (unsigned int) (uintptr_t) arg;
 362   tss_t *keys = dtorcheck_keys[id]; /* an array of KEYS_COUNT keys */
 363   int i;
 364 
 365   for (i = 0; i < KEYS_COUNT; i++)
 366     ASSERT (tss_create (&keys[i], destructor_table[id]) == thrd_success);
 367 
 368   for (i = 0; i < KEYS_COUNT; i++)
 369     ASSERT (tss_set (keys[i], (void *) (uintptr_t) (10 * i + id + 1))
 370             == thrd_success);
 371 
 372   return 0;
 373 }
 374 
 375 static void
 376 test_tss_dtorcheck2 (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 377 {
 378   thrd_t threads[THREAD_COUNT];
 379   unsigned int id;
 380   int i;
 381   uintptr_t expected_sum;
 382 
 383   sum = 0;
 384 
 385   /* Spawn the threads.  */
 386   for (id = 0; id < THREAD_COUNT; id++)
 387     ASSERT (thrd_create (&threads[id], dtorcheck2_thread, (void *) (uintptr_t) id)
 388             == thrd_success);
 389 
 390   /* Wait for the threads to terminate.  */
 391   for (id = 0; id < THREAD_COUNT; id++)
 392     ASSERT (thrd_join (threads[id], NULL) == thrd_success);
 393 
 394   /* Clean up the keys.  */
 395   for (id = 0; id < THREAD_COUNT; id++)
 396     for (i = 0; i < KEYS_COUNT; i++)
 397       tss_delete (dtorcheck_keys[id][i]);
 398 
 399   /* Check that the destructor was invoked for each key.  */
 400   expected_sum = 10 * THREAD_COUNT * (KEYS_COUNT * (KEYS_COUNT - 1) / 2)
 401                  + KEYS_COUNT * (THREAD_COUNT * (THREAD_COUNT - 1) / 2)
 402                  + THREAD_COUNT * KEYS_COUNT;
 403   if (sum != expected_sum)
 404     abort ();
 405 }
 406 
 407 #undef KEYS_COUNT
 408 #undef THREAD_COUNT
 409 
 410 
 411 /* --- Test thread-local storage with with races between init and destroy --- */
 412 
 413 /* Number of simultaneous threads.  */
 414 #define THREAD_COUNT 10
 415 
 416 /* Number of keys to allocate in each thread.  */
 417 #define KEYS_COUNT 10
 418 
 419 /* Number of times to destroy and reallocate a key in each thread.  */
 420 #define REPEAT_COUNT 100000
 421 
 422 static tss_t racecheck_keys[THREAD_COUNT][KEYS_COUNT];
 423 
 424 /* Worker thread that does many destructions and reallocations of keys, and also
 425    uses destructors that verify that the destructor belongs to the right key.  */
 426 static int
 427 racecheck_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 428 {
 429   unsigned int id = (unsigned int) (uintptr_t) arg;
 430   tss_t *keys = racecheck_keys[id]; /* an array of KEYS_COUNT keys */
 431   int repeat;
 432   int i;
 433 
 434   dbgprintf ("Worker %p started\n", thrd_current_pointer ());
 435 
 436   for (i = 0; i < KEYS_COUNT; i++)
 437     {
 438       ASSERT (tss_create (&keys[i], destructor_table[i]) == thrd_success);
 439       ASSERT (tss_set (keys[i], (void *) (uintptr_t) (10 * id + i + 1))
 440               == thrd_success);
 441     }
 442 
 443   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
 444     {
 445       i = ((unsigned int) rand () >> 3) % KEYS_COUNT;
 446       dbgprintf ("Worker %p reallocating key %d\n", thrd_current_pointer (), i);
 447       tss_delete (keys[i]);
 448       ASSERT (tss_create (&keys[i], destructor_table[i]) == thrd_success);
 449       ASSERT (tss_set (keys[i], (void *) (uintptr_t) (10 * id + i + 1))
 450               == thrd_success);
 451     }
 452 
 453   dbgprintf ("Worker %p dying.\n", thrd_current_pointer ());
 454   return 0;
 455 }
 456 
 457 static void
 458 test_tss_racecheck (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 459 {
 460   thrd_t threads[THREAD_COUNT];
 461   unsigned int id;
 462   int i;
 463   uintptr_t expected_sum;
 464 
 465   sum = 0;
 466 
 467   /* Spawn the threads.  */
 468   for (id = 0; id < THREAD_COUNT; id++)
 469     ASSERT (thrd_create (&threads[id], racecheck_thread, (void *) (uintptr_t) id)
 470             == thrd_success);
 471 
 472   /* Wait for the threads to terminate.  */
 473   for (id = 0; id < THREAD_COUNT; id++)
 474     ASSERT (thrd_join (threads[id], NULL) == thrd_success);
 475 
 476   /* Clean up the keys.  */
 477   for (id = 0; id < THREAD_COUNT; id++)
 478     for (i = 0; i < KEYS_COUNT; i++)
 479       tss_delete (racecheck_keys[id][i]);
 480 
 481   /* Check that the destructor was invoked for each key.  */
 482   expected_sum = 10 * KEYS_COUNT * (THREAD_COUNT * (THREAD_COUNT - 1) / 2)
 483                  + THREAD_COUNT * (KEYS_COUNT * (KEYS_COUNT - 1) / 2)
 484                  + THREAD_COUNT * KEYS_COUNT;
 485   if (sum != expected_sum)
 486     abort ();
 487 }
 488 
 489 #undef REPEAT_COUNT
 490 #undef KEYS_COUNT
 491 #undef THREAD_COUNT
 492 
 493 
 494 /* -------------------------------------------------------------------------- */
 495 
 496 int
 497 main ()
     /* [previous][next][first][last][top][bottom][index][help] */
 498 {
 499 #if HAVE_DECL_ALARM
 500   /* Declare failure if test takes too long, by using default abort
 501      caused by SIGALRM.  */
 502   int alarm_value = 600;
 503   signal (SIGALRM, SIG_DFL);
 504   alarm (alarm_value);
 505 #endif
 506 
 507   ASSERT (mtx_init (&sumlock, mtx_plain) == thrd_success);
 508 
 509   printf ("Starting test_tss ..."); fflush (stdout);
 510   test_tss ();
 511   printf (" OK\n"); fflush (stdout);
 512 
 513   printf ("Starting test_tss_dtorcheck1 ..."); fflush (stdout);
 514   test_tss_dtorcheck1 ();
 515   printf (" OK\n"); fflush (stdout);
 516 
 517   printf ("Starting test_tss_dtorcheck2 ..."); fflush (stdout);
 518   test_tss_dtorcheck2 ();
 519   printf (" OK\n"); fflush (stdout);
 520 
 521   printf ("Starting test_tss_racecheck ..."); fflush (stdout);
 522   test_tss_racecheck ();
 523   printf (" OK\n"); fflush (stdout);
 524 
 525   return 0;
 526 }

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