test-lock, test-tls: mention why a test is skipped
[gnulib.git] / tests / test-tls.c
1 /* Test of thread-local storage in multithreaded situations.
2    Copyright (C) 2005, 2008 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 <http://www.gnu.org/licenses/>.  */
16
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005.  */
18
19 #include <config.h>
20
21 #if USE_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WIN32_THREADS
22
23 #if USE_POSIX_THREADS
24 # define TEST_POSIX_THREADS 1
25 #endif
26 #if USE_SOLARIS_THREADS
27 # define TEST_SOLARIS_THREADS 1
28 #endif
29 #if USE_PTH_THREADS
30 # define TEST_PTH_THREADS 1
31 #endif
32 #if USE_WIN32_THREADS
33 # define TEST_WIN32_THREADS 1
34 #endif
35
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
39
40 /* Whether to print debugging messages.  */
41 #define ENABLE_DEBUGGING 0
42
43 /* Number of simultaneous threads.  */
44 #define THREAD_COUNT 16
45
46 /* Number of operations performed in each thread.  */
47 #define REPEAT_COUNT 50000
48
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52
53 #include "glthread/tls.h"
54
55 #if ENABLE_DEBUGGING
56 # define dbgprintf printf
57 #else
58 # define dbgprintf if (0) printf
59 #endif
60
61 #if TEST_POSIX_THREADS
62 # include <pthread.h>
63 # include <sched.h>
64 typedef pthread_t gl_thread_t;
65 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
66 {
67   pthread_t thread;
68   if (pthread_create (&thread, NULL, func, arg) != 0)
69     abort ();
70   return thread;
71 }
72 static inline void gl_thread_join (gl_thread_t thread)
73 {
74   void *retval;
75   if (pthread_join (thread, &retval) != 0)
76     abort ();
77 }
78 static inline void gl_thread_yield (void)
79 {
80   sched_yield ();
81 }
82 static inline void * gl_thread_self (void)
83 {
84   return (void *) pthread_self ();
85 }
86 #endif
87 #if TEST_PTH_THREADS
88 # include <pth.h>
89 typedef pth_t gl_thread_t;
90 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
91 {
92   pth_t thread = pth_spawn (NULL, func, arg);
93   if (thread == NULL)
94     abort ();
95   return thread;
96 }
97 static inline void gl_thread_join (gl_thread_t thread)
98 {
99   if (!pth_join (thread, NULL))
100     abort ();
101 }
102 static inline void gl_thread_yield (void)
103 {
104   pth_yield (NULL);
105 }
106 static inline void * gl_thread_self (void)
107 {
108   return pth_self ();
109 }
110 #endif
111 #if TEST_SOLARIS_THREADS
112 # include <thread.h>
113 typedef thread_t gl_thread_t;
114 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
115 {
116   thread_t thread;
117   if (thr_create (NULL, 0, func, arg, 0, &thread) != 0)
118     abort ();
119   return thread;
120 }
121 static inline void gl_thread_join (gl_thread_t thread)
122 {
123   void *retval;
124   if (thr_join (thread, NULL, &retval) != 0)
125     abort ();
126 }
127 static inline void gl_thread_yield (void)
128 {
129   thr_yield ();
130 }
131 static inline void * gl_thread_self (void)
132 {
133   return (void *) thr_self ();
134 }
135 #endif
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)
142 {
143   struct wrapper_args *warg = (struct wrapper_args *)varg;
144   void * (*func) (void *) = warg->func;
145   void *arg = warg->arg;
146   free (warg);
147   func (arg);
148   return 0;
149 }
150 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
151 {
152   struct wrapper_args *warg =
153     (struct wrapper_args *) malloc (sizeof (struct wrapper_args));
154   if (warg == NULL)
155     abort ();
156   warg->func = func;
157   warg->arg = arg;
158   {
159     DWORD thread_id;
160     HANDLE thread =
161       CreateThread (NULL, 100000, wrapper_func, warg, 0, &thread_id);
162     if (thread == NULL)
163       abort ();
164     return thread;
165   }
166 }
167 static inline void gl_thread_join (gl_thread_t thread)
168 {
169   if (WaitForSingleObject (thread, INFINITE) == WAIT_FAILED)
170     abort ();
171   if (!CloseHandle (thread))
172     abort ();
173 }
174 static inline void gl_thread_yield (void)
175 {
176   Sleep (0);
177 }
178 static inline void * gl_thread_self (void)
179 {
180   return (void *) GetCurrentThreadId ();
181 }
182 #endif
183 #if EXPLICIT_YIELD
184 # define yield() gl_thread_yield ()
185 #else
186 # define yield()
187 #endif
188
189 static inline void
190 perhaps_yield (void)
191 {
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)
195     yield ();
196 }
197
198 #define KEYS_COUNT 4
199
200 static gl_tls_key_t mykeys[KEYS_COUNT];
201
202 static void *
203 worker_thread (void *arg)
204 {
205   unsigned int id = (unsigned int) (unsigned long) arg;
206   int i, j, repeat;
207   unsigned int values[KEYS_COUNT];
208
209   dbgprintf ("Worker %p started\n", gl_thread_self ());
210
211   /* Initialize the per-thread storage.  */
212   for (i = 0; i < KEYS_COUNT; i++)
213     {
214       values[i] = (((unsigned int) rand() >> 3) % 1000000) * THREAD_COUNT + id;
215       /* Hopefully no arithmetic overflow.  */
216       if ((values[i] % THREAD_COUNT) != id)
217         abort ();
218     }
219   perhaps_yield ();
220
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)
225       abort ();
226   dbgprintf ("Worker %p after  initial verify\n", gl_thread_self ());
227   perhaps_yield ();
228
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++)
232     {
233       unsigned int *ptr = (unsigned int *) malloc (sizeof (unsigned int));
234       *ptr = values[i];
235       gl_tls_set (mykeys[i], ptr);
236     }
237   dbgprintf ("Worker %p after  first tls_set\n", gl_thread_self ());
238   perhaps_yield ();
239
240   /* Shuffle around the pointers.  */
241   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
242     {
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;
246       if (i != j)
247         {
248           void *vi = gl_tls_get (mykeys[i]);
249           void *vj = gl_tls_get (mykeys[j]);
250
251           gl_tls_set (mykeys[i], vj);
252           gl_tls_set (mykeys[j], vi);
253         }
254       perhaps_yield ();
255     }
256
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)
261       abort ();
262   dbgprintf ("Worker %p after  final verify\n", gl_thread_self ());
263   perhaps_yield ();
264
265   dbgprintf ("Worker %p dying.\n", gl_thread_self ());
266   return NULL;
267 }
268
269 void
270 test_tls (void)
271 {
272   int pass, i;
273
274   for (pass = 0; pass < 2; pass++)
275     {
276       gl_thread_t threads[THREAD_COUNT];
277
278       if (pass == 0)
279         for (i = 0; i < KEYS_COUNT; i++)
280           gl_tls_key_init (mykeys[i], free);
281       else
282         for (i = KEYS_COUNT - 1; i >= 0; i--)
283           gl_tls_key_init (mykeys[i], free);
284
285       /* Spawn the threads.  */
286       for (i = 0; i < THREAD_COUNT; i++)
287         threads[i] = gl_thread_create (worker_thread, NULL);
288
289       /* Wait for the threads to terminate.  */
290       for (i = 0; i < THREAD_COUNT; i++)
291         gl_thread_join (threads[i]);
292
293       for (i = 0; i < KEYS_COUNT; i++)
294         gl_tls_key_destroy (mykeys[i]);
295     }
296 }
297
298 int
299 main ()
300 {
301 #if TEST_PTH_THREADS
302   if (!pth_init ())
303     abort ();
304 #endif
305
306   printf ("Starting test_tls ..."); fflush (stdout);
307   test_tls ();
308   printf (" OK\n"); fflush (stdout);
309
310   return 0;
311 }
312
313 #else
314
315 /* No multithreading available.  */
316
317 #include <stdio.h>
318
319 int
320 main ()
321 {
322   fputs ("multithreading not enabled\n", stderr);
323   return 77;
324 }
325
326 #endif