1 /* Test of thread-local storage in multithreaded situations.
2    Copyright (C) 2005, 2008-2020 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
perhaps_yield(void)68 perhaps_yield (void)
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 *
worker_thread(void * arg)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 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
test_tls(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
inc_sum(uintptr_t value)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
destructor0(void * value)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
destructor1(void * value)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
destructor2(void * value)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
destructor3(void * value)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
destructor4(void * value)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
destructor5(void * value)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
destructor6(void * value)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
destructor7(void * value)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
destructor8(void * value)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
destructor9(void * value)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 *
dtorcheck1_thread(void * arg)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
test_tls_dtorcheck1(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 *
dtorcheck2_thread(void * arg)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
test_tls_dtorcheck2(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 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 *
racecheck_thread(void * arg)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 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
test_tls_racecheck(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
main()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
main()532 main ()
533 {
534   fputs ("Skipping test: multithreading not enabled\n", stderr);
535   return 77;
536 }
537 
538 #endif
539