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