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