(root)/
gettext-0.22.4/
gettext-tools/
gnulib-tests/
test-tls.c
       1  /* Test of thread-local storage in multithreaded situations.
       2     Copyright (C) 2005, 2008-2023 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)
      69  {
      70    /* This helps making the sequence of thread activations less predictable.  */
      71    if ((((unsigned long) random () >> 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)
      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 long) random () >> 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 long) random () >> 3) % KEYS_COUNT;
     131        j = ((unsigned long) random () >> 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)
     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)
     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)
     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)
     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)
     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)
     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)
     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)
     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)
     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)
     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)
     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)
     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)
     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)
     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)
     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)
     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 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)
     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 long) random () >> 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)
     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 ()
     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 ()
     533  {
     534    fputs ("Skipping test: multithreading not enabled\n", stderr);
     535    return 77;
     536  }
     537  
     538  #endif