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