Support compiling without -loldnames on native Windows.
[gnulib.git] / tests / test-thread_local.c
blob67f2d451abe0b795d82782ef58e3aee4853f2f11
1 /* Test of thread-local storage in multithreaded situations.
2 Copyright (C) 2005, 2008-2020 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005. */
19 #include <config.h>
21 #include <threads.h>
23 #ifdef thread_local
25 /* Whether to help the scheduler through explicit thrd_yield().
26 Uncomment this to see if the operating system has a fair scheduler. */
27 #define EXPLICIT_YIELD 1
29 /* Whether to print debugging messages. */
30 #define ENABLE_DEBUGGING 0
32 /* Number of simultaneous threads. */
33 #define THREAD_COUNT 16
35 /* Number of operations performed in each thread. */
36 #define REPEAT_COUNT 50000
38 #include <stdint.h>
39 #include <stdio.h>
40 #include <stdlib.h>
42 #if HAVE_DECL_ALARM
43 # include <signal.h>
44 # include <unistd.h>
45 #endif
47 #include "macros.h"
49 #if ENABLE_DEBUGGING
50 # define dbgprintf printf
51 #else
52 # define dbgprintf if (0) printf
53 #endif
55 #if EXPLICIT_YIELD
56 # define yield() thrd_yield ()
57 #else
58 # define yield()
59 #endif
61 /* Returns a reference to the current thread as a pointer, for debugging. */
62 #if defined __MVS__
63 /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
64 The first three bytes of this field appear to uniquely identify a
65 pthread_t, though not necessarily representing a pointer. */
66 # define thrd_current_pointer() (*((void **) thrd_current ().__))
67 #elif defined __sun
68 /* On Solaris, thrd_t is merely an 'unsigned int'. */
69 # define thrd_current_pointer() ((void *) (uintptr_t) thrd_current ())
70 #else
71 # define thrd_current_pointer() ((void *) thrd_current ())
72 #endif
74 static void
75 perhaps_yield (void)
77 /* Call yield () only with a certain probability, otherwise the
78 sequence of thread activations may be too predictable. */
79 if ((((unsigned int) rand () >> 3) % 4) == 0)
80 yield ();
84 /* ----------------------- Test thread-local storage ----------------------- */
86 #define KEYS_COUNT 4
87 static unsigned int thread_local value0;
88 static unsigned int thread_local value1;
89 static unsigned int thread_local value2;
90 static unsigned int thread_local value3;
92 static int
93 worker_thread (void *arg)
95 unsigned int id = (unsigned int) (uintptr_t) arg;
96 int i, j, repeat;
97 unsigned int *values[KEYS_COUNT] = { &value0, &value1, &value2, &value3 };
99 dbgprintf ("Worker %p started\n", thrd_current_pointer ());
101 /* Initialize the per-thread storage. */
102 dbgprintf ("Worker %p before first assignment\n", thrd_current_pointer ());
103 for (i = 0; i < KEYS_COUNT; i++)
105 *values[i] = (((unsigned int) rand () >> 3) % 1000000) * THREAD_COUNT + id;
106 /* Hopefully no arithmetic overflow. */
107 if ((*values[i] % THREAD_COUNT) != id)
108 abort ();
110 dbgprintf ("Worker %p after first assignment\n", thrd_current_pointer ());
111 perhaps_yield ();
113 /* Shuffle around the pointers. */
114 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
116 dbgprintf ("Worker %p doing value swapping\n", thrd_current_pointer ());
117 i = ((unsigned int) rand () >> 3) % KEYS_COUNT;
118 j = ((unsigned int) rand () >> 3) % KEYS_COUNT;
119 if (i != j)
121 unsigned int vi = *values[i];
122 unsigned int vj = *values[j];
124 *values[i] = vj;
125 *values[j] = vi;
127 perhaps_yield ();
130 /* Verify that all the values are from this thread. */
131 dbgprintf ("Worker %p before final verify\n", thrd_current_pointer ());
132 for (i = 0; i < KEYS_COUNT; i++)
133 if ((*values[i] % THREAD_COUNT) != id)
134 abort ();
135 dbgprintf ("Worker %p after final verify\n", thrd_current_pointer ());
136 perhaps_yield ();
138 dbgprintf ("Worker %p dying.\n", thrd_current_pointer ());
139 return 0;
142 static void
143 test_thread_local (void)
145 int pass, i;
147 for (pass = 0; pass < 2; pass++)
149 thrd_t threads[THREAD_COUNT];
151 /* Spawn the threads. */
152 for (i = 0; i < THREAD_COUNT; i++)
153 ASSERT (thrd_create (&threads[i], worker_thread, (void *) (uintptr_t) i)
154 == thrd_success);
156 /* Wait for the threads to terminate. */
157 for (i = 0; i < THREAD_COUNT; i++)
158 ASSERT (thrd_join (threads[i], NULL) == thrd_success);
163 /* -------------------------------------------------------------------------- */
166 main ()
168 #if HAVE_DECL_ALARM
169 /* Declare failure if test takes too long, by using default abort
170 caused by SIGALRM. */
171 int alarm_value = 600;
172 signal (SIGALRM, SIG_DFL);
173 alarm (alarm_value);
174 #endif
176 printf ("Starting test_thread_local ..."); fflush (stdout);
177 test_thread_local ();
178 printf (" OK\n"); fflush (stdout);
180 return 0;
183 #else
185 /* No thread-local storage support available in the compiler and linker. */
187 #include <stdio.h>
190 main ()
192 fputs ("Skipping test: thread_local not supported\n", stderr);
193 return 77;
196 #endif