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 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 "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> 64typedef pthread_t gl_thread_t; 65static 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} 72static inline void gl_thread_join (gl_thread_t thread) 73{ 74 void *retval; 75 if (pthread_join (thread, &retval) != 0) 76 abort (); 77} 78static inline void gl_thread_yield (void) 79{ 80 sched_yield (); 81} 82static inline void * gl_thread_self (void) 83{ 84 return (void *) pthread_self (); 85} 86#endif 87#if TEST_PTH_THREADS 88# include <pth.h> 89typedef pth_t gl_thread_t; 90static 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} 97static inline void gl_thread_join (gl_thread_t thread) 98{ 99 if (!pth_join (thread, NULL)) 100 abort (); 101} 102static inline void gl_thread_yield (void) 103{ 104 pth_yield (NULL); 105} 106static inline void * gl_thread_self (void) 107{ 108 return pth_self (); 109} 110#endif 111#if TEST_SOLARIS_THREADS 112# include <thread.h> 113typedef thread_t gl_thread_t; 114static 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} 121static inline void gl_thread_join (gl_thread_t thread) 122{ 123 void *retval; 124 if (thr_join (thread, NULL, &retval) != 0) 125 abort (); 126} 127static inline void gl_thread_yield (void) 128{ 129 thr_yield (); 130} 131static inline void * gl_thread_self (void) 132{ 133 return (void *) thr_self (); 134} 135#endif 136#if TEST_WIN32_THREADS 137# include <windows.h> 138typedef HANDLE gl_thread_t; 139/* Use a wrapper function, instead of adding WINAPI through a cast. */ 140struct wrapper_args { void * (*func) (void *); void *arg; }; 141static 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} 150static 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} 167static 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} 174static inline void gl_thread_yield (void) 175{ 176 Sleep (0); 177} 178static 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 189static inline void 190perhaps_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 200static gl_tls_key_t mykeys[KEYS_COUNT]; 201 202static void * 203worker_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 269void 270test_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 298int 299main () 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 317int 318main () 319{ 320 return 77; 321} 322 323#endif 324