1 /* Test of thread-local storage in multithreaded situations.
2 Copyright (C) 2005, 2008 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 <http://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005. */
21 #if USE_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WIN32_THREADS
24 # define TEST_POSIX_THREADS 1
26 #if USE_SOLARIS_THREADS
27 # define TEST_SOLARIS_THREADS 1
30 # define TEST_PTH_THREADS 1
33 # define TEST_WIN32_THREADS 1
36 /* Whether to help the scheduler through explicit yield().
37 Uncomment this to see if the operating system has a fair scheduler. */
38 #define EXPLICIT_YIELD 1
40 /* Whether to print debugging messages. */
41 #define ENABLE_DEBUGGING 0
43 /* Number of simultaneous threads. */
44 #define THREAD_COUNT 16
46 /* Number of operations performed in each thread. */
47 #define REPEAT_COUNT 50000
53 #include "glthread/tls.h"
56 # define dbgprintf printf
58 # define dbgprintf if (0) printf
61 #if TEST_POSIX_THREADS
64 typedef pthread_t gl_thread_t;
65 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
68 if (pthread_create (&thread, NULL, func, arg) != 0)
72 static inline void gl_thread_join (gl_thread_t thread)
75 if (pthread_join (thread, &retval) != 0)
78 static inline void gl_thread_yield (void)
82 static inline void * gl_thread_self (void)
84 return (void *) pthread_self ();
89 typedef pth_t gl_thread_t;
90 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
92 pth_t thread = pth_spawn (NULL, func, arg);
97 static inline void gl_thread_join (gl_thread_t thread)
99 if (!pth_join (thread, NULL))
102 static inline void gl_thread_yield (void)
106 static inline void * gl_thread_self (void)
111 #if TEST_SOLARIS_THREADS
113 typedef thread_t gl_thread_t;
114 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
117 if (thr_create (NULL, 0, func, arg, 0, &thread) != 0)
121 static inline void gl_thread_join (gl_thread_t thread)
124 if (thr_join (thread, NULL, &retval) != 0)
127 static inline void gl_thread_yield (void)
131 static inline void * gl_thread_self (void)
133 return (void *) thr_self ();
136 #if TEST_WIN32_THREADS
137 # include <windows.h>
138 typedef HANDLE gl_thread_t;
139 /* Use a wrapper function, instead of adding WINAPI through a cast. */
140 struct wrapper_args { void * (*func) (void *); void *arg; };
141 static DWORD WINAPI wrapper_func (void *varg)
143 struct wrapper_args *warg = (struct wrapper_args *)varg;
144 void * (*func) (void *) = warg->func;
145 void *arg = warg->arg;
150 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
152 struct wrapper_args *warg =
153 (struct wrapper_args *) malloc (sizeof (struct wrapper_args));
161 CreateThread (NULL, 100000, wrapper_func, warg, 0, &thread_id);
167 static inline void gl_thread_join (gl_thread_t thread)
169 if (WaitForSingleObject (thread, INFINITE) == WAIT_FAILED)
171 if (!CloseHandle (thread))
174 static inline void gl_thread_yield (void)
178 static inline void * gl_thread_self (void)
180 return (void *) GetCurrentThreadId ();
184 # define yield() gl_thread_yield ()
192 /* Call yield () only with a certain probability, otherwise with GNU Pth
193 the sequence of thread activations is too predictable. */
194 if ((((unsigned int) rand () >> 3) % 4) == 0)
200 static gl_tls_key_t mykeys[KEYS_COUNT];
203 worker_thread (void *arg)
205 unsigned int id = (unsigned int) (unsigned long) arg;
207 unsigned int values[KEYS_COUNT];
209 dbgprintf ("Worker %p started\n", gl_thread_self ());
211 /* Initialize the per-thread storage. */
212 for (i = 0; i < KEYS_COUNT; i++)
214 values[i] = (((unsigned int) rand() >> 3) % 1000000) * THREAD_COUNT + id;
215 /* Hopefully no arithmetic overflow. */
216 if ((values[i] % THREAD_COUNT) != id)
221 /* Verify that the initial value is NULL. */
222 dbgprintf ("Worker %p before initial verify\n", gl_thread_self ());
223 for (i = 0; i < KEYS_COUNT; i++)
224 if (gl_tls_get (mykeys[i]) != NULL)
226 dbgprintf ("Worker %p after initial verify\n", gl_thread_self ());
229 /* Initialize the per-thread storage. */
230 dbgprintf ("Worker %p before first tls_set\n", gl_thread_self ());
231 for (i = 0; i < KEYS_COUNT; i++)
233 unsigned int *ptr = (unsigned int *) malloc (sizeof (unsigned int));
235 gl_tls_set (mykeys[i], ptr);
237 dbgprintf ("Worker %p after first tls_set\n", gl_thread_self ());
240 /* Shuffle around the pointers. */
241 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
243 dbgprintf ("Worker %p doing value swapping\n", gl_thread_self ());
244 i = ((unsigned int) rand() >> 3) % KEYS_COUNT;
245 j = ((unsigned int) rand() >> 3) % KEYS_COUNT;
248 void *vi = gl_tls_get (mykeys[i]);
249 void *vj = gl_tls_get (mykeys[j]);
251 gl_tls_set (mykeys[i], vj);
252 gl_tls_set (mykeys[j], vi);
257 /* Verify that all the values are from this thread. */
258 dbgprintf ("Worker %p before final verify\n", gl_thread_self ());
259 for (i = 0; i < KEYS_COUNT; i++)
260 if ((*(unsigned int *) gl_tls_get (mykeys[i]) % THREAD_COUNT) != id)
262 dbgprintf ("Worker %p after final verify\n", gl_thread_self ());
265 dbgprintf ("Worker %p dying.\n", gl_thread_self ());
274 for (pass = 0; pass < 2; pass++)
276 gl_thread_t threads[THREAD_COUNT];
279 for (i = 0; i < KEYS_COUNT; i++)
280 gl_tls_key_init (mykeys[i], free);
282 for (i = KEYS_COUNT - 1; i >= 0; i--)
283 gl_tls_key_init (mykeys[i], free);
285 /* Spawn the threads. */
286 for (i = 0; i < THREAD_COUNT; i++)
287 threads[i] = gl_thread_create (worker_thread, NULL);
289 /* Wait for the threads to terminate. */
290 for (i = 0; i < THREAD_COUNT; i++)
291 gl_thread_join (threads[i]);
293 for (i = 0; i < KEYS_COUNT; i++)
294 gl_tls_key_destroy (mykeys[i]);
306 printf ("Starting test_tls ..."); fflush (stdout);
308 printf (" OK\n"); fflush (stdout);
315 /* No multithreading available. */
322 fputs ("Skipping test: multithreading not enabled\n", stderr);