Make use of the modules 'thread', 'yield' in the 'tls' test.
[pspp] / 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 #include "glthread/thread.h"
55 #include "glthread/yield.h"
56
57 #if ENABLE_DEBUGGING
58 # define dbgprintf printf
59 #else
60 # define dbgprintf if (0) printf
61 #endif
62
63 #if EXPLICIT_YIELD
64 # define yield() gl_thread_yield ()
65 #else
66 # define yield()
67 #endif
68
69 static inline void
70 perhaps_yield (void)
71 {
72   /* Call yield () only with a certain probability, otherwise with GNU Pth
73      the sequence of thread activations is too predictable.  */
74   if ((((unsigned int) rand () >> 3) % 4) == 0)
75     yield ();
76 }
77
78 #define KEYS_COUNT 4
79
80 static gl_tls_key_t mykeys[KEYS_COUNT];
81
82 static void *
83 worker_thread (void *arg)
84 {
85   unsigned int id = (unsigned int) (unsigned long) arg;
86   int i, j, repeat;
87   unsigned int values[KEYS_COUNT];
88
89   dbgprintf ("Worker %p started\n", gl_thread_self ());
90
91   /* Initialize the per-thread storage.  */
92   for (i = 0; i < KEYS_COUNT; i++)
93     {
94       values[i] = (((unsigned int) rand() >> 3) % 1000000) * THREAD_COUNT + id;
95       /* Hopefully no arithmetic overflow.  */
96       if ((values[i] % THREAD_COUNT) != id)
97         abort ();
98     }
99   perhaps_yield ();
100
101   /* Verify that the initial value is NULL.  */
102   dbgprintf ("Worker %p before initial verify\n", gl_thread_self ());
103   for (i = 0; i < KEYS_COUNT; i++)
104     if (gl_tls_get (mykeys[i]) != NULL)
105       abort ();
106   dbgprintf ("Worker %p after  initial verify\n", gl_thread_self ());
107   perhaps_yield ();
108
109   /* Initialize the per-thread storage.  */
110   dbgprintf ("Worker %p before first tls_set\n", gl_thread_self ());
111   for (i = 0; i < KEYS_COUNT; i++)
112     {
113       unsigned int *ptr = (unsigned int *) malloc (sizeof (unsigned int));
114       *ptr = values[i];
115       gl_tls_set (mykeys[i], ptr);
116     }
117   dbgprintf ("Worker %p after  first tls_set\n", gl_thread_self ());
118   perhaps_yield ();
119
120   /* Shuffle around the pointers.  */
121   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
122     {
123       dbgprintf ("Worker %p doing value swapping\n", gl_thread_self ());
124       i = ((unsigned int) rand() >> 3) % KEYS_COUNT;
125       j = ((unsigned int) rand() >> 3) % KEYS_COUNT;
126       if (i != j)
127         {
128           void *vi = gl_tls_get (mykeys[i]);
129           void *vj = gl_tls_get (mykeys[j]);
130
131           gl_tls_set (mykeys[i], vj);
132           gl_tls_set (mykeys[j], vi);
133         }
134       perhaps_yield ();
135     }
136
137   /* Verify that all the values are from this thread.  */
138   dbgprintf ("Worker %p before final verify\n", gl_thread_self ());
139   for (i = 0; i < KEYS_COUNT; i++)
140     if ((*(unsigned int *) gl_tls_get (mykeys[i]) % THREAD_COUNT) != id)
141       abort ();
142   dbgprintf ("Worker %p after  final verify\n", gl_thread_self ());
143   perhaps_yield ();
144
145   dbgprintf ("Worker %p dying.\n", gl_thread_self ());
146   return NULL;
147 }
148
149 void
150 test_tls (void)
151 {
152   int pass, i;
153
154   for (pass = 0; pass < 2; pass++)
155     {
156       gl_thread_t threads[THREAD_COUNT];
157
158       if (pass == 0)
159         for (i = 0; i < KEYS_COUNT; i++)
160           gl_tls_key_init (mykeys[i], free);
161       else
162         for (i = KEYS_COUNT - 1; i >= 0; i--)
163           gl_tls_key_init (mykeys[i], free);
164
165       /* Spawn the threads.  */
166       for (i = 0; i < THREAD_COUNT; i++)
167         threads[i] = gl_thread_create (worker_thread, NULL);
168
169       /* Wait for the threads to terminate.  */
170       for (i = 0; i < THREAD_COUNT; i++)
171         gl_thread_join (threads[i], NULL);
172
173       for (i = 0; i < KEYS_COUNT; i++)
174         gl_tls_key_destroy (mykeys[i]);
175     }
176 }
177
178 int
179 main ()
180 {
181 #if TEST_PTH_THREADS
182   if (!pth_init ())
183     abort ();
184 #endif
185
186   printf ("Starting test_tls ..."); fflush (stdout);
187   test_tls ();
188   printf (" OK\n"); fflush (stdout);
189
190   return 0;
191 }
192
193 #else
194
195 /* No multithreading available.  */
196
197 #include <stdio.h>
198
199 int
200 main ()
201 {
202   fputs ("Skipping test: multithreading not enabled\n", stderr);
203   return 77;
204 }
205
206 #endif