1 1.1 christos /* Test of locking in multithreaded situations. 2 1.1 christos Copyright (C) 2005 Free Software Foundation, Inc. 3 1.1 christos 4 1.1 christos This program is free software; you can redistribute it and/or modify it 5 1.1 christos under the terms of the GNU Library General Public License as published 6 1.1 christos by the Free Software Foundation; either version 2, or (at your option) 7 1.1 christos any later version. 8 1.1 christos 9 1.1 christos This program is distributed in the hope that it will be useful, 10 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of 11 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 1.1 christos Library General Public License for more details. 13 1.1 christos 14 1.1 christos You should have received a copy of the GNU Library General Public 15 1.1 christos License along with this program; if not, write to the Free Software 16 1.1 christos Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 17 1.1 christos USA. */ 18 1.1 christos 19 1.1 christos /* Written by Bruno Haible <bruno (at) clisp.org>, 2005. */ 20 1.1 christos 21 1.1 christos #ifdef HAVE_CONFIG_H 22 1.1 christos # include <config.h> 23 1.1 christos #endif 24 1.1 christos 25 1.1 christos #if USE_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WIN32_THREADS 26 1.1 christos 27 1.1 christos #if USE_POSIX_THREADS 28 1.1 christos # define TEST_POSIX_THREADS 1 29 1.1 christos #endif 30 1.1 christos #if USE_SOLARIS_THREADS 31 1.1 christos # define TEST_SOLARIS_THREADS 1 32 1.1 christos #endif 33 1.1 christos #if USE_PTH_THREADS 34 1.1 christos # define TEST_PTH_THREADS 1 35 1.1 christos #endif 36 1.1 christos #if USE_WIN32_THREADS 37 1.1 christos # define TEST_WIN32_THREADS 1 38 1.1 christos #endif 39 1.1 christos 40 1.1 christos /* Whether to enable locking. 41 1.1 christos Uncomment this to get a test program without locking, to verify that 42 1.1 christos it crashes. */ 43 1.1 christos #define ENABLE_LOCKING 1 44 1.1 christos 45 1.1 christos /* Which tests to perform. 46 1.1 christos Uncomment some of these, to verify that all tests crash if no locking 47 1.1 christos is enabled. */ 48 1.1 christos #define DO_TEST_LOCK 1 49 1.1 christos #define DO_TEST_RWLOCK 1 50 1.1 christos #define DO_TEST_RECURSIVE_LOCK 1 51 1.1 christos #define DO_TEST_ONCE 1 52 1.1 christos 53 1.1 christos /* Whether to help the scheduler through explicit yield(). 54 1.1 christos Uncomment this to see if the operating system has a fair scheduler. */ 55 1.1 christos #define EXPLICIT_YIELD 1 56 1.1 christos 57 1.1 christos /* Whether to print debugging messages. */ 58 1.1 christos #define ENABLE_DEBUGGING 0 59 1.1 christos 60 1.1 christos /* Number of simultaneous threads. */ 61 1.1 christos #define THREAD_COUNT 10 62 1.1 christos 63 1.1 christos /* Number of operations performed in each thread. 64 1.1 christos This is quite high, because with a smaller count, say 5000, we often get 65 1.1 christos an "OK" result even without ENABLE_LOCKING (on Linux/x86). */ 66 1.1 christos #define REPEAT_COUNT 50000 67 1.1 christos 68 1.1 christos #include <stdio.h> 69 1.1 christos #include <stdlib.h> 70 1.1 christos #include <string.h> 71 1.1 christos 72 1.1 christos #if !ENABLE_LOCKING 73 1.1 christos # undef USE_POSIX_THREADS 74 1.1 christos # undef USE_SOLARIS_THREADS 75 1.1 christos # undef USE_PTH_THREADS 76 1.1 christos # undef USE_WIN32_THREADS 77 1.1 christos #endif 78 1.1 christos #include "lock.h" 79 1.1 christos 80 1.1 christos #if ENABLE_DEBUGGING 81 1.1 christos # define dbgprintf printf 82 1.1 christos #else 83 1.1 christos # define dbgprintf if (0) printf 84 1.1 christos #endif 85 1.1 christos 86 1.1 christos #if TEST_POSIX_THREADS 87 1.1 christos # include <pthread.h> 88 1.1 christos # include <sched.h> 89 1.1 christos typedef pthread_t gl_thread_t; 90 1.1 christos static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg) 91 1.1 christos { 92 1.1 christos pthread_t thread; 93 1.1 christos if (pthread_create (&thread, NULL, func, arg) != 0) 94 1.1 christos abort (); 95 1.1 christos return thread; 96 1.1 christos } 97 1.1 christos static inline void gl_thread_join (gl_thread_t thread) 98 1.1 christos { 99 1.1 christos void *retval; 100 1.1 christos if (pthread_join (thread, &retval) != 0) 101 1.1 christos abort (); 102 1.1 christos } 103 1.1 christos static inline void gl_thread_yield (void) 104 1.1 christos { 105 1.1 christos sched_yield (); 106 1.1 christos } 107 1.1 christos static inline void * gl_thread_self (void) 108 1.1 christos { 109 1.1 christos return (void *) pthread_self (); 110 1.1 christos } 111 1.1 christos #endif 112 1.1 christos #if TEST_PTH_THREADS 113 1.1 christos # include <pth.h> 114 1.1 christos typedef pth_t gl_thread_t; 115 1.1 christos static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg) 116 1.1 christos { 117 1.1 christos pth_t thread = pth_spawn (NULL, func, arg); 118 1.1 christos if (thread == NULL) 119 1.1 christos abort (); 120 1.1 christos return thread; 121 1.1 christos } 122 1.1 christos static inline void gl_thread_join (gl_thread_t thread) 123 1.1 christos { 124 1.1 christos if (!pth_join (thread, NULL)) 125 1.1 christos abort (); 126 1.1 christos } 127 1.1 christos static inline void gl_thread_yield (void) 128 1.1 christos { 129 1.1 christos pth_yield (NULL); 130 1.1 christos } 131 1.1 christos static inline void * gl_thread_self (void) 132 1.1 christos { 133 1.1 christos return pth_self (); 134 1.1 christos } 135 1.1 christos #endif 136 1.1 christos #if TEST_SOLARIS_THREADS 137 1.1 christos # include <thread.h> 138 1.1 christos typedef thread_t gl_thread_t; 139 1.1 christos static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg) 140 1.1 christos { 141 1.1 christos thread_t thread; 142 1.1 christos if (thr_create (NULL, 0, func, arg, 0, &thread) != 0) 143 1.1 christos abort (); 144 1.1 christos return thread; 145 1.1 christos } 146 1.1 christos static inline void gl_thread_join (gl_thread_t thread) 147 1.1 christos { 148 1.1 christos void *retval; 149 1.1 christos if (thr_join (thread, NULL, &retval) != 0) 150 1.1 christos abort (); 151 1.1 christos } 152 1.1 christos static inline void gl_thread_yield (void) 153 1.1 christos { 154 1.1 christos thr_yield (); 155 1.1 christos } 156 1.1 christos static inline void * gl_thread_self (void) 157 1.1 christos { 158 1.1 christos return (void *) thr_self (); 159 1.1 christos } 160 1.1 christos #endif 161 1.1 christos #if TEST_WIN32_THREADS 162 1.1 christos # include <windows.h> 163 1.1 christos typedef HANDLE gl_thread_t; 164 1.1 christos /* Use a wrapper function, instead of adding WINAPI through a cast. */ 165 1.1 christos struct wrapper_args { void * (*func) (void *); void *arg; }; 166 1.1 christos static DWORD WINAPI wrapper_func (void *varg) 167 1.1 christos { 168 1.1 christos struct wrapper_args *warg = (struct wrapper_args *)varg; 169 1.1 christos void * (*func) (void *) = warg->func; 170 1.1 christos void *arg = warg->arg; 171 1.1 christos free (warg); 172 1.1 christos func (arg); 173 1.1 christos return 0; 174 1.1 christos } 175 1.1 christos static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg) 176 1.1 christos { 177 1.1 christos struct wrapper_args *warg = 178 1.1 christos (struct wrapper_args *) malloc (sizeof (struct wrapper_args)); 179 1.1 christos if (warg == NULL) 180 1.1 christos abort (); 181 1.1 christos warg->func = func; 182 1.1 christos warg->arg = arg; 183 1.1 christos { 184 1.1 christos DWORD thread_id; 185 1.1 christos HANDLE thread = 186 1.1 christos CreateThread (NULL, 100000, wrapper_func, warg, 0, &thread_id); 187 1.1 christos if (thread == NULL) 188 1.1 christos abort (); 189 1.1 christos return thread; 190 1.1 christos } 191 1.1 christos } 192 1.1 christos static inline void gl_thread_join (gl_thread_t thread) 193 1.1 christos { 194 1.1 christos if (WaitForSingleObject (thread, INFINITE) == WAIT_FAILED) 195 1.1 christos abort (); 196 1.1 christos if (!CloseHandle (thread)) 197 1.1 christos abort (); 198 1.1 christos } 199 1.1 christos static inline void gl_thread_yield (void) 200 1.1 christos { 201 1.1 christos Sleep (0); 202 1.1 christos } 203 1.1 christos static inline void * gl_thread_self (void) 204 1.1 christos { 205 1.1 christos return (void *) GetCurrentThreadId (); 206 1.1 christos } 207 1.1 christos #endif 208 1.1 christos #if EXPLICIT_YIELD 209 1.1 christos # define yield() gl_thread_yield () 210 1.1 christos #else 211 1.1 christos # define yield() 212 1.1 christos #endif 213 1.1 christos 214 1.1 christos #define ACCOUNT_COUNT 4 215 1.1 christos 216 1.1 christos static int account[ACCOUNT_COUNT]; 217 1.1 christos 218 1.1 christos static int 219 1.1 christos random_account (void) 220 1.1 christos { 221 1.1 christos return ((unsigned int) rand() >> 3) % ACCOUNT_COUNT; 222 1.1 christos } 223 1.1 christos 224 1.1 christos static void 225 1.1 christos check_accounts (void) 226 1.1 christos { 227 1.1 christos int i, sum; 228 1.1 christos 229 1.1 christos sum = 0; 230 1.1 christos for (i = 0; i < ACCOUNT_COUNT; i++) 231 1.1 christos sum += account[i]; 232 1.1 christos if (sum != ACCOUNT_COUNT * 1000) 233 1.1 christos abort (); 234 1.1 christos } 235 1.1 christos 236 1.1 christos /* Test normal locks by having several bank accounts and several threads 237 1.1 christos which shuffle around money between the accounts and another thread 238 1.1 christos checking that all the money is still there. */ 239 1.1 christos 240 1.1 christos gl_lock_define_initialized(static, my_lock) 241 1.1 christos 242 1.1 christos static void * 243 1.1 christos lock_mutator_thread (void *arg) 244 1.1 christos { 245 1.1 christos int repeat; 246 1.1 christos 247 1.1 christos for (repeat = REPEAT_COUNT; repeat > 0; repeat--) 248 1.1 christos { 249 1.1 christos int i1, i2, value; 250 1.1 christos 251 1.1 christos dbgprintf ("Mutator %p before lock\n", gl_thread_self ()); 252 1.1 christos gl_lock_lock (my_lock); 253 1.1 christos dbgprintf ("Mutator %p after lock\n", gl_thread_self ()); 254 1.1 christos 255 1.1 christos i1 = random_account (); 256 1.1 christos i2 = random_account (); 257 1.1 christos value = ((unsigned int) rand() >> 3) % 10; 258 1.1 christos account[i1] += value; 259 1.1 christos account[i2] -= value; 260 1.1 christos 261 1.1 christos dbgprintf ("Mutator %p before unlock\n", gl_thread_self ()); 262 1.1 christos gl_lock_unlock (my_lock); 263 1.1 christos dbgprintf ("Mutator %p after unlock\n", gl_thread_self ()); 264 1.1 christos 265 1.1 christos dbgprintf ("Mutator %p before check lock\n", gl_thread_self ()); 266 1.1 christos gl_lock_lock (my_lock); 267 1.1 christos check_accounts (); 268 1.1 christos gl_lock_unlock (my_lock); 269 1.1 christos dbgprintf ("Mutator %p after check unlock\n", gl_thread_self ()); 270 1.1 christos 271 1.1 christos yield (); 272 1.1 christos } 273 1.1 christos 274 1.1 christos dbgprintf ("Mutator %p dying.\n", gl_thread_self ()); 275 1.1 christos return NULL; 276 1.1 christos } 277 1.1 christos 278 1.1 christos static volatile int lock_checker_done; 279 1.1 christos 280 1.1 christos static void * 281 1.1 christos lock_checker_thread (void *arg) 282 1.1 christos { 283 1.1 christos while (!lock_checker_done) 284 1.1 christos { 285 1.1 christos dbgprintf ("Checker %p before check lock\n", gl_thread_self ()); 286 1.1 christos gl_lock_lock (my_lock); 287 1.1 christos check_accounts (); 288 1.1 christos gl_lock_unlock (my_lock); 289 1.1 christos dbgprintf ("Checker %p after check unlock\n", gl_thread_self ()); 290 1.1 christos 291 1.1 christos yield (); 292 1.1 christos } 293 1.1 christos 294 1.1 christos dbgprintf ("Checker %p dying.\n", gl_thread_self ()); 295 1.1 christos return NULL; 296 1.1 christos } 297 1.1 christos 298 1.1 christos void 299 1.1 christos test_lock (void) 300 1.1 christos { 301 1.1 christos int i; 302 1.1 christos gl_thread_t checkerthread; 303 1.1 christos gl_thread_t threads[THREAD_COUNT]; 304 1.1 christos 305 1.1 christos /* Initialization. */ 306 1.1 christos for (i = 0; i < ACCOUNT_COUNT; i++) 307 1.1 christos account[i] = 1000; 308 1.1 christos lock_checker_done = 0; 309 1.1 christos 310 1.1 christos /* Spawn the threads. */ 311 1.1 christos checkerthread = gl_thread_create (lock_checker_thread, NULL); 312 1.1 christos for (i = 0; i < THREAD_COUNT; i++) 313 1.1 christos threads[i] = gl_thread_create (lock_mutator_thread, NULL); 314 1.1 christos 315 1.1 christos /* Wait for the threads to terminate. */ 316 1.1 christos for (i = 0; i < THREAD_COUNT; i++) 317 1.1 christos gl_thread_join (threads[i]); 318 1.1 christos lock_checker_done = 1; 319 1.1 christos gl_thread_join (checkerthread); 320 1.1 christos check_accounts (); 321 1.1 christos } 322 1.1 christos 323 1.1 christos /* Test read-write locks by having several bank accounts and several threads 324 1.1 christos which shuffle around money between the accounts and several other threads 325 1.1 christos that check that all the money is still there. */ 326 1.1 christos 327 1.1 christos gl_rwlock_define_initialized(static, my_rwlock) 328 1.1 christos 329 1.1 christos static void * 330 1.1 christos rwlock_mutator_thread (void *arg) 331 1.1 christos { 332 1.1 christos int repeat; 333 1.1 christos 334 1.1 christos for (repeat = REPEAT_COUNT; repeat > 0; repeat--) 335 1.1 christos { 336 1.1 christos int i1, i2, value; 337 1.1 christos 338 1.1 christos dbgprintf ("Mutator %p before wrlock\n", gl_thread_self ()); 339 1.1 christos gl_rwlock_wrlock (my_rwlock); 340 1.1 christos dbgprintf ("Mutator %p after wrlock\n", gl_thread_self ()); 341 1.1 christos 342 1.1 christos i1 = random_account (); 343 1.1 christos i2 = random_account (); 344 1.1 christos value = ((unsigned int) rand() >> 3) % 10; 345 1.1 christos account[i1] += value; 346 1.1 christos account[i2] -= value; 347 1.1 christos 348 1.1 christos dbgprintf ("Mutator %p before unlock\n", gl_thread_self ()); 349 1.1 christos gl_rwlock_unlock (my_rwlock); 350 1.1 christos dbgprintf ("Mutator %p after unlock\n", gl_thread_self ()); 351 1.1 christos 352 1.1 christos yield (); 353 1.1 christos } 354 1.1 christos 355 1.1 christos dbgprintf ("Mutator %p dying.\n", gl_thread_self ()); 356 1.1 christos return NULL; 357 1.1 christos } 358 1.1 christos 359 1.1 christos static volatile int rwlock_checker_done; 360 1.1 christos 361 1.1 christos static void * 362 1.1 christos rwlock_checker_thread (void *arg) 363 1.1 christos { 364 1.1 christos while (!rwlock_checker_done) 365 1.1 christos { 366 1.1 christos dbgprintf ("Checker %p before check rdlock\n", gl_thread_self ()); 367 1.1 christos gl_rwlock_rdlock (my_rwlock); 368 1.1 christos check_accounts (); 369 1.1 christos gl_rwlock_unlock (my_rwlock); 370 1.1 christos dbgprintf ("Checker %p after check unlock\n", gl_thread_self ()); 371 1.1 christos 372 1.1 christos yield (); 373 1.1 christos } 374 1.1 christos 375 1.1 christos dbgprintf ("Checker %p dying.\n", gl_thread_self ()); 376 1.1 christos return NULL; 377 1.1 christos } 378 1.1 christos 379 1.1 christos void 380 1.1 christos test_rwlock (void) 381 1.1 christos { 382 1.1 christos int i; 383 1.1 christos gl_thread_t checkerthreads[THREAD_COUNT]; 384 1.1 christos gl_thread_t threads[THREAD_COUNT]; 385 1.1 christos 386 1.1 christos /* Initialization. */ 387 1.1 christos for (i = 0; i < ACCOUNT_COUNT; i++) 388 1.1 christos account[i] = 1000; 389 1.1 christos rwlock_checker_done = 0; 390 1.1 christos 391 1.1 christos /* Spawn the threads. */ 392 1.1 christos for (i = 0; i < THREAD_COUNT; i++) 393 1.1 christos checkerthreads[i] = gl_thread_create (rwlock_checker_thread, NULL); 394 1.1 christos for (i = 0; i < THREAD_COUNT; i++) 395 1.1 christos threads[i] = gl_thread_create (rwlock_mutator_thread, NULL); 396 1.1 christos 397 1.1 christos /* Wait for the threads to terminate. */ 398 1.1 christos for (i = 0; i < THREAD_COUNT; i++) 399 1.1 christos gl_thread_join (threads[i]); 400 1.1 christos rwlock_checker_done = 1; 401 1.1 christos for (i = 0; i < THREAD_COUNT; i++) 402 1.1 christos gl_thread_join (checkerthreads[i]); 403 1.1 christos check_accounts (); 404 1.1 christos } 405 1.1 christos 406 1.1 christos /* Test recursive locks by having several bank accounts and several threads 407 1.1 christos which shuffle around money between the accounts (recursively) and another 408 1.1 christos thread checking that all the money is still there. */ 409 1.1 christos 410 1.1 christos gl_recursive_lock_define_initialized(static, my_reclock) 411 1.1 christos 412 1.1 christos static void 413 1.1 christos recshuffle (void) 414 1.1 christos { 415 1.1 christos int i1, i2, value; 416 1.1 christos 417 1.1 christos dbgprintf ("Mutator %p before lock\n", gl_thread_self ()); 418 1.1 christos gl_recursive_lock_lock (my_reclock); 419 1.1 christos dbgprintf ("Mutator %p after lock\n", gl_thread_self ()); 420 1.1 christos 421 1.1 christos i1 = random_account (); 422 1.1 christos i2 = random_account (); 423 1.1 christos value = ((unsigned int) rand() >> 3) % 10; 424 1.1 christos account[i1] += value; 425 1.1 christos account[i2] -= value; 426 1.1 christos 427 1.1 christos /* Recursive with probability 0.5. */ 428 1.1 christos if (((unsigned int) rand() >> 3) % 2) 429 1.1 christos recshuffle (); 430 1.1 christos 431 1.1 christos dbgprintf ("Mutator %p before unlock\n", gl_thread_self ()); 432 1.1 christos gl_recursive_lock_unlock (my_reclock); 433 1.1 christos dbgprintf ("Mutator %p after unlock\n", gl_thread_self ()); 434 1.1 christos } 435 1.1 christos 436 1.1 christos static void * 437 1.1 christos reclock_mutator_thread (void *arg) 438 1.1 christos { 439 1.1 christos int repeat; 440 1.1 christos 441 1.1 christos for (repeat = REPEAT_COUNT; repeat > 0; repeat--) 442 1.1 christos { 443 1.1 christos recshuffle (); 444 1.1 christos 445 1.1 christos dbgprintf ("Mutator %p before check lock\n", gl_thread_self ()); 446 1.1 christos gl_recursive_lock_lock (my_reclock); 447 1.1 christos check_accounts (); 448 1.1 christos gl_recursive_lock_unlock (my_reclock); 449 1.1 christos dbgprintf ("Mutator %p after check unlock\n", gl_thread_self ()); 450 1.1 christos 451 1.1 christos yield (); 452 1.1 christos } 453 1.1 christos 454 1.1 christos dbgprintf ("Mutator %p dying.\n", gl_thread_self ()); 455 1.1 christos return NULL; 456 1.1 christos } 457 1.1 christos 458 1.1 christos static volatile int reclock_checker_done; 459 1.1 christos 460 1.1 christos static void * 461 1.1 christos reclock_checker_thread (void *arg) 462 1.1 christos { 463 1.1 christos while (!reclock_checker_done) 464 1.1 christos { 465 1.1 christos dbgprintf ("Checker %p before check lock\n", gl_thread_self ()); 466 1.1 christos gl_recursive_lock_lock (my_reclock); 467 1.1 christos check_accounts (); 468 1.1 christos gl_recursive_lock_unlock (my_reclock); 469 1.1 christos dbgprintf ("Checker %p after check unlock\n", gl_thread_self ()); 470 1.1 christos 471 1.1 christos yield (); 472 1.1 christos } 473 1.1 christos 474 1.1 christos dbgprintf ("Checker %p dying.\n", gl_thread_self ()); 475 1.1 christos return NULL; 476 1.1 christos } 477 1.1 christos 478 1.1 christos void 479 1.1 christos test_recursive_lock (void) 480 1.1 christos { 481 1.1 christos int i; 482 1.1 christos gl_thread_t checkerthread; 483 1.1 christos gl_thread_t threads[THREAD_COUNT]; 484 1.1 christos 485 1.1 christos /* Initialization. */ 486 1.1 christos for (i = 0; i < ACCOUNT_COUNT; i++) 487 1.1 christos account[i] = 1000; 488 1.1 christos reclock_checker_done = 0; 489 1.1 christos 490 1.1 christos /* Spawn the threads. */ 491 1.1 christos checkerthread = gl_thread_create (reclock_checker_thread, NULL); 492 1.1 christos for (i = 0; i < THREAD_COUNT; i++) 493 1.1 christos threads[i] = gl_thread_create (reclock_mutator_thread, NULL); 494 1.1 christos 495 1.1 christos /* Wait for the threads to terminate. */ 496 1.1 christos for (i = 0; i < THREAD_COUNT; i++) 497 1.1 christos gl_thread_join (threads[i]); 498 1.1 christos reclock_checker_done = 1; 499 1.1 christos gl_thread_join (checkerthread); 500 1.1 christos check_accounts (); 501 1.1 christos } 502 1.1 christos 503 1.1 christos /* Test once-only execution by having several threads attempt to grab a 504 1.1 christos once-only task simultaneously (triggered by releasing a read-write lock). */ 505 1.1 christos 506 1.1 christos gl_once_define(static, fresh_once) 507 1.1 christos static int ready[THREAD_COUNT]; 508 1.1 christos static gl_lock_t ready_lock[THREAD_COUNT]; 509 1.1 christos #if ENABLE_LOCKING 510 1.1 christos static gl_rwlock_t fire_signal[REPEAT_COUNT]; 511 1.1 christos #else 512 1.1 christos static volatile int fire_signal_state; 513 1.1 christos #endif 514 1.1 christos static gl_once_t once_control; 515 1.1 christos static int performed; 516 1.1 christos gl_lock_define_initialized(static, performed_lock) 517 1.1 christos 518 1.1 christos static void 519 1.1 christos once_execute (void) 520 1.1 christos { 521 1.1 christos gl_lock_lock (performed_lock); 522 1.1 christos performed++; 523 1.1 christos gl_lock_unlock (performed_lock); 524 1.1 christos } 525 1.1 christos 526 1.1 christos static void * 527 1.1 christos once_contender_thread (void *arg) 528 1.1 christos { 529 1.1 christos int id = (int) (long) arg; 530 1.1 christos int repeat; 531 1.1 christos 532 1.1 christos for (repeat = 0; repeat <= REPEAT_COUNT; repeat++) 533 1.1 christos { 534 1.1 christos /* Tell the main thread that we're ready. */ 535 1.1 christos gl_lock_lock (ready_lock[id]); 536 1.1 christos ready[id] = 1; 537 1.1 christos gl_lock_unlock (ready_lock[id]); 538 1.1 christos 539 1.1 christos if (repeat == REPEAT_COUNT) 540 1.1 christos break; 541 1.1 christos 542 1.1 christos dbgprintf ("Contender %p waiting for signal for round %d\n", 543 1.1 christos gl_thread_self (), repeat); 544 1.1 christos #if ENABLE_LOCKING 545 1.1 christos /* Wait for the signal to go. */ 546 1.1 christos gl_rwlock_rdlock (fire_signal[repeat]); 547 1.1 christos /* And don't hinder the others (if the scheduler is unfair). */ 548 1.1 christos gl_rwlock_unlock (fire_signal[repeat]); 549 1.1 christos #else 550 1.1 christos /* Wait for the signal to go. */ 551 1.1 christos while (fire_signal_state <= repeat) 552 1.1 christos yield (); 553 1.1 christos #endif 554 1.1 christos dbgprintf ("Contender %p got the signal for round %d\n", 555 1.1 christos gl_thread_self (), repeat); 556 1.1 christos 557 1.1 christos /* Contend for execution. */ 558 1.1 christos gl_once (once_control, once_execute); 559 1.1 christos } 560 1.1 christos 561 1.1 christos return NULL; 562 1.1 christos } 563 1.1 christos 564 1.1 christos void 565 1.1 christos test_once (void) 566 1.1 christos { 567 1.1 christos int i, repeat; 568 1.1 christos gl_thread_t threads[THREAD_COUNT]; 569 1.1 christos 570 1.1 christos /* Initialize all variables. */ 571 1.1 christos for (i = 0; i < THREAD_COUNT; i++) 572 1.1 christos { 573 1.1 christos ready[i] = 0; 574 1.1 christos gl_lock_init (ready_lock[i]); 575 1.1 christos } 576 1.1 christos #if ENABLE_LOCKING 577 1.1 christos for (i = 0; i < REPEAT_COUNT; i++) 578 1.1 christos gl_rwlock_init (fire_signal[i]); 579 1.1 christos #else 580 1.1 christos fire_signal_state = 0; 581 1.1 christos #endif 582 1.1 christos 583 1.1 christos /* Block all fire_signals. */ 584 1.1 christos for (i = REPEAT_COUNT-1; i >= 0; i--) 585 1.1 christos gl_rwlock_wrlock (fire_signal[i]); 586 1.1 christos 587 1.1 christos /* Spawn the threads. */ 588 1.1 christos for (i = 0; i < THREAD_COUNT; i++) 589 1.1 christos threads[i] = gl_thread_create (once_contender_thread, (void *) (long) i); 590 1.1 christos 591 1.1 christos for (repeat = 0; repeat <= REPEAT_COUNT; repeat++) 592 1.1 christos { 593 1.1 christos /* Wait until every thread is ready. */ 594 1.1 christos dbgprintf ("Main thread before synchonizing for round %d\n", repeat); 595 1.1 christos for (;;) 596 1.1 christos { 597 1.1 christos int ready_count = 0; 598 1.1 christos for (i = 0; i < THREAD_COUNT; i++) 599 1.1 christos { 600 1.1 christos gl_lock_lock (ready_lock[i]); 601 1.1 christos ready_count += ready[i]; 602 1.1 christos gl_lock_unlock (ready_lock[i]); 603 1.1 christos } 604 1.1 christos if (ready_count == THREAD_COUNT) 605 1.1 christos break; 606 1.1 christos yield (); 607 1.1 christos } 608 1.1 christos dbgprintf ("Main thread after synchonizing for round %d\n", repeat); 609 1.1 christos 610 1.1 christos if (repeat > 0) 611 1.1 christos { 612 1.1 christos /* Check that exactly one thread executed the once_execute() 613 1.1 christos function. */ 614 1.1 christos if (performed != 1) 615 1.1 christos abort (); 616 1.1 christos } 617 1.1 christos 618 1.1 christos if (repeat == REPEAT_COUNT) 619 1.1 christos break; 620 1.1 christos 621 1.1 christos /* Preparation for the next round: Initialize once_control. */ 622 1.1 christos memcpy (&once_control, &fresh_once, sizeof (gl_once_t)); 623 1.1 christos 624 1.1 christos /* Preparation for the next round: Reset the performed counter. */ 625 1.1 christos performed = 0; 626 1.1 christos 627 1.1 christos /* Preparation for the next round: Reset the ready flags. */ 628 1.1 christos for (i = 0; i < THREAD_COUNT; i++) 629 1.1 christos { 630 1.1 christos gl_lock_lock (ready_lock[i]); 631 1.1 christos ready[i] = 0; 632 1.1 christos gl_lock_unlock (ready_lock[i]); 633 1.1 christos } 634 1.1 christos 635 1.1 christos /* Signal all threads simultaneously. */ 636 1.1 christos dbgprintf ("Main thread giving signal for round %d\n", repeat); 637 1.1 christos #if ENABLE_LOCKING 638 1.1 christos gl_rwlock_unlock (fire_signal[repeat]); 639 1.1 christos #else 640 1.1 christos fire_signal_state = repeat + 1; 641 1.1 christos #endif 642 1.1 christos } 643 1.1 christos 644 1.1 christos /* Wait for the threads to terminate. */ 645 1.1 christos for (i = 0; i < THREAD_COUNT; i++) 646 1.1 christos gl_thread_join (threads[i]); 647 1.1 christos } 648 1.1 christos 649 1.1 christos int 650 1.1 christos main () 651 1.1 christos { 652 1.1 christos #if TEST_PTH_THREADS 653 1.1 christos if (!pth_init ()) 654 1.1 christos abort (); 655 1.1 christos #endif 656 1.1 christos 657 1.1 christos #if DO_TEST_LOCK 658 1.1 christos printf ("Starting test_lock ..."); fflush (stdout); 659 1.1 christos test_lock (); 660 1.1 christos printf (" OK\n"); fflush (stdout); 661 1.1 christos #endif 662 1.1 christos #if DO_TEST_RWLOCK 663 1.1 christos printf ("Starting test_rwlock ..."); fflush (stdout); 664 1.1 christos test_rwlock (); 665 1.1 christos printf (" OK\n"); fflush (stdout); 666 1.1 christos #endif 667 1.1 christos #if DO_TEST_RECURSIVE_LOCK 668 1.1 christos printf ("Starting test_recursive_lock ..."); fflush (stdout); 669 1.1 christos test_recursive_lock (); 670 1.1 christos printf (" OK\n"); fflush (stdout); 671 1.1 christos #endif 672 1.1 christos #if DO_TEST_ONCE 673 1.1 christos printf ("Starting test_once ..."); fflush (stdout); 674 1.1 christos test_once (); 675 1.1 christos printf (" OK\n"); fflush (stdout); 676 1.1 christos #endif 677 1.1 christos 678 1.1 christos return 0; 679 1.1 christos } 680 1.1 christos 681 1.1 christos #else 682 1.1 christos 683 1.1 christos /* No multithreading available. */ 684 1.1 christos 685 1.1 christos int 686 1.1 christos main () 687 1.1 christos { 688 1.1 christos return 77; 689 1.1 christos } 690 1.1 christos 691 1.1 christos #endif 692