autoupdate
[gnulib.git] / tests / test-tls.c
1 /* Test of thread-local storage in multithreaded situations.
2    Copyright (C) 2005 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify it
5    under the terms of the GNU Library General Public License as published
6    by the Free Software Foundation; either version 2, or (at your option)
7    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 GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public
15    License along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
17    USA.  */
18
19 /* Written by Bruno Haible <bruno@clisp.org>, 2005.  */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #if USE_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WIN32_THREADS
26
27 #if USE_POSIX_THREADS
28 # define TEST_POSIX_THREADS 1
29 #endif
30 #if USE_SOLARIS_THREADS
31 # define TEST_SOLARIS_THREADS 1
32 #endif
33 #if USE_PTH_THREADS
34 # define TEST_PTH_THREADS 1
35 #endif
36 #if USE_WIN32_THREADS
37 # define TEST_WIN32_THREADS 1
38 #endif
39
40 /* Whether to help the scheduler through explicit yield().
41    Uncomment this to see if the operating system has a fair scheduler.  */
42 #define EXPLICIT_YIELD 1
43
44 /* Whether to print debugging messages.  */
45 #define ENABLE_DEBUGGING 0
46
47 /* Number of simultaneous threads.  */
48 #define THREAD_COUNT 16
49
50 /* Number of operations performed in each thread.  */
51 #define REPEAT_COUNT 50000
52
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56
57 #include "tls.h"
58
59 #if ENABLE_DEBUGGING
60 # define dbgprintf printf
61 #else
62 # define dbgprintf if (0) printf
63 #endif
64
65 #if TEST_POSIX_THREADS
66 # include <pthread.h>
67 # include <sched.h>
68 typedef pthread_t gl_thread_t;
69 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
70 {
71   pthread_t thread;
72   if (pthread_create (&thread, NULL, func, arg) != 0)
73     abort ();
74   return thread;
75 }
76 static inline void gl_thread_join (gl_thread_t thread)
77 {
78   void *retval;
79   if (pthread_join (thread, &retval) != 0)
80     abort ();
81 }
82 static inline void gl_thread_yield (void)
83 {
84   sched_yield ();
85 }
86 static inline void * gl_thread_self (void)
87 {
88   return (void *) pthread_self ();
89 }
90 #endif
91 #if TEST_PTH_THREADS
92 # include <pth.h>
93 typedef pth_t gl_thread_t;
94 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
95 {
96   pth_t thread = pth_spawn (NULL, func, arg);
97   if (thread == NULL)
98     abort ();
99   return thread;
100 }
101 static inline void gl_thread_join (gl_thread_t thread)
102 {
103   if (!pth_join (thread, NULL))
104     abort ();
105 }
106 static inline void gl_thread_yield (void)
107 {
108   pth_yield (NULL);
109 }
110 static inline void * gl_thread_self (void)
111 {
112   return pth_self ();
113 }
114 #endif
115 #if TEST_SOLARIS_THREADS
116 # include <thread.h>
117 typedef thread_t gl_thread_t;
118 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
119 {
120   thread_t thread;
121   if (thr_create (NULL, 0, func, arg, 0, &thread) != 0)
122     abort ();
123   return thread;
124 }
125 static inline void gl_thread_join (gl_thread_t thread)
126 {
127   void *retval;
128   if (thr_join (thread, NULL, &retval) != 0)
129     abort ();
130 }
131 static inline void gl_thread_yield (void)
132 {
133   thr_yield ();
134 }
135 static inline void * gl_thread_self (void)
136 {
137   return (void *) thr_self ();
138 }
139 #endif
140 #if TEST_WIN32_THREADS
141 # include <windows.h>
142 typedef HANDLE gl_thread_t;
143 /* Use a wrapper function, instead of adding WINAPI through a cast.  */
144 struct wrapper_args { void * (*func) (void *); void *arg; };
145 static DWORD WINAPI wrapper_func (void *varg)
146 {
147   struct wrapper_args *warg = (struct wrapper_args *)varg;
148   void * (*func) (void *) = warg->func;
149   void *arg = warg->arg;
150   free (warg);
151   func (arg);
152   return 0;
153 }
154 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
155 {
156   struct wrapper_args *warg =
157     (struct wrapper_args *) malloc (sizeof (struct wrapper_args));
158   if (warg == NULL)
159     abort ();
160   warg->func = func;
161   warg->arg = arg;
162   {
163     DWORD thread_id;
164     HANDLE thread =
165       CreateThread (NULL, 100000, wrapper_func, warg, 0, &thread_id);
166     if (thread == NULL)
167       abort ();
168     return thread;
169   }
170 }
171 static inline void gl_thread_join (gl_thread_t thread)
172 {
173   if (WaitForSingleObject (thread, INFINITE) == WAIT_FAILED)
174     abort ();
175   if (!CloseHandle (thread))
176     abort ();
177 }
178 static inline void gl_thread_yield (void)
179 {
180   Sleep (0);
181 }
182 static inline void * gl_thread_self (void)
183 {
184   return (void *) GetCurrentThreadId ();
185 }
186 #endif
187 #if EXPLICIT_YIELD
188 # define yield() gl_thread_yield ()
189 #else
190 # define yield()
191 #endif
192
193 static inline void
194 perhaps_yield (void)
195 {
196   /* Call yield () only with a certain probability, otherwise with GNU Pth
197      the sequence of thread activations is too predictable.  */
198   if ((((unsigned int) rand () >> 3) % 4) == 0)
199     yield ();
200 }
201
202 #define KEYS_COUNT 4
203
204 static gl_tls_key_t mykeys[KEYS_COUNT];
205
206 static void *
207 worker_thread (void *arg)
208 {
209   unsigned int id = (unsigned int) (unsigned long) arg;
210   int i, j, repeat;
211   unsigned int values[KEYS_COUNT];
212
213   dbgprintf ("Worker %p started\n", gl_thread_self ());
214
215   /* Initialize the per-thread storage.  */
216   for (i = 0; i < KEYS_COUNT; i++)
217     {
218       values[i] = (((unsigned int) rand() >> 3) % 1000000) * THREAD_COUNT + id;
219       /* Hopefully no arithmetic overflow.  */
220       if ((values[i] % THREAD_COUNT) != id)
221         abort ();
222     }
223   perhaps_yield ();
224
225   /* Verify that the initial value is NULL.  */
226   dbgprintf ("Worker %p before initial verify\n", gl_thread_self ());
227   for (i = 0; i < KEYS_COUNT; i++)
228     if (gl_tls_get (mykeys[i]) != NULL)
229       abort ();
230   dbgprintf ("Worker %p after  initial verify\n", gl_thread_self ());
231   perhaps_yield ();
232
233   /* Initialize the per-thread storage.  */
234   dbgprintf ("Worker %p before first tls_set\n", gl_thread_self ());
235   for (i = 0; i < KEYS_COUNT; i++)
236     {
237       unsigned int *ptr = (unsigned int *) malloc (sizeof (unsigned int));
238       *ptr = values[i];
239       gl_tls_set (mykeys[i], ptr);
240     }
241   dbgprintf ("Worker %p after  first tls_set\n", gl_thread_self ());
242   perhaps_yield ();
243
244   /* Shuffle around the pointers.  */
245   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
246     {
247       dbgprintf ("Worker %p doing value swapping\n", gl_thread_self ());
248       i = ((unsigned int) rand() >> 3) % KEYS_COUNT;
249       j = ((unsigned int) rand() >> 3) % KEYS_COUNT;
250       if (i != j)
251         {
252           void *vi = gl_tls_get (mykeys[i]);
253           void *vj = gl_tls_get (mykeys[j]);
254
255           gl_tls_set (mykeys[i], vj);
256           gl_tls_set (mykeys[j], vi);
257         }
258       perhaps_yield ();
259     }
260
261   /* Verify that all the values are from this thread.  */
262   dbgprintf ("Worker %p before final verify\n", gl_thread_self ());
263   for (i = 0; i < KEYS_COUNT; i++)
264     if ((*(unsigned int *) gl_tls_get (mykeys[i]) % THREAD_COUNT) != id)
265       abort ();
266   dbgprintf ("Worker %p after  final verify\n", gl_thread_self ());
267   perhaps_yield ();
268
269   dbgprintf ("Worker %p dying.\n", gl_thread_self ());
270   return NULL;
271 }
272
273 void
274 test_tls (void)
275 {
276   int pass, i;
277
278   for (pass = 0; pass < 2; pass++)
279     {
280       gl_thread_t threads[THREAD_COUNT];
281
282       if (pass == 0)
283         for (i = 0; i < KEYS_COUNT; i++)
284           gl_tls_key_init (mykeys[i], free);
285       else
286         for (i = KEYS_COUNT - 1; i >= 0; i--)
287           gl_tls_key_init (mykeys[i], free);
288
289       /* Spawn the threads.  */
290       for (i = 0; i < THREAD_COUNT; i++)
291         threads[i] = gl_thread_create (worker_thread, NULL);
292
293       /* Wait for the threads to terminate.  */
294       for (i = 0; i < THREAD_COUNT; i++)
295         gl_thread_join (threads[i]);
296
297       for (i = 0; i < KEYS_COUNT; i++)
298         gl_tls_key_destroy (mykeys[i]);
299     }
300 }
301
302 int
303 main ()
304 {
305 #if TEST_PTH_THREADS
306   if (!pth_init ())
307     abort ();
308 #endif
309
310   printf ("Starting test_tls ..."); fflush (stdout);
311   test_tls ();
312   printf (" OK\n"); fflush (stdout);
313
314   return 0;
315 }
316
317 #else
318
319 /* No multithreading available.  */
320
321 int
322 main ()
323 {
324   return 77;
325 }
326
327 #endif