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