root/maint/gnulib/tests/test-pthread-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
  22. main

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

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