Line data Source code
1 : /* Test of locking in multithreaded situations. 2 : Copyright (C) 2005, 2008-2020 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 <https://www.gnu.org/licenses/>. */ 16 : 17 : /* Written by Bruno Haible <bruno@clisp.org>, 2005. */ 18 : 19 : #include <config.h> 20 : 21 : #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS 22 : 23 : #if USE_ISOC_THREADS 24 : # define TEST_ISOC_THREADS 1 25 : #endif 26 : #if USE_POSIX_THREADS 27 : # define TEST_POSIX_THREADS 1 28 : #endif 29 : #if USE_ISOC_AND_POSIX_THREADS 30 : # define TEST_ISOC_AND_POSIX_THREADS 1 31 : #endif 32 : #if USE_WINDOWS_THREADS 33 : # define TEST_WINDOWS_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 <stdint.h> 65 : #include <stdio.h> 66 : #include <stdlib.h> 67 : #include <string.h> 68 : 69 : #if !ENABLE_LOCKING 70 : # undef USE_ISOC_THREADS 71 : # undef USE_POSIX_THREADS 72 : # undef USE_ISOC_AND_POSIX_THREADS 73 : # undef USE_WINDOWS_THREADS 74 : #endif 75 : #include "glthread/lock.h" 76 : 77 : #if !ENABLE_LOCKING 78 : # if TEST_ISOC_THREADS 79 : # define USE_ISOC_THREADS 1 80 : # endif 81 : # if TEST_POSIX_THREADS 82 : # define USE_POSIX_THREADS 1 83 : # endif 84 : # if TEST_ISOC_AND_POSIX_THREADS 85 : # define USE_ISOC_AND_POSIX_THREADS 1 86 : # endif 87 : # if TEST_WINDOWS_THREADS 88 : # define USE_WINDOWS_THREADS 1 89 : # endif 90 : #endif 91 : 92 : #include "glthread/thread.h" 93 : #include "glthread/yield.h" 94 : 95 : #if HAVE_DECL_ALARM 96 : # include <signal.h> 97 : # include <unistd.h> 98 : #endif 99 : 100 : #include "atomic-int-gnulib.h" 101 : 102 : #if ENABLE_DEBUGGING 103 : # define dbgprintf printf 104 : #else 105 : # define dbgprintf if (0) printf 106 : #endif 107 : 108 : #if EXPLICIT_YIELD 109 : # define yield() gl_thread_yield () 110 : #else 111 : # define yield() 112 : #endif 113 : 114 : #define ACCOUNT_COUNT 4 115 : 116 : static int account[ACCOUNT_COUNT]; 117 : 118 : static int 119 4002468 : random_account (void) 120 : { 121 4002468 : return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT; 122 : } 123 : 124 : static void 125 1397026 : check_accounts (void) 126 : { 127 : int i, sum; 128 : 129 1397026 : sum = 0; 130 6981277 : for (i = 0; i < ACCOUNT_COUNT; i++) 131 5584251 : sum += account[i]; 132 1397026 : if (sum != ACCOUNT_COUNT * 1000) 133 0 : abort (); 134 1397026 : } 135 : 136 : 137 : /* ------------------- Test normal (non-recursive) locks ------------------- */ 138 : 139 : /* Test normal locks by having several bank accounts and several threads 140 : which shuffle around money between the accounts and another thread 141 : checking that all the money is still there. */ 142 : 143 : gl_lock_define_initialized(static, my_lock) 144 : 145 : static void * 146 10 : lock_mutator_thread (void *arg) 147 : { 148 : int repeat; 149 : 150 495030 : for (repeat = REPEAT_COUNT; repeat > 0; repeat--) 151 : { 152 : int i1, i2, value; 153 : 154 : dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ()); 155 494481 : gl_lock_lock (my_lock); 156 : dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ()); 157 : 158 500000 : i1 = random_account (); 159 500000 : i2 = random_account (); 160 500000 : value = ((unsigned int) rand () >> 3) % 10; 161 500000 : account[i1] += value; 162 500000 : account[i2] -= value; 163 : 164 : dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ()); 165 500000 : gl_lock_unlock (my_lock); 166 : dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ()); 167 : 168 : dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ()); 169 498429 : gl_lock_lock (my_lock); 170 500000 : check_accounts (); 171 500000 : gl_lock_unlock (my_lock); 172 : dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ()); 173 : 174 498477 : yield (); 175 : } 176 : 177 : dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ()); 178 549 : return NULL; 179 : } 180 : 181 : static struct atomic_int lock_checker_done; 182 : 183 : static void * 184 1 : lock_checker_thread (void *arg) 185 : { 186 91689 : while (get_atomic_int_value (&lock_checker_done) == 0) 187 : { 188 : dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ()); 189 91688 : gl_lock_lock (my_lock); 190 91688 : check_accounts (); 191 91688 : gl_lock_unlock (my_lock); 192 : dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ()); 193 : 194 91688 : yield (); 195 : } 196 : 197 : dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ()); 198 1 : return NULL; 199 : } 200 : 201 : static void 202 1 : test_lock (void) 203 : { 204 : int i; 205 : gl_thread_t checkerthread; 206 : gl_thread_t threads[THREAD_COUNT]; 207 : 208 : /* Initialization. */ 209 5 : for (i = 0; i < ACCOUNT_COUNT; i++) 210 4 : account[i] = 1000; 211 1 : init_atomic_int (&lock_checker_done); 212 1 : set_atomic_int_value (&lock_checker_done, 0); 213 : 214 : /* Spawn the threads. */ 215 1 : checkerthread = gl_thread_create (lock_checker_thread, NULL); 216 11 : for (i = 0; i < THREAD_COUNT; i++) 217 10 : threads[i] = gl_thread_create (lock_mutator_thread, NULL); 218 : 219 : /* Wait for the threads to terminate. */ 220 11 : for (i = 0; i < THREAD_COUNT; i++) 221 10 : gl_thread_join (threads[i], NULL); 222 1 : set_atomic_int_value (&lock_checker_done, 1); 223 1 : gl_thread_join (checkerthread, NULL); 224 1 : check_accounts (); 225 1 : } 226 : 227 : 228 : /* ----------------- Test read-write (non-recursive) locks ----------------- */ 229 : 230 : /* Test read-write locks by having several bank accounts and several threads 231 : which shuffle around money between the accounts and several other threads 232 : that check that all the money is still there. */ 233 : 234 : gl_rwlock_define_initialized(static, my_rwlock) 235 : 236 : static void * 237 10 : rwlock_mutator_thread (void *arg) 238 : { 239 : int repeat; 240 : 241 495508 : for (repeat = REPEAT_COUNT; repeat > 0; repeat--) 242 : { 243 : int i1, i2, value; 244 : 245 : dbgprintf ("Mutator %p before wrlock\n", gl_thread_self_pointer ()); 246 494318 : gl_rwlock_wrlock (my_rwlock); 247 : dbgprintf ("Mutator %p after wrlock\n", gl_thread_self_pointer ()); 248 : 249 500000 : i1 = random_account (); 250 500000 : i2 = random_account (); 251 500000 : value = ((unsigned int) rand () >> 3) % 10; 252 500000 : account[i1] += value; 253 500000 : account[i2] -= value; 254 : 255 : dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ()); 256 500000 : gl_rwlock_unlock (my_rwlock); 257 : dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ()); 258 : 259 498107 : yield (); 260 : } 261 : 262 : dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ()); 263 1190 : return NULL; 264 : } 265 : 266 : static struct atomic_int rwlock_checker_done; 267 : 268 : static void * 269 10 : rwlock_checker_thread (void *arg) 270 : { 271 208396 : while (get_atomic_int_value (&rwlock_checker_done) == 0) 272 : { 273 : dbgprintf ("Checker %p before check rdlock\n", gl_thread_self_pointer ()); 274 209493 : gl_rwlock_rdlock (my_rwlock); 275 217431 : check_accounts (); 276 216803 : gl_rwlock_unlock (my_rwlock); 277 : dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ()); 278 : 279 217779 : yield (); 280 : } 281 : 282 : dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ()); 283 10 : return NULL; 284 : } 285 : 286 : static void 287 1 : test_rwlock (void) 288 : { 289 : int i; 290 : gl_thread_t checkerthreads[THREAD_COUNT]; 291 : gl_thread_t threads[THREAD_COUNT]; 292 : 293 : /* Initialization. */ 294 5 : for (i = 0; i < ACCOUNT_COUNT; i++) 295 4 : account[i] = 1000; 296 1 : init_atomic_int (&rwlock_checker_done); 297 1 : set_atomic_int_value (&rwlock_checker_done, 0); 298 : 299 : /* Spawn the threads. */ 300 11 : for (i = 0; i < THREAD_COUNT; i++) 301 10 : checkerthreads[i] = gl_thread_create (rwlock_checker_thread, NULL); 302 11 : for (i = 0; i < THREAD_COUNT; i++) 303 10 : threads[i] = gl_thread_create (rwlock_mutator_thread, NULL); 304 : 305 : /* Wait for the threads to terminate. */ 306 11 : for (i = 0; i < THREAD_COUNT; i++) 307 10 : gl_thread_join (threads[i], NULL); 308 1 : set_atomic_int_value (&rwlock_checker_done, 1); 309 11 : for (i = 0; i < THREAD_COUNT; i++) 310 10 : gl_thread_join (checkerthreads[i], NULL); 311 1 : check_accounts (); 312 1 : } 313 : 314 : 315 : /* -------------------------- Test recursive locks -------------------------- */ 316 : 317 : /* Test recursive locks by having several bank accounts and several threads 318 : which shuffle around money between the accounts (recursively) and another 319 : thread checking that all the money is still there. */ 320 : 321 : gl_recursive_lock_define_initialized(static, my_reclock) 322 : 323 : static void 324 993012 : recshuffle (void) 325 : { 326 : int i1, i2, value; 327 : 328 : dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ()); 329 993012 : gl_recursive_lock_lock (my_reclock); 330 : dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ()); 331 : 332 1001234 : i1 = random_account (); 333 1001234 : i2 = random_account (); 334 1001234 : value = ((unsigned int) rand () >> 3) % 10; 335 1001234 : account[i1] += value; 336 1001234 : account[i2] -= value; 337 : 338 : /* Recursive with probability 0.5. */ 339 1001234 : if (((unsigned int) rand () >> 3) % 2) 340 501234 : recshuffle (); 341 : 342 : dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ()); 343 1001234 : gl_recursive_lock_unlock (my_reclock); 344 : dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ()); 345 997630 : } 346 : 347 : static void * 348 10 : reclock_mutator_thread (void *arg) 349 : { 350 : int repeat; 351 : 352 495348 : for (repeat = REPEAT_COUNT; repeat > 0; repeat--) 353 : { 354 496150 : recshuffle (); 355 : 356 : dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ()); 357 496871 : gl_recursive_lock_lock (my_reclock); 358 500000 : check_accounts (); 359 500000 : gl_recursive_lock_unlock (my_reclock); 360 : dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ()); 361 : 362 497482 : yield (); 363 : } 364 : 365 : dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ()); 366 0 : return NULL; 367 : } 368 : 369 : static struct atomic_int reclock_checker_done; 370 : 371 : static void * 372 1 : reclock_checker_thread (void *arg) 373 : { 374 88467 : while (get_atomic_int_value (&reclock_checker_done) == 0) 375 : { 376 : dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ()); 377 88466 : gl_recursive_lock_lock (my_reclock); 378 88466 : check_accounts (); 379 88466 : gl_recursive_lock_unlock (my_reclock); 380 : dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ()); 381 : 382 88466 : yield (); 383 : } 384 : 385 : dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ()); 386 1 : return NULL; 387 : } 388 : 389 : static void 390 1 : test_recursive_lock (void) 391 : { 392 : int i; 393 : gl_thread_t checkerthread; 394 : gl_thread_t threads[THREAD_COUNT]; 395 : 396 : /* Initialization. */ 397 5 : for (i = 0; i < ACCOUNT_COUNT; i++) 398 4 : account[i] = 1000; 399 1 : init_atomic_int (&reclock_checker_done); 400 1 : set_atomic_int_value (&reclock_checker_done, 0); 401 : 402 : /* Spawn the threads. */ 403 1 : checkerthread = gl_thread_create (reclock_checker_thread, NULL); 404 11 : for (i = 0; i < THREAD_COUNT; i++) 405 10 : threads[i] = gl_thread_create (reclock_mutator_thread, NULL); 406 : 407 : /* Wait for the threads to terminate. */ 408 11 : for (i = 0; i < THREAD_COUNT; i++) 409 10 : gl_thread_join (threads[i], NULL); 410 1 : set_atomic_int_value (&reclock_checker_done, 1); 411 1 : gl_thread_join (checkerthread, NULL); 412 1 : check_accounts (); 413 1 : } 414 : 415 : 416 : /* ------------------------ Test once-only execution ------------------------ */ 417 : 418 : /* Test once-only execution by having several threads attempt to grab a 419 : once-only task simultaneously (triggered by releasing a read-write lock). */ 420 : 421 : gl_once_define(static, fresh_once) 422 : static int ready[THREAD_COUNT]; 423 : static gl_lock_t ready_lock[THREAD_COUNT]; 424 : #if ENABLE_LOCKING 425 : static gl_rwlock_t fire_signal[REPEAT_COUNT]; 426 : #else 427 : static volatile int fire_signal_state; 428 : #endif 429 : static gl_once_t once_control; 430 : static int performed; 431 : gl_lock_define_initialized(static, performed_lock) 432 : 433 : static void 434 50000 : once_execute (void) 435 : { 436 50000 : gl_lock_lock (performed_lock); 437 50000 : performed++; 438 50000 : gl_lock_unlock (performed_lock); 439 50000 : } 440 : 441 : static void * 442 10 : once_contender_thread (void *arg) 443 : { 444 10 : int id = (int) (intptr_t) arg; 445 : int repeat; 446 : 447 497250 : for (repeat = 0; repeat <= REPEAT_COUNT; repeat++) 448 : { 449 : /* Tell the main thread that we're ready. */ 450 497681 : gl_lock_lock (ready_lock[id]); 451 498530 : ready[id] = 1; 452 498530 : gl_lock_unlock (ready_lock[id]); 453 : 454 498299 : if (repeat == REPEAT_COUNT) 455 10 : break; 456 : 457 : dbgprintf ("Contender %p waiting for signal for round %d\n", 458 : gl_thread_self_pointer (), repeat); 459 : #if ENABLE_LOCKING 460 : /* Wait for the signal to go. */ 461 498289 : gl_rwlock_rdlock (fire_signal[repeat]); 462 : /* And don't hinder the others (if the scheduler is unfair). */ 463 499643 : gl_rwlock_unlock (fire_signal[repeat]); 464 : #else 465 : /* Wait for the signal to go. */ 466 : while (fire_signal_state <= repeat) 467 : yield (); 468 : #endif 469 : dbgprintf ("Contender %p got the signal for round %d\n", 470 : gl_thread_self_pointer (), repeat); 471 : 472 : /* Contend for execution. */ 473 499549 : gl_once (once_control, once_execute); 474 : } 475 : 476 0 : return NULL; 477 : } 478 : 479 : static void 480 1 : test_once (void) 481 : { 482 : int i, repeat; 483 : gl_thread_t threads[THREAD_COUNT]; 484 : 485 : /* Initialize all variables. */ 486 11 : for (i = 0; i < THREAD_COUNT; i++) 487 : { 488 10 : ready[i] = 0; 489 10 : gl_lock_init (ready_lock[i]); 490 : } 491 : #if ENABLE_LOCKING 492 50001 : for (i = 0; i < REPEAT_COUNT; i++) 493 50000 : gl_rwlock_init (fire_signal[i]); 494 : #else 495 : fire_signal_state = 0; 496 : #endif 497 : 498 : #if ENABLE_LOCKING 499 : /* Block all fire_signals. */ 500 50001 : for (i = REPEAT_COUNT-1; i >= 0; i--) 501 50000 : gl_rwlock_wrlock (fire_signal[i]); 502 : #endif 503 : 504 : /* Spawn the threads. */ 505 11 : for (i = 0; i < THREAD_COUNT; i++) 506 10 : threads[i] = 507 10 : gl_thread_create (once_contender_thread, (void *) (intptr_t) i); 508 : 509 50001 : for (repeat = 0; repeat <= REPEAT_COUNT; repeat++) 510 : { 511 : /* Wait until every thread is ready. */ 512 : dbgprintf ("Main thread before synchronizing for round %d\n", repeat); 513 : for (;;) 514 133943 : { 515 183944 : int ready_count = 0; 516 2023384 : for (i = 0; i < THREAD_COUNT; i++) 517 : { 518 1839440 : gl_lock_lock (ready_lock[i]); 519 1839440 : ready_count += ready[i]; 520 1839440 : gl_lock_unlock (ready_lock[i]); 521 : } 522 183944 : if (ready_count == THREAD_COUNT) 523 50001 : break; 524 133943 : yield (); 525 : } 526 : dbgprintf ("Main thread after synchronizing for round %d\n", repeat); 527 : 528 50001 : if (repeat > 0) 529 : { 530 : /* Check that exactly one thread executed the once_execute() 531 : function. */ 532 50000 : if (performed != 1) 533 0 : abort (); 534 : } 535 : 536 50001 : if (repeat == REPEAT_COUNT) 537 1 : break; 538 : 539 : /* Preparation for the next round: Initialize once_control. */ 540 50000 : memcpy (&once_control, &fresh_once, sizeof (gl_once_t)); 541 : 542 : /* Preparation for the next round: Reset the performed counter. */ 543 50000 : performed = 0; 544 : 545 : /* Preparation for the next round: Reset the ready flags. */ 546 550000 : for (i = 0; i < THREAD_COUNT; i++) 547 : { 548 500000 : gl_lock_lock (ready_lock[i]); 549 500000 : ready[i] = 0; 550 500000 : gl_lock_unlock (ready_lock[i]); 551 : } 552 : 553 : /* Signal all threads simultaneously. */ 554 : dbgprintf ("Main thread giving signal for round %d\n", repeat); 555 : #if ENABLE_LOCKING 556 50000 : gl_rwlock_unlock (fire_signal[repeat]); 557 : #else 558 : fire_signal_state = repeat + 1; 559 : #endif 560 : } 561 : 562 : /* Wait for the threads to terminate. */ 563 11 : for (i = 0; i < THREAD_COUNT; i++) 564 10 : gl_thread_join (threads[i], NULL); 565 1 : } 566 : 567 : 568 : /* -------------------------------------------------------------------------- */ 569 : 570 : int 571 1 : main () 572 : { 573 : #if HAVE_DECL_ALARM 574 : /* Declare failure if test takes too long, by using default abort 575 : caused by SIGALRM. */ 576 1 : int alarm_value = 600; 577 1 : signal (SIGALRM, SIG_DFL); 578 1 : alarm (alarm_value); 579 : #endif 580 : 581 : #if DO_TEST_LOCK 582 1 : printf ("Starting test_lock ..."); fflush (stdout); 583 1 : test_lock (); 584 1 : printf (" OK\n"); fflush (stdout); 585 : #endif 586 : #if DO_TEST_RWLOCK 587 1 : printf ("Starting test_rwlock ..."); fflush (stdout); 588 1 : test_rwlock (); 589 1 : printf (" OK\n"); fflush (stdout); 590 : #endif 591 : #if DO_TEST_RECURSIVE_LOCK 592 1 : printf ("Starting test_recursive_lock ..."); fflush (stdout); 593 1 : test_recursive_lock (); 594 1 : printf (" OK\n"); fflush (stdout); 595 : #endif 596 : #if DO_TEST_ONCE 597 1 : printf ("Starting test_once ..."); fflush (stdout); 598 1 : test_once (); 599 1 : printf (" OK\n"); fflush (stdout); 600 : #endif 601 : 602 1 : return 0; 603 : } 604 : 605 : #else 606 : 607 : /* No multithreading available. */ 608 : 609 : #include <stdio.h> 610 : 611 : int 612 : main () 613 : { 614 : fputs ("Skipping test: multithreading not enabled\n", stderr); 615 : return 77; 616 : } 617 : 618 : #endif