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