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 * This file is a stress test for the function pthread_cond_timedwait. 18 * 19 * It aims to check the following assertion: 20 * When a cancel request unblocks the thread, 21 * it must not consume any pending condition signal request. 22 23 * The steps are: 24 * -> Create a bunch of threads waiting on a condvar. 25 * -> At the same time (using a barrier) one thread is canceled and the condition is signaled. 26 * -> Test checks that the cond signaling was not lost (at least one thread must have woken cleanly). 27 * -> Then everything is cleaned up and started again. 28 29 */ 30 31 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */ 32 #define _POSIX_C_SOURCE 200112L 33 34 /* We need the XSI extention for the mutex attributes */ 35 #ifndef WITHOUT_XOPEN 36 #define _XOPEN_SOURCE 600 37 #endif 38 /********************************************************************************************/ 39 /****************************** standard includes *****************************************/ 40 /********************************************************************************************/ 41 #include <pthread.h> 42 #include <stdarg.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <unistd.h> 46 47 #include <errno.h> 48 #include <signal.h> 49 #include <string.h> 50 #include <time.h> 51 52 /********************************************************************************************/ 53 /****************************** Test framework *****************************************/ 54 /********************************************************************************************/ 55 #include "testfrmw.h" 56 #include "testfrmw.c" 57 /* This header is responsible for defining the following macros: 58 * UNRESOLVED(ret, descr); 59 * where descr is a description of the error and ret is an int (error code for example) 60 * FAILED(descr); 61 * where descr is a short text saying why the test has failed. 62 * PASSED(); 63 * No parameter. 64 * 65 * Both three macros shall terminate the calling process. 66 * The testcase shall not terminate in any other maneer. 67 * 68 * The other file defines the functions 69 * void output_init() 70 * void output(char * string, ...) 71 * 72 * Those may be used to output information. 73 */ 74 75 /********************************************************************************************/ 76 /********************************** Configuration ******************************************/ 77 /********************************************************************************************/ 78 #ifndef SCALABILITY_FACTOR 79 #define SCALABILITY_FACTOR 1 80 #endif 81 #ifndef VERBOSE 82 #define VERBOSE 1 83 #endif 84 85 /* Size of the "bunch" of threads -- the real number will be 2 more threads per scenarii */ 86 #define NCHILDREN (20) 87 88 #define TIMEOUT (60) 89 90 #ifndef WITHOUT_ALTCLK 91 #define USE_ALTCLK /* make tests with MONOTONIC CLOCK if supported */ 92 #endif 93 94 /********************************************************************************************/ 95 /*********************************** Test case *****************************************/ 96 /********************************************************************************************/ 97 98 #ifdef WITHOUT_XOPEN 99 /* We define those to avoid compilation errors, but they won't be used */ 100 #define PTHREAD_MUTEX_DEFAULT 0 101 #define PTHREAD_MUTEX_NORMAL 0 102 #define PTHREAD_MUTEX_ERRORCHECK 0 103 #define PTHREAD_MUTEX_RECURSIVE 0 104 105 #endif 106 107 struct _scenar { 108 int m_type; /* Mutex type to use */ 109 int mc_pshared; /* 0: mutex and cond are process-private (default) ~ !0: Both are process-shared, if supported */ 110 int c_clock; /* 0: cond uses the default clock. ~ !0: Cond uses monotonic clock, if supported. */ 111 int fork; /* 0: Test between threads. ~ !0: Test across processes, if supported (mmap) */ 112 char *descr; /* Case description */ 113 } scenarii[] = { 114 { 115 PTHREAD_MUTEX_DEFAULT, 0, 0, 0, "Default mutex"} 116 , { 117 PTHREAD_MUTEX_NORMAL, 0, 0, 0, "Normal mutex"} 118 , { 119 PTHREAD_MUTEX_ERRORCHECK, 0, 0, 0, "Errorcheck mutex"} 120 , { 121 PTHREAD_MUTEX_RECURSIVE, 0, 0, 0, "Recursive mutex"} 122 123 , { 124 PTHREAD_MUTEX_DEFAULT, 1, 0, 0, "PShared default mutex"} 125 , { 126 PTHREAD_MUTEX_NORMAL, 1, 0, 0, "Pshared normal mutex"} 127 , { 128 PTHREAD_MUTEX_ERRORCHECK, 1, 0, 0, "Pshared errorcheck mutex"} 129 , { 130 PTHREAD_MUTEX_RECURSIVE, 1, 0, 0, "Pshared recursive mutex"} 131 132 , { 133 PTHREAD_MUTEX_DEFAULT, 1, 0, 1, 134 "Pshared default mutex across processes"} 135 , { 136 PTHREAD_MUTEX_NORMAL, 1, 0, 1, 137 "Pshared normal mutex across processes"} 138 , { 139 PTHREAD_MUTEX_ERRORCHECK, 1, 0, 1, 140 "Pshared errorcheck mutex across processes"} 141 , { 142 PTHREAD_MUTEX_RECURSIVE, 1, 0, 1, 143 "Pshared recursive mutex across processes"} 144 145 #ifdef USE_ALTCLK 146 , { 147 PTHREAD_MUTEX_DEFAULT, 1, 1, 1, 148 "Pshared default mutex and alt clock condvar across processes"} 149 , { 150 PTHREAD_MUTEX_NORMAL, 1, 1, 1, 151 "Pshared normal mutex and alt clock condvar across processes"} 152 , { 153 PTHREAD_MUTEX_ERRORCHECK, 1, 1, 1, 154 "Pshared errorcheck mutex and alt clock condvar across processes"} 155 , { 156 PTHREAD_MUTEX_RECURSIVE, 1, 1, 1, 157 "Pshared recursive mutex and alt clock condvar across processes"} 158 159 , { 160 PTHREAD_MUTEX_DEFAULT, 0, 1, 0, 161 "Default mutex and alt clock condvar"} 162 , { 163 PTHREAD_MUTEX_NORMAL, 0, 1, 0, 164 "Normal mutex and alt clock condvar"} 165 , { 166 PTHREAD_MUTEX_ERRORCHECK, 0, 1, 0, 167 "Errorcheck mutex and alt clock condvar"} 168 , { 169 PTHREAD_MUTEX_RECURSIVE, 0, 1, 0, 170 "Recursive mutex and alt clock condvar"} 171 172 , { 173 PTHREAD_MUTEX_DEFAULT, 1, 1, 0, 174 "PShared default mutex and alt clock condvar"} 175 , { 176 PTHREAD_MUTEX_NORMAL, 1, 1, 0, 177 "Pshared normal mutex and alt clock condvar"} 178 , { 179 PTHREAD_MUTEX_ERRORCHECK, 1, 1, 0, 180 "Pshared errorcheck mutex and alt clock condvar"} 181 , { 182 PTHREAD_MUTEX_RECURSIVE, 1, 1, 0, 183 "Pshared recursive mutex and alt clock condvar"} 184 #endif 185 }; 186 187 #define NSCENAR (sizeof(scenarii)/sizeof(scenarii[0])) 188 189 /* This is the shared structure for all threads related to the same condvar */ 190 struct celldata { 191 pthread_t workers[NCHILDREN * SCALABILITY_FACTOR + 2]; 192 pthread_t signaler; 193 194 pthread_barrier_t bar; 195 pthread_mutex_t mtx; 196 pthread_cond_t cnd; 197 clockid_t cid; 198 199 int boolean; 200 int count; 201 202 long canceled; 203 long cancelfailed; 204 long cnttotal; 205 } cells[NSCENAR * SCALABILITY_FACTOR]; 206 207 char do_it = 1; 208 pthread_attr_t ta; 209 210 void cleanup(void *arg) 211 { 212 int ret; 213 struct celldata *cd = (struct celldata *)arg; 214 215 /* Unlock the mutex */ 216 ret = pthread_mutex_unlock(&(cd->mtx)); 217 if (ret != 0) { 218 UNRESOLVED(ret, "Failed to unlock mutex in cancel handler"); 219 } 220 221 } 222 223 void *worker(void *arg) 224 { 225 int ret; 226 struct celldata *cd = (struct celldata *)arg; 227 struct timespec ts; 228 229 /* lock the mutex */ 230 ret = pthread_mutex_lock(&(cd->mtx)); 231 if (ret != 0) { 232 UNRESOLVED(ret, "Unable to lock mutex in worker"); 233 } 234 235 /* Tell the cellmaster we are ready (count++) */ 236 cd->count += 1; 237 238 /* Timeout = now + TIMEOUT */ 239 ret = clock_gettime(cd->cid, &ts); 240 if (ret != 0) { 241 UNRESOLVED(errno, "Gettime failed"); 242 } 243 ts.tv_sec += TIMEOUT * SCALABILITY_FACTOR; 244 245 /* register cleanup handler */ 246 pthread_cleanup_push(cleanup, arg); 247 248 do { 249 /* cond timedwait (while boolean == false) */ 250 ret = pthread_cond_timedwait(&(cd->cnd), &(cd->mtx), &ts); 251 252 /* if timeout => failed (lost signal) */ 253 if (ret == ETIMEDOUT) { 254 FAILED 255 ("Timeout occured. A condition signal was probably lost."); 256 } 257 258 if (ret != 0) { 259 UNRESOLVED(ret, "Cond timedwait failed"); 260 } 261 262 } while (cd->boolean == 0); 263 264 /* broadcast the condition */ 265 ret = pthread_cond_broadcast(&(cd->cnd)); 266 if (ret != 0) { 267 UNRESOLVED(ret, "Broadcasting the condition failed"); 268 } 269 270 /* unregister the cleanup */ 271 pthread_cleanup_pop(0); 272 273 /* unlock the mutex */ 274 ret = pthread_mutex_unlock(&(cd->mtx)); 275 if (ret != 0) { 276 UNRESOLVED(ret, "Unable to unlock the mutex"); 277 } 278 279 return NULL; 280 } 281 282 void *signaler(void *arg) 283 { 284 int ret; 285 struct celldata *cd = (struct celldata *)arg; 286 287 /* Lock the mutex if required */ 288 if (cd->boolean == -1) { 289 ret = pthread_mutex_lock(&(cd->mtx)); 290 if (ret != 0) { 291 UNRESOLVED(ret, "mutex lock failed in signaler"); 292 } 293 } 294 295 /* wait the barrier */ 296 ret = pthread_barrier_wait(&(cd->bar)); 297 if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) { 298 UNRESOLVED(ret, "Barrier wait failed"); 299 } 300 301 /* signal the cond */ 302 ret = pthread_cond_signal(&(cd->cnd)); 303 if (ret != 0) { 304 UNRESOLVED(ret, "Signaling the cond failed"); 305 } 306 307 /* Unlock the mutex if required */ 308 if (cd->boolean == -1) { 309 ret = pthread_mutex_unlock(&(cd->mtx)); 310 if (ret != 0) { 311 UNRESOLVED(ret, "mutex unlock failed in signaler"); 312 } 313 } 314 315 return NULL; 316 } 317 318 void *cellmanager(void *arg) 319 { 320 int ret, i; 321 struct celldata *cd = (struct celldata *)arg; 322 struct timespec ts; 323 int randval; 324 void *w_ret; 325 326 cd->canceled = 0; 327 cd->cancelfailed = 0; 328 cd->cnttotal = 0; 329 330 /* while do_it */ 331 while (do_it) { 332 /* Initialize some stuff */ 333 cd->boolean = 0; 334 cd->count = 0; 335 cd->cnttotal += 1; 336 337 /* create the workers */ 338 for (i = 0; i < NCHILDREN * SCALABILITY_FACTOR + 2; i++) { 339 ret = 340 pthread_create(&(cd->workers[i]), &ta, worker, arg); 341 if (ret != 0) { 342 UNRESOLVED(ret, 343 "Unable to create enough threads"); 344 } 345 } 346 347 /* choose a (pseudo) random thread to cancel */ 348 ret = clock_gettime(cd->cid, &ts); 349 if (ret != 0) { 350 UNRESOLVED(errno, "Failed to read clock"); 351 } 352 randval = 353 (ts.tv_sec + 354 (ts.tv_nsec >> 10)) % (NCHILDREN * SCALABILITY_FACTOR + 2); 355 356 /* wait for the workers to be ready */ 357 do { 358 ret = pthread_mutex_lock(&(cd->mtx)); 359 if (ret != 0) { 360 UNRESOLVED(ret, "Mutex lock failed"); 361 } 362 363 i = cd->count; 364 365 ret = pthread_mutex_unlock(&(cd->mtx)); 366 if (ret != 0) { 367 UNRESOLVED(ret, "Mutex unlock failed"); 368 } 369 } while (i < NCHILDREN * SCALABILITY_FACTOR + 2); 370 371 /* Set the boolean (1 => no lock in signaler; -1 => lock) */ 372 cd->boolean = (ts.tv_sec & 1) ? -1 : 1; 373 374 /* create the signaler */ 375 ret = pthread_create(&(cd->signaler), &ta, signaler, arg); 376 if (ret != 0) { 377 UNRESOLVED(ret, "Failed to create signaler thread"); 378 } 379 380 /* wait the barrier */ 381 ret = pthread_barrier_wait(&(cd->bar)); 382 if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) { 383 UNRESOLVED(ret, "Failed to wait for the barrier"); 384 } 385 386 /* cancel the chosen thread */ 387 ret = pthread_cancel(cd->workers[randval]); 388 389 /* it is possible the thread is already terminated -- so we don't stop on error */ 390 if (ret != 0) { 391 #if VERBOSE > 2 392 output("%d\n", randval); 393 #endif 394 cd->cancelfailed += 1; 395 } 396 397 /* join every threads */ 398 ret = pthread_join(cd->signaler, NULL); 399 if (ret != 0) { 400 UNRESOLVED(ret, "Failed to join the signaler thread"); 401 } 402 403 for (i = 0; i < NCHILDREN * SCALABILITY_FACTOR + 2; i++) { 404 ret = pthread_join(cd->workers[i], &w_ret); 405 if (ret != 0) { 406 UNRESOLVED(ret, "Unable to join a worker"); 407 } 408 if (w_ret == PTHREAD_CANCELED) 409 cd->canceled += 1; 410 } 411 } 412 413 return NULL; 414 } 415 416 void sighdl(int sig) 417 { 418 /* do_it = 0 */ 419 do { 420 do_it = 0; 421 } 422 while (do_it); 423 } 424 425 int main(int argc, char *argv[]) 426 { 427 int ret, i, j; 428 struct sigaction sa; 429 430 pthread_mutexattr_t ma; 431 pthread_condattr_t ca; 432 clockid_t cid = CLOCK_REALTIME; 433 long canceled = 0; 434 long cancelfailed = 0; 435 long cnttotal = 0; 436 437 long pshared, monotonic, cs; 438 439 pthread_t mngrs[NSCENAR * SCALABILITY_FACTOR]; 440 441 output_init(); 442 443 /* check the system abilities */ 444 pshared = sysconf(_SC_THREAD_PROCESS_SHARED); 445 cs = sysconf(_SC_CLOCK_SELECTION); 446 monotonic = sysconf(_SC_MONOTONIC_CLOCK); 447 448 #if VERBOSE > 0 449 output("Test starting\n"); 450 output("System abilities:\n"); 451 output(" TPS : %li\n", pshared); 452 output(" CS : %li\n", cs); 453 output(" MON : %li\n", monotonic); 454 if ((cs < 0) || (monotonic < 0)) 455 output("Alternative clock won't be tested\n"); 456 #endif 457 458 if (monotonic < 0) 459 cs = -1; 460 461 #ifndef USE_ALTCLK 462 if (cs > 0) 463 output 464 ("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n"); 465 #endif 466 467 /* Initialize the celldatas according to scenarii */ 468 for (i = 0; i < NSCENAR; i++) { 469 #if VERBOSE > 1 470 output("[parent] Preparing attributes for: %s\n", 471 scenarii[i].descr); 472 #ifdef WITHOUT_XOPEN 473 output("[parent] Mutex attributes DISABLED -> not used\n"); 474 #endif 475 #endif 476 477 /* set / reset everything */ 478 ret = pthread_mutexattr_init(&ma); 479 if (ret != 0) { 480 UNRESOLVED(ret, 481 "[parent] Unable to initialize the mutex attribute object"); 482 } 483 ret = pthread_condattr_init(&ca); 484 if (ret != 0) { 485 UNRESOLVED(ret, 486 "[parent] Unable to initialize the cond attribute object"); 487 } 488 #ifndef WITHOUT_XOPEN 489 /* Set the mutex type */ 490 ret = pthread_mutexattr_settype(&ma, scenarii[i].m_type); 491 if (ret != 0) { 492 UNRESOLVED(ret, "[parent] Unable to set mutex type"); 493 } 494 #if VERBOSE > 1 495 output("[parent] Mutex type : %i\n", scenarii[i].m_type); 496 #endif 497 #endif 498 499 /* Set the pshared attributes, if supported */ 500 if ((pshared > 0) && (scenarii[i].mc_pshared != 0)) { 501 ret = 502 pthread_mutexattr_setpshared(&ma, 503 PTHREAD_PROCESS_SHARED); 504 if (ret != 0) { 505 UNRESOLVED(ret, 506 "[parent] Unable to set the mutex process-shared"); 507 } 508 ret = 509 pthread_condattr_setpshared(&ca, 510 PTHREAD_PROCESS_SHARED); 511 if (ret != 0) { 512 UNRESOLVED(ret, 513 "[parent] Unable to set the cond var process-shared"); 514 } 515 #if VERBOSE > 1 516 output("[parent] Mutex & cond are process-shared\n"); 517 #endif 518 } 519 #if VERBOSE > 1 520 else { 521 output("[parent] Mutex & cond are process-private\n"); 522 } 523 #endif 524 525 /* Set the alternative clock, if supported */ 526 #ifdef USE_ALTCLK 527 if ((cs > 0) && (scenarii[i].c_clock != 0)) { 528 ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC); 529 if (ret != 0) { 530 UNRESOLVED(ret, 531 "[parent] Unable to set the monotonic clock for the cond"); 532 } 533 #if VERBOSE > 1 534 output("[parent] Cond uses the Monotonic clock\n"); 535 #endif 536 } 537 #if VERBOSE > 1 538 else { 539 output("[parent] Cond uses the default clock\n"); 540 } 541 #endif 542 ret = pthread_condattr_getclock(&ca, &cid); 543 if (ret != 0) { 544 UNRESOLVED(ret, "Unable to get clock from cond attr"); 545 } 546 #endif 547 548 /* Initialize all the mutex and condvars which uses those attributes */ 549 for (j = 0; j < SCALABILITY_FACTOR; j++) { 550 cells[i + j * NSCENAR].cid = cid; 551 552 /* initialize the condvar */ 553 ret = 554 pthread_cond_init(&(cells[i + j * NSCENAR].cnd), 555 &ca); 556 if (ret != 0) { 557 UNRESOLVED(ret, "Cond init failed"); 558 } 559 560 /* initialize the mutex */ 561 ret = 562 pthread_mutex_init(&(cells[i + j * NSCENAR].mtx), 563 &ma); 564 if (ret != 0) { 565 UNRESOLVED(ret, "Mutex init failed"); 566 } 567 568 /* initialize the barrier */ 569 ret = 570 pthread_barrier_init(&(cells[i + j * NSCENAR].bar), 571 NULL, 2); 572 if (ret != 0) { 573 UNRESOLVED(ret, "Failed to init barrier"); 574 } 575 } 576 577 ret = pthread_condattr_destroy(&ca); 578 if (ret != 0) { 579 UNRESOLVED(ret, 580 "Failed to destroy the cond var attribute object"); 581 } 582 583 ret = pthread_mutexattr_destroy(&ma); 584 if (ret != 0) { 585 UNRESOLVED(ret, 586 "Failed to destroy the mutex attribute object"); 587 } 588 } 589 #if VERBOSE > 1 590 output("[parent] All condvars & mutex are ready\n"); 591 #endif 592 593 /* register the signal handler */ 594 sigemptyset(&sa.sa_mask); 595 sa.sa_flags = 0; 596 sa.sa_handler = sighdl; 597 if ((ret = sigaction(SIGUSR1, &sa, NULL))) { 598 UNRESOLVED(ret, "Unable to register signal handler"); 599 } 600 #if VERBOSE > 1 601 output("[parent] Signal handler registered\n"); 602 #endif 603 604 /* Initialize the thread attribute object */ 605 ret = pthread_attr_init(&ta); 606 if (ret != 0) { 607 UNRESOLVED(ret, 608 "[parent] Failed to initialize a thread attribute object"); 609 } 610 ret = pthread_attr_setstacksize(&ta, sysconf(_SC_THREAD_STACK_MIN)); 611 if (ret != 0) { 612 UNRESOLVED(ret, "[parent] Failed to set thread stack size"); 613 } 614 615 /* create the NSCENAR * SCALABILITY_FACTOR manager threads */ 616 for (i = 0; i < NSCENAR * SCALABILITY_FACTOR; i++) { 617 ret = pthread_create(&mngrs[i], &ta, cellmanager, &(cells[i])); 618 /* In case of failure we can exit; the child process will die after a while */ 619 if (ret != 0) { 620 UNRESOLVED(ret, "[Parent] Failed to create a thread"); 621 } 622 #if VERBOSE > 1 623 if ((i % 4) == 0) 624 output("[parent] %i manager threads created...\n", 625 i + 1); 626 #endif 627 } 628 629 #if VERBOSE > 1 630 output("[parent] All %i manager threads are running...\n", 631 NSCENAR * SCALABILITY_FACTOR); 632 #endif 633 634 /* join the manager threads and destroy the cells */ 635 for (i = 0; i < NSCENAR * SCALABILITY_FACTOR; i++) { 636 ret = pthread_join(mngrs[i], NULL); 637 if (ret != 0) { 638 UNRESOLVED(ret, "[Parent] Failed to join a thread"); 639 } 640 641 canceled += cells[i].canceled; 642 cancelfailed += cells[i].cancelfailed; 643 cnttotal += cells[i].cnttotal; 644 645 ret = pthread_barrier_destroy(&(cells[i].bar)); 646 if (ret != 0) { 647 UNRESOLVED(ret, "Failed to destroy a barrier"); 648 } 649 650 ret = pthread_cond_destroy(&(cells[i].cnd)); 651 if (ret != 0) { 652 UNRESOLVED(ret, "Failed to destroy a cond"); 653 } 654 655 ret = pthread_mutex_destroy(&(cells[i].mtx)); 656 if (ret != 0) { 657 UNRESOLVED(ret, "Failed to destroy a mutex"); 658 } 659 } 660 661 /* exit */ 662 #if VERBOSE > 0 663 output("Test passed\n"); 664 output(" Total loops : %8li\n", cnttotal); 665 #endif 666 #if VERBOSE > 1 667 output(" Failed cancel request: %8li\n", cancelfailed); 668 output(" Canceled threads : %8li\n", canceled); 669 #endif 670 671 PASSED; 672 } 673