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

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

DEFINITIONS

This source file includes following definitions.
  1. perhaps_yield
  2. worker_thread
  3. test_tls
  4. gl_lock_define_initialized
  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_tls_dtorcheck1
  17. dtorcheck2_thread
  18. test_tls_dtorcheck2
  19. racecheck_thread
  20. test_tls_racecheck
  21. main
  22. 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 #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
  22 
  23 #if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_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_WINDOWS_THREADS
  30 # define TEST_WINDOWS_THREADS 1
  31 #endif
  32 
  33 /* Whether to help the scheduler through explicit 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 #include <stdint.h>
  41 #include <stdio.h>
  42 #include <stdlib.h>
  43 #include <string.h>
  44 
  45 #include "glthread/tls.h"
  46 #include "glthread/thread.h"
  47 #include "glthread/lock.h"
  48 #include "glthread/yield.h"
  49 
  50 #if HAVE_DECL_ALARM
  51 # include <signal.h>
  52 # include <unistd.h>
  53 #endif
  54 
  55 #if ENABLE_DEBUGGING
  56 # define dbgprintf printf
  57 #else
  58 # define dbgprintf if (0) printf
  59 #endif
  60 
  61 #if EXPLICIT_YIELD
  62 # define yield() gl_thread_yield ()
  63 #else
  64 # define yield()
  65 #endif
  66 
  67 static void
  68 perhaps_yield (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  69 {
  70   /* This helps making the sequence of thread activations less 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 gl_tls_key_t mykeys[KEYS_COUNT];
  87 
  88 static void *
  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", gl_thread_self_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", gl_thread_self_pointer ());
 109   for (i = 0; i < KEYS_COUNT; i++)
 110     if (gl_tls_get (mykeys[i]) != NULL)
 111       abort ();
 112   dbgprintf ("Worker %p after  initial verify\n", gl_thread_self_pointer ());
 113   perhaps_yield ();
 114 
 115   /* Initialize the per-thread storage.  */
 116   dbgprintf ("Worker %p before first tls_set\n", gl_thread_self_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       gl_tls_set (mykeys[i], ptr);
 122     }
 123   dbgprintf ("Worker %p after  first tls_set\n", gl_thread_self_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", gl_thread_self_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 = gl_tls_get (mykeys[i]);
 135           void *vj = gl_tls_get (mykeys[j]);
 136 
 137           gl_tls_set (mykeys[i], vj);
 138           gl_tls_set (mykeys[j], vi);
 139         }
 140       perhaps_yield ();
 141     }
 142 
 143   /* Verify that all the values are from this thread.  */
 144   dbgprintf ("Worker %p before final verify\n", gl_thread_self_pointer ());
 145   for (i = 0; i < KEYS_COUNT; i++)
 146     if ((*(unsigned int *) gl_tls_get (mykeys[i]) % THREAD_COUNT) != id)
 147       abort ();
 148   dbgprintf ("Worker %p after  final verify\n", gl_thread_self_pointer ());
 149   perhaps_yield ();
 150 
 151   dbgprintf ("Worker %p dying.\n", gl_thread_self_pointer ());
 152   return NULL;
 153 }
 154 
 155 static void
 156 test_tls (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 157 {
 158   int pass, i;
 159 
 160   for (pass = 0; pass < 2; pass++)
 161     {
 162       gl_thread_t threads[THREAD_COUNT];
 163 
 164       if (pass == 0)
 165         for (i = 0; i < KEYS_COUNT; i++)
 166           gl_tls_key_init (mykeys[i], free);
 167       else
 168         for (i = KEYS_COUNT - 1; i >= 0; i--)
 169           gl_tls_key_init (mykeys[i], free);
 170 
 171       /* Spawn the threads.  */
 172       for (i = 0; i < THREAD_COUNT; i++)
 173         threads[i] = gl_thread_create (worker_thread, (void *) (uintptr_t) i);
 174 
 175       /* Wait for the threads to terminate.  */
 176       for (i = 0; i < THREAD_COUNT; i++)
 177         gl_thread_join (threads[i], NULL);
 178 
 179       for (i = 0; i < KEYS_COUNT; i++)
 180         gl_tls_key_destroy (mykeys[i]);
 181     }
 182 }
 183 
 184 #undef KEYS_COUNT
 185 #undef REPEAT_COUNT
 186 #undef THREAD_COUNT
 187 
 188 
 189 /* --------------- Test thread-local storage with destructors --------------- */
 190 
 191 /* Number of simultaneous threads.  */
 192 #define THREAD_COUNT 10
 193 
 194 /* Number of keys to allocate in each thread.  */
 195 #define KEYS_COUNT 10
 196 
 197 gl_lock_define_initialized(static, sumlock)
     /* [previous][next][first][last][top][bottom][index][help] */
 198 static uintptr_t sum;
 199 
 200 static void
 201 inc_sum (uintptr_t value)
 202 {
 203   gl_lock_lock (sumlock);
 204   sum += value;
 205   gl_lock_unlock (sumlock);
 206 }
 207 
 208 static void
 209 destructor0 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 210 {
 211   if ((((uintptr_t) value - 1) % 10) != 0)
 212     abort ();
 213   inc_sum ((uintptr_t) value);
 214 }
 215 
 216 static void
 217 destructor1 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 218 {
 219   if ((((uintptr_t) value - 1) % 10) != 1)
 220     abort ();
 221   inc_sum ((uintptr_t) value);
 222 }
 223 
 224 static void
 225 destructor2 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 226 {
 227   if ((((uintptr_t) value - 1) % 10) != 2)
 228     abort ();
 229   inc_sum ((uintptr_t) value);
 230 }
 231 
 232 static void
 233 destructor3 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 234 {
 235   if ((((uintptr_t) value - 1) % 10) != 3)
 236     abort ();
 237   inc_sum ((uintptr_t) value);
 238 }
 239 
 240 static void
 241 destructor4 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 242 {
 243   if ((((uintptr_t) value - 1) % 10) != 4)
 244     abort ();
 245   inc_sum ((uintptr_t) value);
 246 }
 247 
 248 static void
 249 destructor5 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 250 {
 251   if ((((uintptr_t) value - 1) % 10) != 5)
 252     abort ();
 253   inc_sum ((uintptr_t) value);
 254 }
 255 
 256 static void
 257 destructor6 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 258 {
 259   if ((((uintptr_t) value - 1) % 10) != 6)
 260     abort ();
 261   inc_sum ((uintptr_t) value);
 262 }
 263 
 264 static void
 265 destructor7 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 266 {
 267   if ((((uintptr_t) value - 1) % 10) != 7)
 268     abort ();
 269   inc_sum ((uintptr_t) value);
 270 }
 271 
 272 static void
 273 destructor8 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 274 {
 275   if ((((uintptr_t) value - 1) % 10) != 8)
 276     abort ();
 277   inc_sum ((uintptr_t) value);
 278 }
 279 
 280 static void
 281 destructor9 (void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 282 {
 283   if ((((uintptr_t) value - 1) % 10) != 9)
 284     abort ();
 285   inc_sum ((uintptr_t) value);
 286 }
 287 
 288 static void (*destructor_table[10]) (void *) =
 289   {
 290     destructor0,
 291     destructor1,
 292     destructor2,
 293     destructor3,
 294     destructor4,
 295     destructor5,
 296     destructor6,
 297     destructor7,
 298     destructor8,
 299     destructor9
 300   };
 301 
 302 static gl_tls_key_t dtorcheck_keys[THREAD_COUNT][KEYS_COUNT];
 303 
 304 /* Worker thread that uses destructors that verify that the destructor belongs
 305    to the right thread.  */
 306 static void *
 307 dtorcheck1_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 308 {
 309   unsigned int id = (unsigned int) (uintptr_t) arg;
 310   gl_tls_key_t *keys = dtorcheck_keys[id]; /* an array of KEYS_COUNT keys */
 311   int i;
 312 
 313   for (i = 0; i < KEYS_COUNT; i++)
 314     gl_tls_key_init (keys[i], destructor_table[i]);
 315 
 316   for (i = 0; i < KEYS_COUNT; i++)
 317     gl_tls_set (keys[i], (void *) (uintptr_t) (10 * id + i + 1));
 318 
 319   return NULL;
 320 }
 321 
 322 static void
 323 test_tls_dtorcheck1 (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 324 {
 325   gl_thread_t threads[THREAD_COUNT];
 326   unsigned int id;
 327   int i;
 328   uintptr_t expected_sum;
 329 
 330   sum = 0;
 331 
 332   /* Spawn the threads.  */
 333   for (id = 0; id < THREAD_COUNT; id++)
 334     threads[id] = gl_thread_create (dtorcheck1_thread, (void *) (uintptr_t) id);
 335 
 336   /* Wait for the threads to terminate.  */
 337   for (id = 0; id < THREAD_COUNT; id++)
 338     gl_thread_join (threads[id], NULL);
 339 
 340   /* Clean up the keys.  */
 341   for (id = 0; id < THREAD_COUNT; id++)
 342     for (i = 0; i < KEYS_COUNT; i++)
 343       gl_tls_key_destroy (dtorcheck_keys[id][i]);
 344 
 345   /* Check that the destructor was invoked for each key.  */
 346   expected_sum = 10 * KEYS_COUNT * (THREAD_COUNT * (THREAD_COUNT - 1) / 2)
 347                  + THREAD_COUNT * (KEYS_COUNT * (KEYS_COUNT - 1) / 2)
 348                  + THREAD_COUNT * KEYS_COUNT;
 349   if (sum != expected_sum)
 350     abort ();
 351 }
 352 
 353 /* Worker thread that uses destructors that verify that the destructor belongs
 354    to the right key allocated within the thread.  */
 355 static void *
 356 dtorcheck2_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 357 {
 358   unsigned int id = (unsigned int) (uintptr_t) arg;
 359   gl_tls_key_t *keys = dtorcheck_keys[id]; /* an array of KEYS_COUNT keys */
 360   int i;
 361 
 362   for (i = 0; i < KEYS_COUNT; i++)
 363     gl_tls_key_init (keys[i], destructor_table[id]);
 364 
 365   for (i = 0; i < KEYS_COUNT; i++)
 366     gl_tls_set (keys[i], (void *) (uintptr_t) (10 * i + id + 1));
 367 
 368   return NULL;
 369 }
 370 
 371 static void
 372 test_tls_dtorcheck2 (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 373 {
 374   gl_thread_t threads[THREAD_COUNT];
 375   unsigned int id;
 376   int i;
 377   uintptr_t expected_sum;
 378 
 379   sum = 0;
 380 
 381   /* Spawn the threads.  */
 382   for (id = 0; id < THREAD_COUNT; id++)
 383     threads[id] = gl_thread_create (dtorcheck2_thread, (void *) (uintptr_t) id);
 384 
 385   /* Wait for the threads to terminate.  */
 386   for (id = 0; id < THREAD_COUNT; id++)
 387     gl_thread_join (threads[id], NULL);
 388 
 389   /* Clean up the keys.  */
 390   for (id = 0; id < THREAD_COUNT; id++)
 391     for (i = 0; i < KEYS_COUNT; i++)
 392       gl_tls_key_destroy (dtorcheck_keys[id][i]);
 393 
 394   /* Check that the destructor was invoked for each key.  */
 395   expected_sum = 10 * THREAD_COUNT * (KEYS_COUNT * (KEYS_COUNT - 1) / 2)
 396                  + KEYS_COUNT * (THREAD_COUNT * (THREAD_COUNT - 1) / 2)
 397                  + THREAD_COUNT * KEYS_COUNT;
 398   if (sum != expected_sum)
 399     abort ();
 400 }
 401 
 402 #undef KEYS_COUNT
 403 #undef THREAD_COUNT
 404 
 405 
 406 /* --- Test thread-local storage with with races between init and destroy --- */
 407 
 408 /* Number of simultaneous threads.  */
 409 #define THREAD_COUNT 10
 410 
 411 /* Number of keys to allocate in each thread.  */
 412 #define KEYS_COUNT 10
 413 
 414 /* Number of times to destroy and reallocate a key in each thread.  */
 415 #define REPEAT_COUNT 100000
 416 
 417 static gl_tls_key_t racecheck_keys[THREAD_COUNT][KEYS_COUNT];
 418 
 419 /* Worker thread that does many destructions and reallocations of keys, and also
 420    uses destructors that verify that the destructor belongs to the right key.  */
 421 static void *
 422 racecheck_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 423 {
 424   unsigned int id = (unsigned int) (uintptr_t) arg;
 425   gl_tls_key_t *keys = racecheck_keys[id]; /* an array of KEYS_COUNT keys */
 426   int repeat;
 427   int i;
 428 
 429   dbgprintf ("Worker %p started\n", gl_thread_self_pointer ());
 430 
 431   for (i = 0; i < KEYS_COUNT; i++)
 432     {
 433       gl_tls_key_init (keys[i], destructor_table[i]);
 434       gl_tls_set (keys[i], (void *) (uintptr_t) (10 * id + i + 1));
 435     }
 436 
 437   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
 438     {
 439       i = ((unsigned int) rand () >> 3) % KEYS_COUNT;
 440       dbgprintf ("Worker %p reallocating key %d\n", gl_thread_self_pointer (), i);
 441       gl_tls_key_destroy (keys[i]);
 442       gl_tls_key_init (keys[i], destructor_table[i]);
 443       gl_tls_set (keys[i], (void *) (uintptr_t) (10 * id + i + 1));
 444     }
 445 
 446   dbgprintf ("Worker %p dying.\n", gl_thread_self_pointer ());
 447   return NULL;
 448 }
 449 
 450 static void
 451 test_tls_racecheck (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 452 {
 453   gl_thread_t threads[THREAD_COUNT];
 454   unsigned int id;
 455   int i;
 456   uintptr_t expected_sum;
 457 
 458   sum = 0;
 459 
 460   /* Spawn the threads.  */
 461   for (id = 0; id < THREAD_COUNT; id++)
 462     threads[id] = gl_thread_create (racecheck_thread, (void *) (uintptr_t) id);
 463 
 464   /* Wait for the threads to terminate.  */
 465   for (id = 0; id < THREAD_COUNT; id++)
 466     gl_thread_join (threads[id], NULL);
 467 
 468   /* Clean up the keys.  */
 469   for (id = 0; id < THREAD_COUNT; id++)
 470     for (i = 0; i < KEYS_COUNT; i++)
 471       gl_tls_key_destroy (racecheck_keys[id][i]);
 472 
 473   /* Check that the destructor was invoked for each key.  */
 474   expected_sum = 10 * KEYS_COUNT * (THREAD_COUNT * (THREAD_COUNT - 1) / 2)
 475                  + THREAD_COUNT * (KEYS_COUNT * (KEYS_COUNT - 1) / 2)
 476                  + THREAD_COUNT * KEYS_COUNT;
 477   if (sum != expected_sum)
 478     abort ();
 479 }
 480 
 481 #undef REPEAT_COUNT
 482 #undef KEYS_COUNT
 483 #undef THREAD_COUNT
 484 
 485 
 486 /* -------------------------------------------------------------------------- */
 487 
 488 int
 489 main ()
     /* [previous][next][first][last][top][bottom][index][help] */
 490 {
 491 #if HAVE_DECL_ALARM
 492   /* Declare failure if test takes too long, by using default abort
 493      caused by SIGALRM.  */
 494   int alarm_value = 600;
 495   signal (SIGALRM, SIG_DFL);
 496   alarm (alarm_value);
 497 #endif
 498 
 499   printf ("Starting test_tls ..."); fflush (stdout);
 500   test_tls ();
 501   printf (" OK\n"); fflush (stdout);
 502 
 503   printf ("Starting test_tls_dtorcheck1 ..."); fflush (stdout);
 504   test_tls_dtorcheck1 ();
 505   printf (" OK\n"); fflush (stdout);
 506 
 507   printf ("Starting test_tls_dtorcheck2 ..."); fflush (stdout);
 508   test_tls_dtorcheck2 ();
 509   printf (" OK\n"); fflush (stdout);
 510 
 511   /* This test hangs with the mingw-w64 winpthreads.  */
 512 #if (defined _WIN32 && ! defined __CYGWIN__) && TEST_POSIX_THREADS
 513   fputs ("Skipping test: it is known to hang with the mingw-w64 winpthreads.\n",
 514          stderr);
 515   exit (77);
 516 #else
 517   printf ("Starting test_tls_racecheck ..."); fflush (stdout);
 518   test_tls_racecheck ();
 519   printf (" OK\n"); fflush (stdout);
 520 #endif
 521 
 522   return 0;
 523 }
 524 
 525 #else
 526 
 527 /* No multithreading available.  */
 528 
 529 #include <stdio.h>
 530 
 531 int
 532 main ()
     /* [previous][next][first][last][top][bottom][index][help] */
 533 {
 534   fputs ("Skipping test: multithreading not enabled\n", stderr);
 535   return 77;
 536 }
 537 
 538 #endif

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