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 inside the function, the thread releases the mutex 21 * before waiting for the conditionnal variable. 22 * Those two operations are atomic in the mean that 23 * no other thread can gain access to the mutex 24 * then signal (or broadcast) the condition 25 * without the blocked thread behaving as if 26 * this signal (or broadcast) had happened 27 * after it blocked on the conditionnal variable. 28 29 * The steps are: 30 * -> Create N mutex & N cond vars with different attributes 31 * -> Create N threads A, which 32 * -> locks the mutex 33 * -> create a thread B, which 34 * -> locks the mutex 35 * -> while the boolean is false, 36 * -> broadcasts the condvar 37 * -> timedwaits the condition for 10 seconds 38 * -> broadcasts the condvar 39 * -> unlock the mutex 40 * -> while the boolean is false, 41 * -> timedwaits the condvar for 10 seconds 42 * -> signals the condvar 43 * -> unlock the mutex 44 * -> joins the thread B 45 * -> sets the boolean True when it receives SIGUSR1 46 * -> joins the N threads A. 47 * 48 * the test fails when a broadcast returns with a timeout. 49 * 50 * To test for pshared primitive, thread B could be in another process. 51 */ 52 53 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */ 54 #define _POSIX_C_SOURCE 200112L 55 56 /* We need the XSI extention for the mutex attributes 57 and the mkstemp() routine */ 58 #ifndef WITHOUT_XOPEN 59 #define _XOPEN_SOURCE 600 60 #endif 61 /********************************************************************************************/ 62 /****************************** standard includes *****************************************/ 63 /********************************************************************************************/ 64 #include <pthread.h> 65 #include <stdarg.h> 66 #include <stdio.h> 67 #include <stdlib.h> 68 #include <unistd.h> 69 70 #include <errno.h> 71 #include <signal.h> 72 #include <sys/wait.h> 73 #include <sys/mman.h> 74 #include <string.h> 75 #include <time.h> 76 77 /********************************************************************************************/ 78 /****************************** Test framework *****************************************/ 79 /********************************************************************************************/ 80 #include "testfrmw.h" 81 #include "testfrmw.c" 82 /* This header is responsible for defining the following macros: 83 * UNRESOLVED(ret, descr); 84 * where descr is a description of the error and ret is an int (error code for example) 85 * FAILED(descr); 86 * where descr is a short text saying why the test has failed. 87 * PASSED(); 88 * No parameter. 89 * 90 * Both three macros shall terminate the calling process. 91 * The testcase shall not terminate in any other maneer. 92 * 93 * The other file defines the functions 94 * void output_init() 95 * void output(char * string, ...) 96 * 97 * Those may be used to output information. 98 */ 99 100 /********************************************************************************************/ 101 /********************************** Configuration ******************************************/ 102 /********************************************************************************************/ 103 #ifndef SCALABILITY_FACTOR 104 #define SCALABILITY_FACTOR 1 105 #endif 106 #ifndef VERBOSE 107 #define VERBOSE 1 108 #endif 109 110 /* Number of children for each test scenario */ 111 #define NCHILDREN (5) 112 113 #define TIMEOUT 120 114 115 #ifndef WITHOUT_ALTCLK 116 #define USE_ALTCLK /* make tests with MONOTONIC CLOCK if supported */ 117 #endif 118 119 /********************************************************************************************/ 120 /*********************************** Test case *****************************************/ 121 /********************************************************************************************/ 122 123 #ifdef WITHOUT_XOPEN 124 /* We define those to avoid compilation errors, but they won't be used */ 125 #define PTHREAD_MUTEX_DEFAULT 0 126 #define PTHREAD_MUTEX_NORMAL 0 127 #define PTHREAD_MUTEX_ERRORCHECK 0 128 #define PTHREAD_MUTEX_RECURSIVE 0 129 130 #endif 131 132 struct _scenar { 133 int m_type; /* Mutex type to use */ 134 int mc_pshared; /* 0: mutex and cond are process-private (default) ~ !0: Both are process-shared, if supported */ 135 int c_clock; /* 0: cond uses the default clock. ~ !0: Cond uses monotonic clock, if supported. */ 136 int fork; /* 0: Test between threads. ~ !0: Test across processes, if supported (mmap) */ 137 char *descr; /* Case description */ 138 } scenarii[] = { 139 { 140 PTHREAD_MUTEX_DEFAULT, 0, 0, 0, "Default mutex"} 141 , { 142 PTHREAD_MUTEX_NORMAL, 0, 0, 0, "Normal mutex"} 143 , { 144 PTHREAD_MUTEX_ERRORCHECK, 0, 0, 0, "Errorcheck mutex"} 145 , { 146 PTHREAD_MUTEX_RECURSIVE, 0, 0, 0, "Recursive mutex"} 147 148 , { 149 PTHREAD_MUTEX_DEFAULT, 1, 0, 0, "PShared default mutex"} 150 , { 151 PTHREAD_MUTEX_NORMAL, 1, 0, 0, "Pshared normal mutex"} 152 , { 153 PTHREAD_MUTEX_ERRORCHECK, 1, 0, 0, "Pshared errorcheck mutex"} 154 , { 155 PTHREAD_MUTEX_RECURSIVE, 1, 0, 0, "Pshared recursive mutex"} 156 157 , { 158 PTHREAD_MUTEX_DEFAULT, 1, 0, 1, 159 "Pshared default mutex across processes"} 160 , { 161 PTHREAD_MUTEX_NORMAL, 1, 0, 1, 162 "Pshared normal mutex across processes"} 163 , { 164 PTHREAD_MUTEX_ERRORCHECK, 1, 0, 1, 165 "Pshared errorcheck mutex across processes"} 166 , { 167 PTHREAD_MUTEX_RECURSIVE, 1, 0, 1, 168 "Pshared recursive mutex across processes"} 169 170 #ifdef USE_ALTCLK 171 , { 172 PTHREAD_MUTEX_DEFAULT, 1, 1, 1, 173 "Pshared default mutex and alt clock condvar across processes"} 174 , { 175 PTHREAD_MUTEX_NORMAL, 1, 1, 1, 176 "Pshared normal mutex and alt clock condvar across processes"} 177 , { 178 PTHREAD_MUTEX_ERRORCHECK, 1, 1, 1, 179 "Pshared errorcheck mutex and alt clock condvar across processes"} 180 , { 181 PTHREAD_MUTEX_RECURSIVE, 1, 1, 1, 182 "Pshared recursive mutex and alt clock condvar across processes"} 183 184 , { 185 PTHREAD_MUTEX_DEFAULT, 0, 1, 0, 186 "Default mutex and alt clock condvar"} 187 , { 188 PTHREAD_MUTEX_NORMAL, 0, 1, 0, 189 "Normal mutex and alt clock condvar"} 190 , { 191 PTHREAD_MUTEX_ERRORCHECK, 0, 1, 0, 192 "Errorcheck mutex and alt clock condvar"} 193 , { 194 PTHREAD_MUTEX_RECURSIVE, 0, 1, 0, 195 "Recursive mutex and alt clock condvar"} 196 197 , { 198 PTHREAD_MUTEX_DEFAULT, 1, 1, 0, 199 "PShared default mutex and alt clock condvar"} 200 , { 201 PTHREAD_MUTEX_NORMAL, 1, 1, 0, 202 "Pshared normal mutex and alt clock condvar"} 203 , { 204 PTHREAD_MUTEX_ERRORCHECK, 1, 1, 0, 205 "Pshared errorcheck mutex and alt clock condvar"} 206 , { 207 PTHREAD_MUTEX_RECURSIVE, 1, 1, 0, 208 "Pshared recursive mutex and alt clock condvar"} 209 #endif 210 }; 211 212 #define NSCENAR (sizeof(scenarii)/sizeof(scenarii[0])) 213 214 #define NTOT (NSCENAR * SCALABILITY_FACTOR * NCHILDREN) 215 216 struct childdata { 217 pthread_mutex_t mtx; 218 pthread_cond_t cnd; 219 clockid_t cid; 220 int fork; 221 int *pBool; 222 }; 223 224 typedef struct { 225 struct childdata cd[NTOT]; 226 int boolean; 227 } testdata_t; 228 229 pthread_attr_t ta; 230 231 /*** 232 * The grand child function (either sub-thread or sub-process) 233 */ 234 void *threaded_B(void *arg) 235 { 236 int ret; 237 struct timespec ts; 238 struct childdata *cd = (struct childdata *)arg; 239 240 ret = pthread_mutex_lock(&(cd->mtx)); 241 if (ret != 0) { 242 UNRESOLVED(ret, "[gchild] Unable to lock mutex"); 243 } 244 245 while (*(cd->pBool) == 0) { 246 ret = pthread_cond_broadcast(&(cd->cnd)); 247 if (ret != 0) { 248 UNRESOLVED(ret, "[gchild] Broadcast failed"); 249 } 250 251 ret = clock_gettime(cd->cid, &ts); 252 if (ret != 0) { 253 UNRESOLVED(errno, "[gchild] Unable to read clock"); 254 } 255 256 ts.tv_sec += TIMEOUT; 257 258 ret = pthread_cond_timedwait(&(cd->cnd), &(cd->mtx), &ts); 259 if (ret == ETIMEDOUT) { 260 FAILED 261 ("[gchild] Timeout occured. This means a cond signal was lost -- or parent died"); 262 } 263 if (ret != 0) { 264 UNRESOLVED(ret, "[gchild] Failed to wait the cond"); 265 } 266 } 267 268 /* We shall broadcast again to be sure the parent is not hung */ 269 ret = pthread_cond_broadcast(&(cd->cnd)); 270 if (ret != 0) { 271 UNRESOLVED(ret, "[gchild] Broadcast failed"); 272 } 273 274 ret = pthread_mutex_unlock(&(cd->mtx)); 275 if (ret != 0) { 276 UNRESOLVED(ret, "[gchild] Failed to finally release the mutex"); 277 } 278 279 return NULL; 280 } 281 282 /*** 283 * The child function (always in the main thread) 284 */ 285 void *threaded_A(void *arg) 286 { 287 struct childdata *cd = (struct childdata *)arg; 288 int ret, status; 289 pid_t child_p = 0, wrc; 290 pthread_t child_t; 291 292 struct timespec ts; 293 294 ret = pthread_mutex_lock(&(cd->mtx)); 295 if (ret != 0) { 296 UNRESOLVED(ret, "[child] Unable to lock mutex"); 297 } 298 299 /* Create the grand child */ 300 if (cd->fork == 0) { 301 ret = pthread_create(&child_t, &ta, threaded_B, arg); 302 if (ret != 0) { 303 UNRESOLVED(ret, 304 "[child] Failed to create a grand child thread"); 305 } 306 } else { 307 child_p = fork(); 308 if (child_p == -1) { 309 UNRESOLVED(ret, 310 "[child] Failed to create a grand child proces"); 311 } 312 313 if (child_p == 0) { /* grand child */ 314 threaded_B(arg); 315 exit(0); 316 } 317 } 318 319 while (*(cd->pBool) == 0) { 320 ret = clock_gettime(cd->cid, &ts); 321 if (ret != 0) { 322 UNRESOLVED(errno, "[child] Unable to read clock"); 323 } 324 325 ts.tv_sec += TIMEOUT; 326 327 ret = pthread_cond_timedwait(&(cd->cnd), &(cd->mtx), &ts); 328 if (ret == ETIMEDOUT) { 329 FAILED 330 ("[child] Timeout occured. This means a cond broadcast was lost -- or gchild died"); 331 } 332 if (ret != 0) { 333 UNRESOLVED(ret, "[child] Failed to wait the cond"); 334 } 335 336 ret = pthread_cond_signal(&(cd->cnd)); 337 if (ret != 0) { 338 UNRESOLVED(ret, "[child] Signal failed"); 339 } 340 } 341 342 ret = pthread_mutex_unlock(&(cd->mtx)); 343 if (ret != 0) { 344 UNRESOLVED(ret, "[gchild] Failed to finally release the mutex"); 345 } 346 347 /* Wait for the grand child termination */ 348 if (cd->fork == 0) { 349 ret = pthread_join(child_t, NULL); 350 if (ret != 0) { 351 UNRESOLVED(ret, 352 "[child] Failed to join a grand child thread"); 353 } 354 } else { 355 wrc = waitpid(child_p, &status, 0); 356 if (wrc != child_p) { 357 output("Expected pid: %i. Got %i\n", (int)child_p, 358 (int)wrc); 359 UNRESOLVED(errno, "Waitpid failed"); 360 } 361 362 if (WIFSIGNALED(status)) { 363 output("Child process killed with signal %d\n", 364 WTERMSIG(status)); 365 UNRESOLVED(0, "Child process was killed"); 366 } 367 368 if (WIFEXITED(status)) { 369 ret = WEXITSTATUS(status); 370 } else { 371 UNRESOLVED(0, 372 "Child process was neither killed nor exited"); 373 } 374 } 375 376 /* the end */ 377 return NULL; 378 } 379 380 int *pBoolean = NULL; 381 382 /*** 383 * Signal handler 384 */ 385 void sighdl(int sig) 386 { 387 #if VERBOSE > 1 388 output("Received the USR1 signal; stopping everything\n"); 389 #endif 390 *pBoolean = 1; 391 } 392 393 int main(int argc, char *argv[]) 394 { 395 int ret, i, j; 396 struct sigaction sa; 397 398 pthread_mutexattr_t ma; 399 pthread_condattr_t ca; 400 clockid_t cid = CLOCK_REALTIME; 401 402 testdata_t *td; 403 testdata_t alternativ; 404 405 int do_fork; 406 long pshared, monotonic, cs, mf; 407 408 pthread_t th[NTOT]; 409 410 output_init(); 411 412 pshared = sysconf(_SC_THREAD_PROCESS_SHARED); 413 cs = sysconf(_SC_CLOCK_SELECTION); 414 monotonic = sysconf(_SC_MONOTONIC_CLOCK); 415 mf = sysconf(_SC_MAPPED_FILES); 416 417 #if VERBOSE > 0 418 output("Test starting\n"); 419 output("System abilities:\n"); 420 output(" TPS : %li\n", pshared); 421 output(" CS : %li\n", cs); 422 output(" MON : %li\n", monotonic); 423 output(" MF : %li\n", mf); 424 if ((mf < 0) || (pshared < 0)) 425 output("Process-shared attributes won't be tested\n"); 426 if ((cs < 0) || (monotonic < 0)) 427 output("Alternative clock won't be tested\n"); 428 #endif 429 430 /* We are not interested in testing the clock if we have no other clock available.. */ 431 if (monotonic < 0) 432 cs = -1; 433 434 #ifndef USE_ALTCLK 435 if (cs > 0) 436 output 437 ("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n"); 438 #endif 439 440 /********** 441 * Allocate space for the testdata structure 442 */ 443 if (mf < 0) { 444 /* Cannot mmap a file, we use an alternative method */ 445 td = &alternativ; 446 pshared = -1; /* We won't do this testing anyway */ 447 #if VERBOSE > 0 448 output("Testdata allocated in the process memory.\n"); 449 #endif 450 } else { 451 /* We will place the test data in a mmaped file */ 452 char filename[] = "/tmp/cond_timedwait_st1-XXXXXX"; 453 size_t sz, ps; 454 void *mmaped; 455 int fd; 456 char *tmp; 457 458 /* We now create the temp files */ 459 fd = mkstemp(filename); 460 if (fd == -1) { 461 UNRESOLVED(errno, 462 "Temporary file could not be created"); 463 } 464 465 /* and make sure the file will be deleted when closed */ 466 unlink(filename); 467 468 #if VERBOSE > 1 469 output("Temp file created (%s).\n", filename); 470 #endif 471 472 ps = (size_t) sysconf(_SC_PAGESIZE); 473 sz = ((sizeof(testdata_t) / ps) + 1) * ps; /* # pages needed to store the testdata */ 474 475 tmp = calloc(1, sz); 476 if (tmp == NULL) { 477 UNRESOLVED(errno, "Memory allocation failed"); 478 } 479 480 /* Write the data to the file. */ 481 if (write(fd, tmp, sz) != (ssize_t) sz) { 482 UNRESOLVED(sz, "Writting to the file failed"); 483 } 484 485 free(tmp); 486 487 /* Now we can map the file in memory */ 488 mmaped = 489 mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 490 if (mmaped == MAP_FAILED) { 491 UNRESOLVED(errno, "mmap failed"); 492 } 493 494 td = (testdata_t *) mmaped; 495 496 /* Our datatest structure is now in shared memory */ 497 #if VERBOSE > 1 498 output("Testdata allocated in shared memory (%ib).\n", 499 sizeof(testdata_t)); 500 #endif 501 } 502 503 /* Init the signal handler variable */ 504 pBoolean = &(td->boolean); 505 506 /* Init the structure */ 507 for (i = 0; i < NSCENAR; i++) { 508 #if VERBOSE > 1 509 output("[parent] Preparing attributes for: %s\n", 510 scenarii[i].descr); 511 #ifdef WITHOUT_XOPEN 512 output("[parent] Mutex attributes DISABLED -> not used\n"); 513 #endif 514 #endif 515 /* set / reset everything */ 516 do_fork = 0; 517 ret = pthread_mutexattr_init(&ma); 518 if (ret != 0) { 519 UNRESOLVED(ret, 520 "[parent] Unable to initialize the mutex attribute object"); 521 } 522 ret = pthread_condattr_init(&ca); 523 if (ret != 0) { 524 UNRESOLVED(ret, 525 "[parent] Unable to initialize the cond attribute object"); 526 } 527 #ifndef WITHOUT_XOPEN 528 /* Set the mutex type */ 529 ret = pthread_mutexattr_settype(&ma, scenarii[i].m_type); 530 if (ret != 0) { 531 UNRESOLVED(ret, "[parent] Unable to set mutex type"); 532 } 533 #if VERBOSE > 1 534 output("[parent] Mutex type : %i\n", scenarii[i].m_type); 535 #endif 536 #endif 537 538 /* Set the pshared attributes, if supported */ 539 if ((pshared > 0) && (scenarii[i].mc_pshared != 0)) { 540 ret = 541 pthread_mutexattr_setpshared(&ma, 542 PTHREAD_PROCESS_SHARED); 543 if (ret != 0) { 544 UNRESOLVED(ret, 545 "[parent] Unable to set the mutex process-shared"); 546 } 547 ret = 548 pthread_condattr_setpshared(&ca, 549 PTHREAD_PROCESS_SHARED); 550 if (ret != 0) { 551 UNRESOLVED(ret, 552 "[parent] Unable to set the cond var process-shared"); 553 } 554 #if VERBOSE > 1 555 output("[parent] Mutex & cond are process-shared\n"); 556 #endif 557 } 558 #if VERBOSE > 1 559 else { 560 output("[parent] Mutex & cond are process-private\n"); 561 } 562 #endif 563 564 /* Set the alternative clock, if supported */ 565 #ifdef USE_ALTCLK 566 if ((cs > 0) && (scenarii[i].c_clock != 0)) { 567 ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC); 568 if (ret != 0) { 569 UNRESOLVED(ret, 570 "[parent] Unable to set the monotonic clock for the cond"); 571 } 572 #if VERBOSE > 1 573 output("[parent] Cond uses the Monotonic clock\n"); 574 #endif 575 } 576 #if VERBOSE > 1 577 else { 578 output("[parent] Cond uses the default clock\n"); 579 } 580 #endif 581 ret = pthread_condattr_getclock(&ca, &cid); 582 if (ret != 0) { 583 UNRESOLVED(ret, "Unable to get clock from cond attr"); 584 } 585 #endif 586 587 /* Tell whether the test will be across processes */ 588 if ((pshared > 0) && (scenarii[i].fork != 0)) { 589 do_fork = 1; 590 #if VERBOSE > 1 591 output("[parent] Child will be a new process\n"); 592 #endif 593 } 594 #if VERBOSE > 1 595 else { 596 output("[parent] Child will be a new thread\n"); 597 } 598 #endif 599 600 /* Initialize all the mutex and condvars which uses those attributes */ 601 for (j = 0; j < SCALABILITY_FACTOR * NCHILDREN; j++) { 602 #define CD (td->cd[i+(j*NSCENAR)]) 603 CD.pBool = &(td->boolean); 604 CD.fork = do_fork; 605 CD.cid = cid; 606 607 /* initialize the condvar */ 608 ret = pthread_cond_init(&(CD.cnd), &ca); 609 if (ret != 0) { 610 UNRESOLVED(ret, "[parent] Cond init failed"); 611 } 612 613 /* initialize the mutex */ 614 ret = pthread_mutex_init(&(CD.mtx), &ma); 615 if (ret != 0) { 616 UNRESOLVED(ret, "[parent] Mutex init failed"); 617 } 618 #undef CD 619 } 620 621 ret = pthread_condattr_destroy(&ca); 622 if (ret != 0) { 623 UNRESOLVED(ret, 624 "Failed to destroy the cond var attribute object"); 625 } 626 627 ret = pthread_mutexattr_destroy(&ma); 628 if (ret != 0) { 629 UNRESOLVED(ret, 630 "Failed to destroy the mutex attribute object"); 631 } 632 } 633 #if VERBOSE > 1 634 output("[parent] All condvars & mutex are ready\n"); 635 #endif 636 637 ret = pthread_attr_init(&ta); 638 if (ret != 0) { 639 UNRESOLVED(ret, 640 "[parent] Failed to initialize a thread attribute object"); 641 } 642 ret = pthread_attr_setstacksize(&ta, sysconf(_SC_THREAD_STACK_MIN)); 643 if (ret != 0) { 644 UNRESOLVED(ret, "[parent] Failed to set thread stack size"); 645 } 646 647 sigemptyset(&sa.sa_mask); 648 sa.sa_flags = 0; 649 sa.sa_handler = sighdl; 650 if ((ret = sigaction(SIGUSR1, &sa, NULL))) { 651 UNRESOLVED(ret, "Unable to register signal handler"); 652 } 653 #if VERBOSE > 1 654 output("[parent] Signal handler registered\n"); 655 #endif 656 657 for (i = 0; i < NTOT; i++) { 658 ret = pthread_create(&th[i], &ta, threaded_A, &(td->cd[i])); 659 /* In case of failure we can exit; the child process will die after a while */ 660 if (ret != 0) { 661 UNRESOLVED(ret, "[Parent] Failed to create a thread"); 662 } 663 #if VERBOSE > 1 664 if ((i % 10) == 0) 665 output("[parent] %i threads created...\n", i + 1); 666 #endif 667 } 668 669 #if VERBOSE > 1 670 output("[parent] All %i threads are running...\n", NTOT); 671 #endif 672 673 for (i = 0; i < NTOT; i++) { 674 ret = pthread_join(th[i], NULL); 675 if (ret != 0) { 676 UNRESOLVED(ret, "[Parent] Failed to join a thread"); 677 } 678 } 679 680 /* Destroy everything */ 681 for (i = 0; i < NTOT; i++) { 682 /* destroy the condvar */ 683 ret = pthread_cond_destroy(&(td->cd[i].cnd)); 684 if (ret != 0) { 685 UNRESOLVED(ret, "[parent] Cond destroy failed"); 686 } 687 688 /* destroy the mutex */ 689 ret = pthread_mutex_init(&(td->cd[i].mtx), &ma); 690 if (ret != 0) { 691 UNRESOLVED(ret, "[parent] Mutex destroy failed"); 692 } 693 } 694 695 #if VERBOSE > 0 696 output("Test passed\n"); 697 #endif 698 699 PASSED; 700 } 701