1 /* 2 * Copyright (c) 2004, Bull S.A.. All rights reserved. 3 * Created by: Sebastien Decugis 4 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of version 2 of the GNU General Public License as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it would be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 * 13 * You should have received a copy of the GNU General Public License along 14 * with this program; if not, write the Free Software Foundation, Inc., 15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 16 * 17 18 * This file is a stress test for the pthread_mutex_lock function. 19 20 * The steps are: 21 * -> For each king of mutex, we create 10*F threads (F is a scalability factor) 22 * -> we call those threads 1 to 10. 23 * -> thread 1 sends signal USR2 to the other 9 threads (which have a handler for it) 24 * -> thread 2 to 6 are loops 25 * { 26 * mutex_lock 27 * if (ctrl) exit 28 * ctrl = 1 29 * yield 30 * ctrl= 0 31 * mutex unlock 32 * } 33 * -> thread 7 & 8 have a timedlock instead of lock 34 * -> thread 9 & 10 have a trylock instead of lock 35 * 36 * -> the whole process stop when receiving signal SIGUSR1. 37 * This goal is achieved with a "do_it" variable. 38 * 39 * NOTE: With gcc/linux, the flag "-lrt" must be specified at link time. 40 */ 41 42 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */ 43 #define _POSIX_C_SOURCE 200112L 44 45 /* We enable the following line to have mutex attributes defined */ 46 #ifndef WITHOUT_XOPEN 47 #define _XOPEN_SOURCE 600 48 #endif 49 50 /********************************************************************************************/ 51 /****************************** standard includes *****************************************/ 52 /********************************************************************************************/ 53 #include <pthread.h> 54 #include <errno.h> 55 #include <semaphore.h> 56 #include <signal.h> 57 #include <unistd.h> 58 #if _POSIX_TIMEOUTS < 0 59 #error "This sample needs POSIX TIMEOUTS option support" 60 #endif 61 #if _POSIX_TIMEOUTS == 0 62 #warning "This sample needs POSIX TIMEOUTS option support" 63 #endif 64 #if _POSIX_TIMERS < 0 65 #error "This sample needs POSIX TIMERS option support" 66 #endif 67 #if _POSIX_TIMERS == 0 68 #warning "This sample needs POSIX TIMERS option support" 69 #endif 70 71 #include <stdio.h> 72 #include <stdlib.h> 73 #include <stdarg.h> 74 #include <time.h> /* required for the pthread_mutex_timedlock() function */ 75 76 /********************************************************************************************/ 77 /****************************** Test framework *****************************************/ 78 /********************************************************************************************/ 79 #include "testfrmw.h" 80 #include "testfrmw.c" 81 /* This header is responsible for defining the following macros: 82 * UNRESOLVED(ret, descr); 83 * where descr is a description of the error and ret is an int (error code for example) 84 * FAILED(descr); 85 * where descr is a short text saying why the test has failed. 86 * PASSED(); 87 * No parameter. 88 * 89 * Both three macros shall terminate the calling process. 90 * The testcase shall not terminate in any other maneer. 91 * 92 * The other file defines the functions 93 * void output_init() 94 * void output(char * string, ...) 95 * 96 * Those may be used to output information. 97 */ 98 99 /********************************************************************************************/ 100 /********************************** Configuration ******************************************/ 101 /********************************************************************************************/ 102 #ifndef SCALABILITY_FACTOR 103 #define SCALABILITY_FACTOR 1 104 #endif 105 #ifndef VERBOSE 106 #define VERBOSE 2 107 #endif 108 #define N 2 /* N * 10 * 6 * SCALABILITY_FACTOR threads will be created */ 109 110 /********************************************************************************************/ 111 /*********************************** Test case *****************************************/ 112 /********************************************************************************************/ 113 char do_it = 1; 114 #ifndef WITHOUT_XOPEN 115 int types[] = { PTHREAD_MUTEX_NORMAL, 116 PTHREAD_MUTEX_ERRORCHECK, 117 PTHREAD_MUTEX_RECURSIVE, 118 PTHREAD_MUTEX_DEFAULT 119 }; 120 #endif 121 122 /* The following type represents the data 123 * for one group of ten threads */ 124 typedef struct { 125 pthread_t threads[10]; /* The 10 threads */ 126 pthread_mutex_t mtx; /* The mutex those threads work on */ 127 char ctrl; /* The value used to check the behavior */ 128 char sigok; /* Used to tell the threads they can return */ 129 sem_t semsig; /* Semaphore for synchronizing the signal handler */ 130 int id; /* An identifier for the threads group */ 131 int tcnt; /* we need to make sure the threads are started before killing 'em */ 132 pthread_mutex_t tmtx; 133 unsigned long long sigcnt, opcnt; /* We count every iteration */ 134 } cell_t; 135 136 pthread_key_t _c; /* this key will always contain a pointer to the thread's cell */ 137 138 /***** The next function is in charge of sending signal USR2 to 139 * all the other threads in its cell, until the end of the test. */ 140 void *sigthr(void *arg) 141 { 142 int ret; 143 int i = 0; 144 cell_t *c = (cell_t *) arg; 145 146 do { 147 sched_yield(); 148 ret = pthread_mutex_lock(&(c->tmtx)); 149 if (ret != 0) { 150 UNRESOLVED(ret, "Failed to lock the mutex"); 151 } 152 i = c->tcnt; 153 ret = pthread_mutex_unlock(&(c->tmtx)); 154 if (ret != 0) { 155 UNRESOLVED(ret, "Failed to unlock the mutex"); 156 } 157 } while (i < 9); 158 159 /* Until we must stop, do */ 160 while (do_it) { 161 /* Wait for the semaphore */ 162 ret = sem_wait(&(c->semsig)); 163 if (ret != 0) { 164 UNRESOLVED(errno, "Sem wait failed in signal thread"); 165 } 166 167 /* Kill the next thread */ 168 i %= 9; 169 ret = pthread_kill(c->threads[++i], SIGUSR2); 170 if (ret != 0) { 171 UNRESOLVED(ret, "Thread kill failed in signal thread"); 172 } 173 174 /* Increment the signal counter */ 175 c->sigcnt++; 176 } 177 178 /* Tell the other threads they can now stop */ 179 do { 180 c->sigok = 1; 181 } 182 while (c->sigok == 0); 183 184 return NULL; 185 } 186 187 /***** The next function is the signal handler 188 * for all the others threads in the cell */ 189 void sighdl(int sig) 190 { 191 int ret; 192 cell_t *c = (cell_t *) pthread_getspecific(_c); 193 ret = sem_post(&(c->semsig)); 194 if (ret != 0) { 195 UNRESOLVED(errno, "Unable to post semaphore in signal handler"); 196 } 197 } 198 199 /***** The next function can return only when the sigthr has terminated. 200 * This avoids the signal thread try to kill a terminated thread. */ 201 void waitsigend(cell_t * c) 202 { 203 while (c->sigok == 0) { 204 sched_yield(); 205 } 206 } 207 208 /***** The next function aims to control that no other thread 209 * owns the mutex at the same time */ 210 void control(cell_t * c, char *loc) 211 { 212 *loc++; /* change the local control value */ 213 if (c->ctrl != 0) { 214 FAILED("Got a non-zero value - two threads owns the mutex"); 215 } 216 c->ctrl = *loc; 217 sched_yield(); 218 if (c->ctrl != *loc) { 219 FAILED 220 ("Got a different value - another thread touched protected data"); 221 } 222 c->ctrl = 0; 223 224 /* Avoid some values for the next control */ 225 if (*loc == 120) 226 *loc = -120; 227 if (*loc == -1) 228 *loc = 1; 229 } 230 231 /***** The next 3 functions are the worker threads 232 */ 233 void *lockthr(void *arg) 234 { 235 int ret; 236 char loc; /* Local value for control */ 237 cell_t *c = (cell_t *) arg; 238 239 /* Set the thread local data key value (used in the signal handler) */ 240 ret = pthread_setspecific(_c, arg); 241 if (ret != 0) { 242 UNRESOLVED(ret, "Unable to assign the thread-local-data key"); 243 } 244 245 /* Signal we're started */ 246 ret = pthread_mutex_lock(&(c->tmtx)); 247 if (ret != 0) { 248 UNRESOLVED(ret, "Failed to lock the mutex"); 249 } 250 c->tcnt += 1; 251 ret = pthread_mutex_unlock(&(c->tmtx)); 252 if (ret != 0) { 253 UNRESOLVED(ret, "Failed to unlock the mutex"); 254 } 255 256 do { 257 /* Lock, control, then unlock */ 258 ret = pthread_mutex_lock(&(c->mtx)); 259 if (ret != 0) { 260 UNRESOLVED(ret, "Mutex lock failed in worker thread"); 261 } 262 263 control(c, &loc); 264 265 ret = pthread_mutex_unlock(&(c->mtx)); 266 if (ret != 0) { 267 UNRESOLVED(ret, "Mutex unlock failed in worker thread"); 268 } 269 270 /* Increment the operation counter */ 271 c->opcnt++; 272 } 273 while (do_it); 274 275 /* Wait for the signal thread to terminate before we can exit */ 276 waitsigend(c); 277 return NULL; 278 } 279 280 void *timedlockthr(void *arg) 281 { 282 int ret; 283 char loc; /* Local value for control */ 284 struct timespec ts; 285 cell_t *c = (cell_t *) arg; 286 287 /* Set the thread local data key value (used in the signal handler) */ 288 ret = pthread_setspecific(_c, arg); 289 if (ret != 0) { 290 UNRESOLVED(ret, "Unable to assign the thread-local-data key"); 291 } 292 293 /* Signal we're started */ 294 ret = pthread_mutex_lock(&(c->tmtx)); 295 if (ret != 0) { 296 UNRESOLVED(ret, "Failed to lock the mutex"); 297 } 298 c->tcnt += 1; 299 ret = pthread_mutex_unlock(&(c->tmtx)); 300 if (ret != 0) { 301 UNRESOLVED(ret, "Failed to unlock the mutex"); 302 } 303 304 do { 305 /* Lock, control, then unlock */ 306 do { 307 ret = clock_gettime(CLOCK_REALTIME, &ts); 308 if (ret != 0) { 309 UNRESOLVED(errno, 310 "Unable to get time for timeout"); 311 } 312 ts.tv_sec++; /* We will wait for 1 second */ 313 ret = pthread_mutex_timedlock(&(c->mtx), &ts); 314 } while (ret == ETIMEDOUT); 315 if (ret != 0) { 316 UNRESOLVED(ret, 317 "Timed mutex lock failed in worker thread"); 318 } 319 320 control(c, &loc); 321 322 ret = pthread_mutex_unlock(&(c->mtx)); 323 if (ret != 0) { 324 UNRESOLVED(ret, "Mutex unlock failed in worker thread"); 325 } 326 327 /* Increment the operation counter */ 328 c->opcnt++; 329 } 330 while (do_it); 331 332 /* Wait for the signal thread to terminate before we can exit */ 333 waitsigend(c); 334 return NULL; 335 } 336 337 void *trylockthr(void *arg) 338 { 339 int ret; 340 char loc; /* Local value for control */ 341 cell_t *c = (cell_t *) arg; 342 343 /* Set the thread local data key value (used in the signal handler) */ 344 ret = pthread_setspecific(_c, arg); 345 if (ret != 0) { 346 UNRESOLVED(ret, "Unable to assign the thread-local-data key"); 347 } 348 349 /* Signal we're started */ 350 ret = pthread_mutex_lock(&(c->tmtx)); 351 if (ret != 0) { 352 UNRESOLVED(ret, "Failed to lock the mutex"); 353 } 354 c->tcnt += 1; 355 ret = pthread_mutex_unlock(&(c->tmtx)); 356 if (ret != 0) { 357 UNRESOLVED(ret, "Failed to unlock the mutex"); 358 } 359 360 do { 361 /* Lock, control, then unlock */ 362 do { 363 ret = pthread_mutex_trylock(&(c->mtx)); 364 } while (ret == EBUSY); 365 if (ret != 0) { 366 UNRESOLVED(ret, 367 "Mutex lock try failed in worker thread"); 368 } 369 370 control(c, &loc); 371 372 ret = pthread_mutex_unlock(&(c->mtx)); 373 if (ret != 0) { 374 UNRESOLVED(ret, "Mutex unlock failed in worker thread"); 375 } 376 377 /* Increment the operation counter */ 378 c->opcnt++; 379 } 380 while (do_it); 381 382 /* Wait for the signal thread to terminate before we can exit */ 383 waitsigend(c); 384 return NULL; 385 } 386 387 /***** The next function initializes a cell_t object 388 * This includes running the threads */ 389 void cell_init(int id, cell_t * c, pthread_mutexattr_t * pma) 390 { 391 int ret, i; 392 pthread_attr_t pa; /* We will specify a minimal stack size */ 393 394 /* mark this group with its ID */ 395 c->id = id; 396 /* Initialize some other values */ 397 c->sigok = 0; 398 c->ctrl = 0; 399 c->sigcnt = 0; 400 c->opcnt = 0; 401 c->tcnt = 0; 402 403 /* Initialize the mutex */ 404 ret = pthread_mutex_init(&(c->tmtx), NULL); 405 if (ret != 0) { 406 UNRESOLVED(ret, "Mutex init failed"); 407 } 408 ret = pthread_mutex_init(&(c->mtx), pma); 409 if (ret != 0) { 410 UNRESOLVED(ret, "Mutex init failed"); 411 } 412 #if VERBOSE > 1 413 output("Mutex initialized in cell %i\n", id); 414 #endif 415 416 /* Initialize the semaphore */ 417 ret = sem_init(&(c->semsig), 0, 0); 418 if (ret != 0) { 419 UNRESOLVED(errno, "Sem init failed"); 420 } 421 #if VERBOSE > 1 422 output("Semaphore initialized in cell %i\n", id); 423 #endif 424 425 /* Create the thread attribute with the minimal size */ 426 ret = pthread_attr_init(&pa); 427 if (ret != 0) { 428 UNRESOLVED(ret, "Unable to create pthread attribute object"); 429 } 430 ret = pthread_attr_setstacksize(&pa, sysconf(_SC_THREAD_STACK_MIN)); 431 if (ret != 0) { 432 UNRESOLVED(ret, "Unable to specify minimal stack size"); 433 } 434 435 /* Create the signal thread */ 436 ret = pthread_create(&(c->threads[0]), &pa, sigthr, (void *)c); 437 if (ret != 0) { 438 UNRESOLVED(ret, "Unable to create the signal thread"); 439 } 440 441 /* Create 5 "lock" threads */ 442 for (i = 1; i <= 5; i++) { 443 ret = pthread_create(&(c->threads[i]), &pa, lockthr, (void *)c); 444 if (ret != 0) { 445 UNRESOLVED(ret, "Unable to create a locker thread"); 446 } 447 } 448 449 /* Create 2 "timedlock" threads */ 450 for (i = 6; i <= 7; i++) { 451 ret = 452 pthread_create(&(c->threads[i]), &pa, timedlockthr, 453 (void *)c); 454 if (ret != 0) { 455 UNRESOLVED(ret, 456 "Unable to create a (timed) locker thread"); 457 } 458 } 459 460 /* Create 2 "trylock" threads */ 461 for (i = 8; i <= 9; i++) { 462 ret = 463 pthread_create(&(c->threads[i]), &pa, trylockthr, 464 (void *)c); 465 if (ret != 0) { 466 UNRESOLVED(ret, 467 "Unable to create a (try) locker thread"); 468 } 469 } 470 471 #if VERBOSE > 1 472 output("All threads initialized in cell %i\n", id); 473 #endif 474 475 /* Destroy the thread attribute object */ 476 ret = pthread_attr_destroy(&pa); 477 if (ret != 0) { 478 UNRESOLVED(ret, "Unable to destroy thread attribute object"); 479 } 480 481 /* Tell the signal thread to start working */ 482 ret = sem_post(&(c->semsig)); 483 if (ret != 0) { 484 UNRESOLVED(ret, "Unable to post signal semaphore"); 485 } 486 } 487 488 /***** The next function destroys a cell_t object 489 * This includes stopping the threads */ 490 void cell_fini(int id, 491 cell_t * c, 492 unsigned long long *globalopcount, 493 unsigned long long *globalsigcount) 494 { 495 int ret, i; 496 497 /* Just a basic check */ 498 if (id != c->id) { 499 output("Something is wrong: Cell %i has id %i\n", id, c->id); 500 FAILED("Some memory has been corrupted"); 501 } 502 503 /* Start with joining the threads */ 504 for (i = 0; i < 10; i++) { 505 ret = pthread_join(c->threads[i], NULL); 506 if (ret != 0) { 507 UNRESOLVED(ret, "Unable to join a thread"); 508 } 509 } 510 511 /* Destroy the semaphore and the mutex */ 512 ret = sem_destroy(&(c->semsig)); 513 if (ret != 0) { 514 UNRESOLVED(errno, "Unable to destroy the semaphore"); 515 } 516 517 ret = pthread_mutex_destroy(&(c->mtx)); 518 if (ret != 0) { 519 output("Unable to destroy the mutex in cell %i (ret = %i)\n", 520 id, ret); 521 FAILED("Mutex destruction failed"); 522 } 523 524 /* Report the cell counters */ 525 *globalopcount += c->opcnt; 526 *globalsigcount += c->sigcnt; 527 #if VERBOSE > 1 528 output 529 ("Counters for cell %i:\n\t%llu locks and unlocks\n\t%llu signals\n", 530 id, c->opcnt, c->sigcnt); 531 #endif 532 533 /* We are done with this cell. */ 534 } 535 536 /**** Next function is called when the process is killed with SIGUSR1 537 * It tells every threads in every cells to stop their work. 538 */ 539 void globalsig(int sig) 540 { 541 output("Signal received, processing. Please wait...\n"); 542 do { 543 do_it = 0; 544 } 545 while (do_it); 546 } 547 548 /****** 549 * Last but not least, the main function 550 */ 551 int main(int argc, char *argv[]) 552 { 553 /* Main is responsible for : 554 * the mutex attributes initializing 555 * the creation of the cells 556 * the destruction of everything on SIGUSR1 reception 557 */ 558 559 int ret; 560 int i; 561 struct sigaction sa; 562 unsigned long long globopcnt = 0, globsigcnt = 0; 563 564 #ifndef WITHOUT_XOPEN 565 int sz = 2 + (sizeof(types) / sizeof(int)); 566 #else 567 int sz = 2; 568 #endif 569 pthread_mutexattr_t ma[sz - 1]; 570 pthread_mutexattr_t *pma[sz]; 571 572 cell_t data[sz * N * SCALABILITY_FACTOR]; 573 574 pma[sz - 1] = NULL; 575 576 #if VERBOSE > 0 577 output("Mutex lock / unlock stress sample is starting\n"); 578 output("Kill with SIGUSR1 to stop the process\n"); 579 output("\t kill -USR1 <pid>\n\n"); 580 #endif 581 582 /* Initialize the mutex attributes */ 583 for (i = 0; i < sz - 1; i++) { 584 pma[i] = &ma[i]; 585 ret = pthread_mutexattr_init(pma[i]); 586 if (ret != 0) { 587 UNRESOLVED(ret, 588 "Unable to init a mutex attribute object"); 589 } 590 #ifndef WITHOUT_XOPEN /* we have the mutex attribute types */ 591 if (i != 0) { 592 ret = pthread_mutexattr_settype(pma[i], types[i - 1]); 593 if (ret != 0) { 594 UNRESOLVED(ret, 595 "Unable to set type of a mutex attribute object"); 596 } 597 } 598 #endif 599 } 600 #if VERBOSE > 1 601 output("%i mutex attribute objects were initialized\n", sz - 1); 602 #endif 603 604 /* Initialize the thread-local-data key */ 605 ret = pthread_key_create(&_c, NULL); 606 if (ret != 0) { 607 UNRESOLVED(ret, "Unable to initialize TLD key"); 608 } 609 #if VERBOSE > 1 610 output("TLD key initialized\n"); 611 #endif 612 613 /* Register the signal handler for SIGUSR1 */ 614 sigemptyset(&sa.sa_mask); 615 sa.sa_flags = 0; 616 sa.sa_handler = globalsig; 617 if ((ret = sigaction(SIGUSR1, &sa, NULL))) { 618 UNRESOLVED(ret, "Unable to register signal handler"); 619 } 620 621 /* Register the signal handler for SIGUSR2 */ 622 sa.sa_handler = sighdl; 623 if ((ret = sigaction(SIGUSR2, &sa, NULL))) { 624 UNRESOLVED(ret, "Unable to register signal handler"); 625 } 626 627 /* Start every threads */ 628 #if VERBOSE > 0 629 output("%i cells of 10 threads are being created...\n", 630 sz * N * SCALABILITY_FACTOR); 631 #endif 632 for (i = 0; i < sz * N * SCALABILITY_FACTOR; i++) 633 cell_init(i, &data[i], pma[i % sz]); 634 #if VERBOSE > 0 635 output("All threads created and running.\n"); 636 #endif 637 638 /* We stay here while not interrupted */ 639 do { 640 sched_yield(); 641 } 642 while (do_it); 643 644 #if VERBOSE > 0 645 output("Starting to join the threads...\n"); 646 #endif 647 /* Everybody is stopping, we must join them, and destroy the cell data */ 648 for (i = 0; i < sz * N * SCALABILITY_FACTOR; i++) 649 cell_fini(i, &data[i], &globopcnt, &globsigcnt); 650 651 /* Destroy the mutex attributes objects */ 652 for (i = 0; i < sz - 1; i++) { 653 ret = pthread_mutexattr_destroy(pma[i]); 654 if (ret != 0) { 655 UNRESOLVED(ret, 656 "Unable to destroy a mutex attribute object"); 657 } 658 } 659 660 /* Destroy the thread-local-data key */ 661 ret = pthread_key_delete(_c); 662 if (ret != 0) { 663 UNRESOLVED(ret, "Unable to destroy TLD key"); 664 } 665 #if VERBOSE > 1 666 output("TLD key destroyed\n"); 667 #endif 668 669 /* output the total counters */ 670 #if VERBOSE > 1 671 output("===============================================\n"); 672 #endif 673 #if VERBOSE > 0 674 output("Total counters:\n\t%llu locks and unlocks\n\t%llu signals\n", 675 globopcnt, globsigcnt); 676 output("pthread_mutex_lock stress test passed.\n"); 677 #endif 678 679 PASSED; 680 } 681