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 N threads which will wait on a condition variable 24 * -> broadcast the condition 25 * -> Every child checks that it owns the mutex (when possible) 26 * 27 */ 28 29 #define _POSIX_C_SOURCE 200112L 30 31 #ifndef WITHOUT_XOPEN 32 #define _XOPEN_SOURCE 600 33 #endif 34 35 #include <pthread.h> 36 #include <stdarg.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <unistd.h> 40 41 #include <errno.h> 42 #include <signal.h> 43 #include <string.h> 44 #include <time.h> 45 #include <sys/mman.h> 46 #include <sys/wait.h> 47 48 #include "../testfrmw/testfrmw.h" 49 #include "../testfrmw/testfrmw.c" 50 51 #define UNRESOLVED_KILLALL(error, text) { \ 52 if (td->fork) { \ 53 int _nch; \ 54 for (_nch = 0; _nch < children.nb; _nch++) \ 55 kill(children.ch[_nch].p, SIGKILL); \ 56 } \ 57 UNRESOLVED(error, text); \ 58 } 59 #define FAILED_KILLALL(text, Tchild) { \ 60 if (td->fork) { \ 61 int _nch; \ 62 for (_nch = 0; _nch < children.nb; _nch++) \ 63 kill(children.ch[_nch].p, SIGKILL); \ 64 } \ 65 FAILED(text); \ 66 } 67 68 #ifndef VERBOSE 69 #define VERBOSE 1 70 #endif 71 72 #define NCHILDREN (20) 73 74 #define TIMEOUT (120) 75 76 #ifndef WITHOUT_ALTCLK 77 #define USE_ALTCLK /* make tests with MONOTONIC CLOCK if supported */ 78 #endif 79 80 #ifdef WITHOUT_XOPEN 81 /* We define those to avoid compilation errors, but they won't be used */ 82 #define PTHREAD_MUTEX_DEFAULT 0 83 #define PTHREAD_MUTEX_NORMAL 0 84 #define PTHREAD_MUTEX_ERRORCHECK 0 85 #define PTHREAD_MUTEX_RECURSIVE 0 86 87 #endif 88 89 struct _scenar { 90 int m_type; 91 int mc_pshared; 92 int c_clock; 93 int fork; 94 char *descr; 95 } scenarii[] = { 96 { 97 PTHREAD_MUTEX_DEFAULT, 0, 0, 0, "Default mutex"}, { 98 PTHREAD_MUTEX_NORMAL, 0, 0, 0, "Normal mutex"}, { 99 PTHREAD_MUTEX_ERRORCHECK, 0, 0, 0, "Errorcheck mutex"}, { 100 PTHREAD_MUTEX_RECURSIVE, 0, 0, 0, "Recursive mutex"}, { 101 PTHREAD_MUTEX_DEFAULT, 1, 0, 0, "PShared default mutex"}, { 102 PTHREAD_MUTEX_NORMAL, 1, 0, 0, "Pshared normal mutex"}, { 103 PTHREAD_MUTEX_ERRORCHECK, 1, 0, 0, "Pshared errorcheck mutex"}, { 104 PTHREAD_MUTEX_RECURSIVE, 1, 0, 0, "Pshared recursive mutex"}, { 105 PTHREAD_MUTEX_DEFAULT, 1, 0, 1, 106 "Pshared default mutex across processes"}, { 107 PTHREAD_MUTEX_NORMAL, 1, 0, 1, "Pshared normal mutex across processes"}, 108 { 109 PTHREAD_MUTEX_ERRORCHECK, 1, 0, 1, 110 "Pshared errorcheck mutex across processes"}, { 111 PTHREAD_MUTEX_RECURSIVE, 1, 0, 1, 112 "Pshared recursive mutex across processes"}, 113 #ifdef USE_ALTCLK 114 { 115 PTHREAD_MUTEX_DEFAULT, 1, 1, 1, 116 "Pshared default mutex and alt clock condvar across processes"}, 117 { 118 PTHREAD_MUTEX_NORMAL, 1, 1, 1, 119 "Pshared normal mutex and alt clock condvar across processes"}, 120 { 121 PTHREAD_MUTEX_ERRORCHECK, 1, 1, 1, 122 "Pshared errorcheck mutex and alt clock condvar across processes"}, 123 { 124 PTHREAD_MUTEX_RECURSIVE, 1, 1, 1, 125 "Pshared recursive mutex and alt clock condvar across processes"}, 126 { 127 PTHREAD_MUTEX_DEFAULT, 0, 1, 0, "Default mutex and alt clock condvar"}, 128 { 129 PTHREAD_MUTEX_NORMAL, 0, 1, 0, "Normal mutex and alt clock condvar"}, 130 { 131 PTHREAD_MUTEX_ERRORCHECK, 0, 1, 0, 132 "Errorcheck mutex and alt clock condvar"}, { 133 PTHREAD_MUTEX_RECURSIVE, 0, 1, 0, 134 "Recursive mutex and alt clock condvar"}, { 135 PTHREAD_MUTEX_DEFAULT, 1, 1, 0, 136 "PShared default mutex and alt clock condvar"}, { 137 PTHREAD_MUTEX_NORMAL, 1, 1, 0, 138 "Pshared normal mutex and alt clock condvar"}, { 139 PTHREAD_MUTEX_ERRORCHECK, 1, 1, 0, 140 "Pshared errorcheck mutex and alt clock condvar"}, { 141 PTHREAD_MUTEX_RECURSIVE, 1, 1, 0, 142 "Pshared recursive mutex and alt clock condvar"}, 143 #endif 144 }; 145 146 #define NSCENAR (sizeof(scenarii) / sizeof(scenarii[0])) 147 148 struct testdata { 149 int count; 150 pthread_cond_t cnd; 151 pthread_mutex_t mtx; 152 int predicate; 153 clockid_t cid; 154 int mtype; 155 char fork; 156 } *td; 157 158 struct { 159 union { 160 pthread_t t; 161 pid_t p; 162 } ch[NCHILDREN]; 163 int nb; 164 } children; 165 166 static void *child(void *arg) 167 { 168 int ret = 0; 169 int timed; 170 struct timespec ts; 171 172 /* lock the mutex */ 173 ret = pthread_mutex_lock(&td->mtx); 174 if (ret != 0) 175 UNRESOLVED(ret, "Failed to lock mutex in child"); 176 177 /* increment count */ 178 td->count++; 179 timed = td->count & 1; 180 181 if (timed) { 182 ret = clock_gettime(td->cid, &ts); 183 if (ret != 0) 184 UNRESOLVED(errno, "Unable to read clock"); 185 ts.tv_sec += TIMEOUT; 186 } 187 188 do { 189 /* Wait while the predicate is false */ 190 if (timed) 191 ret = pthread_cond_timedwait(&td->cnd, &td->mtx, &ts); 192 else 193 ret = pthread_cond_wait(&td->cnd, &td->mtx); 194 #if VERBOSE > 5 195 output("[child] Wokenup timed=%i, Predicate=%i, ret=%i\n", 196 timed, td->predicate, ret); 197 #endif 198 } while ((ret == 0) && (td->predicate == 0)); 199 if (ret == ETIMEDOUT) { 200 FAILED("Timeout occured. This means a cond signal was lost " 201 "-- or parent died"); 202 } 203 if (ret != 0) 204 UNRESOLVED(ret, "Failed to wait for the cond"); 205 206 /* Check that we are owning the mutex */ 207 #ifdef WITHOUT_XOPEN 208 ret = pthread_mutex_trylock(&(td->mtx)); 209 if (ret == 0) 210 FAILED("The mutex was not owned after return from " 211 "condition waiting"); 212 #else 213 if (td->mtype == PTHREAD_MUTEX_RECURSIVE) { 214 ret = pthread_mutex_trylock(&(td->mtx)); 215 if (ret != 0) 216 FAILED("Unable to relock recursive mutex: not owning?"); 217 ret = pthread_mutex_unlock(&(td->mtx)); 218 if (ret != 0) 219 UNRESOLVED(ret, "Failed to unlock the mutex"); 220 } 221 if (td->mtype == PTHREAD_MUTEX_ERRORCHECK) { 222 ret = pthread_mutex_lock(&(td->mtx)); 223 if (ret == 0) 224 FAILED("Was able to lock errorcheck mutex: " 225 "the mutex was not acquired once already?"); 226 } 227 #endif 228 229 /* unlock the mutex */ 230 ret = pthread_mutex_unlock(&td->mtx); 231 if (ret != 0) 232 UNRESOLVED(ret, "Failed to unlock the mutex."); 233 234 return NULL; 235 } 236 237 static void *timer(void *arg) 238 { 239 unsigned int to = TIMEOUT; 240 do { 241 to = sleep(to); 242 } while (to > 0); 243 FAILED_KILLALL("Operation timed out. A signal was lost.", pchildren); 244 return NULL; 245 } 246 247 int main(void) 248 { 249 int ret; 250 251 pthread_mutexattr_t ma; 252 pthread_condattr_t ca; 253 254 int scenar; 255 long pshared, monotonic, cs, mf; 256 257 int child_count; 258 259 pid_t pid; 260 int status; 261 262 pthread_t t_timer; 263 264 pthread_attr_t ta; 265 266 struct testdata alternativ; 267 268 output_init(); 269 270 /* check the system abilities */ 271 pshared = sysconf(_SC_THREAD_PROCESS_SHARED); 272 cs = sysconf(_SC_CLOCK_SELECTION); 273 monotonic = sysconf(_SC_MONOTONIC_CLOCK); 274 mf = sysconf(_SC_MAPPED_FILES); 275 276 #if VERBOSE > 0 277 output("Test starting\n"); 278 output("System abilities:\n"); 279 output(" TPS : %li\n", pshared); 280 output(" CS : %li\n", cs); 281 output(" MON : %li\n", monotonic); 282 output(" MF : %li\n", mf); 283 if ((mf < 0) || (pshared < 0)) 284 output("Process-shared attributes won't be tested\n"); 285 if ((cs < 0) || (monotonic < 0)) 286 output("Alternative clock won't be tested\n"); 287 #endif 288 289 if (monotonic < 0) 290 cs = -1; 291 292 #ifndef USE_ALTCLK 293 if (cs > 0) 294 output("Implementation supports the MONOTONIC CLOCK " 295 "but option is disabled in test.\n"); 296 #endif 297 298 if (mf < 0) { 299 /* Cannot mmap a file, we use an alternative method */ 300 td = &alternativ; 301 pshared = -1; /* We won't do this testing anyway */ 302 #if VERBOSE > 0 303 output("Testdata allocated in the process memory.\n"); 304 #endif 305 } else { 306 char filename[] = "/tmp/cond_broadcast-XXXXXX"; 307 size_t sz, ps; 308 void *mmaped; 309 int fd; 310 char *tmp; 311 312 fd = mkstemp(filename); 313 if (fd == -1) 314 UNRESOLVED(errno, 315 "Temporary file could not be created"); 316 317 unlink(filename); 318 319 #if VERBOSE > 1 320 output("Temp file created (%s).\n", filename); 321 #endif 322 323 ps = (size_t) sysconf(_SC_PAGESIZE); 324 sz = ((sizeof(struct testdata) / ps) + 1) * ps; 325 326 tmp = calloc(1, sz); 327 if (tmp == NULL) 328 UNRESOLVED(errno, "Memory allocation failed"); 329 330 if (write(fd, tmp, sz) != (ssize_t) sz) 331 UNRESOLVED(sz, "Writting to the file failed"); 332 333 free(tmp); 334 335 mmaped = 336 mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 337 if (mmaped == MAP_FAILED) 338 UNRESOLVED(errno, "mmap failed"); 339 340 td = (struct testdata *)mmaped; 341 342 /* Our datatest structure is now in shared memory */ 343 #if VERBOSE > 1 344 output("Testdata allocated in shared memory (%ib).\n", 345 sizeof(struct testdata)); 346 #endif 347 } 348 349 ret = pthread_attr_init(&ta); 350 if (ret != 0) 351 UNRESOLVED(ret, "[parent] Failed to initialize a thread " 352 "attribute object"); 353 ret = pthread_attr_setstacksize(&ta, sysconf(_SC_THREAD_STACK_MIN)); 354 if (ret != 0) 355 UNRESOLVED(ret, "[parent] Failed to set thread stack size"); 356 357 /* Do the test for each test scenario */ 358 for (scenar = 0; scenar < NSCENAR; scenar++) { 359 /* set / reset everything */ 360 td->fork = 0; 361 ret = pthread_mutexattr_init(&ma); 362 if (ret != 0) 363 UNRESOLVED(ret, "[parent] Unable to initialize the " 364 "mutex attribute object"); 365 ret = pthread_condattr_init(&ca); 366 if (ret != 0) 367 UNRESOLVED(ret, "[parent] Unable to initialize the " 368 "cond attribute object"); 369 370 #ifndef WITHOUT_XOPEN 371 /* Set the mutex type */ 372 ret = pthread_mutexattr_settype(&ma, scenarii[scenar].m_type); 373 if (ret != 0) 374 UNRESOLVED(ret, "[parent] Unable to set mutex type"); 375 #endif 376 377 td->mtype = scenarii[scenar].m_type; 378 379 /* Set the pshared attributes, if supported */ 380 if ((pshared > 0) && (scenarii[scenar].mc_pshared != 0)) { 381 ret = pthread_mutexattr_setpshared(&ma, 382 PTHREAD_PROCESS_SHARED); 383 if (ret != 0) 384 UNRESOLVED(ret, "[parent] Unable to set " 385 "the mutex process-shared"); 386 ret = pthread_condattr_setpshared(&ca, 387 PTHREAD_PROCESS_SHARED); 388 if (ret != 0) 389 UNRESOLVED(ret, "[parent] Unable to set " 390 "the cond var process-shared"); 391 } 392 393 /* Set the alternative clock, if supported */ 394 #ifdef USE_ALTCLK 395 if ((cs > 0) && (scenarii[scenar].c_clock != 0)) { 396 ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC); 397 if (ret != 0) 398 UNRESOLVED(ret, "[parent] Unable to set the " 399 "monotonic clock for the cond"); 400 } 401 ret = pthread_condattr_getclock(&ca, &td->cid); 402 if (ret != 0) 403 UNRESOLVED(ret, "Unable to get clock from cond attr"); 404 #else 405 td->cid = CLOCK_REALTIME; 406 #endif 407 408 /* Tell whether the test will be across processes */ 409 if ((pshared > 0) && (scenarii[scenar].fork != 0)) 410 td->fork = 1; 411 412 ret = pthread_cond_init(&td->cnd, &ca); 413 if (ret != 0) 414 UNRESOLVED(ret, "Cond init failed"); 415 416 ret = pthread_mutex_init(&td->mtx, &ma); 417 if (ret != 0) 418 UNRESOLVED(ret, "Mutex init failed"); 419 420 ret = pthread_condattr_destroy(&ca); 421 if (ret != 0) 422 UNRESOLVED(ret, "Failed to destroy the cond var " 423 "attribute object"); 424 425 ret = pthread_mutexattr_destroy(&ma); 426 if (ret != 0) 427 UNRESOLVED(ret, "Failed to destroy the mutex " 428 "attribute object"); 429 430 #if VERBOSE > 2 431 output("[parent] Starting test %s\n", scenarii[scenar].descr); 432 #endif 433 434 td->count = 0; 435 436 /* Create all the children */ 437 for (children.nb = 0; children.nb < NCHILDREN; children.nb++) { 438 if (td->fork == 0) { 439 ret = 440 pthread_create(& 441 (children.ch[children.nb].t), 442 &ta, child, NULL); 443 if (ret != 0) 444 UNRESOLVED(ret, "Failed to create " 445 "enough threads"); 446 } else { 447 children.ch[children.nb].p = fork(); 448 if (children.ch[children.nb].p == 0) { 449 child(NULL); 450 exit(0); 451 } 452 if (children.ch[children.nb].p == -1) { 453 children.nb--; 454 UNRESOLVED_KILLALL(errno, "Failed to " 455 "create enough processes"); 456 } 457 } 458 } 459 #if VERBOSE > 4 460 output("[parent] Created %i children\n", NCHILDREN); 461 #endif 462 463 /* Make sure all children are waiting */ 464 ret = pthread_mutex_lock(&td->mtx); 465 if (ret != 0) 466 UNRESOLVED_KILLALL(ret, "Failed to lock mutex"); 467 child_count = td->count; 468 while (child_count < NCHILDREN) { 469 ret = pthread_mutex_unlock(&td->mtx); 470 if (ret != 0) 471 UNRESOLVED_KILLALL(ret, 472 "Failed to unlock mutex"); 473 sched_yield(); 474 ret = pthread_mutex_lock(&td->mtx); 475 if (ret != 0) 476 UNRESOLVED_KILLALL(ret, "Failed to lock mutex"); 477 child_count = td->count; 478 } 479 480 #if VERBOSE > 4 481 output("[parent] All children are waiting\n"); 482 #endif 483 484 ret = pthread_create(&t_timer, NULL, timer, NULL); 485 if (ret != 0) 486 UNRESOLVED_KILLALL(ret, 487 "Unable to create timer thread"); 488 489 /* Wakeup the children */ 490 td->predicate = 1; 491 ret = pthread_cond_broadcast(&td->cnd); 492 if (ret != 0) 493 UNRESOLVED_KILLALL(ret, "Failed to broadcast " 494 "the condition."); 495 496 #if VERBOSE > 4 497 output("[parent] Condition was signaled\n"); 498 #endif 499 500 ret = pthread_mutex_unlock(&td->mtx); 501 if (ret != 0) 502 UNRESOLVED_KILLALL(ret, "Failed to unlock mutex"); 503 504 #if VERBOSE > 4 505 output("[parent] Joining the children\n"); 506 #endif 507 508 /* join the children */ 509 for (--children.nb; children.nb >= 0; children.nb--) { 510 if (td->fork == 0) { 511 ret = pthread_join(children.ch[children.nb].t, 512 NULL); 513 if (ret != 0) 514 UNRESOLVED(ret, 515 "Failed to join a child thread"); 516 } else { 517 pid = waitpid(children.ch[children.nb].p, 518 &status, 0); 519 if (pid != children.ch[children.nb].p) { 520 ret = errno; 521 output("Waitpid failed (expected: %i, " 522 "got: %i)\n", 523 children.ch[children.nb].p, pid); 524 UNRESOLVED_KILLALL(ret, 525 "Waitpid failed"); 526 } 527 if (WIFEXITED(status)) { 528 if (ret != PTS_FAIL) 529 ret |= WEXITSTATUS(status); 530 } 531 } 532 } 533 if (ret != 0) { 534 output_fini(); 535 exit(ret); 536 } 537 #if VERBOSE > 4 538 output("[parent] All children terminated\n"); 539 #endif 540 541 ret = pthread_cancel(t_timer); 542 if (ret != 0) 543 UNRESOLVED(ret, "Failed to cancel the timeout handler"); 544 545 ret = pthread_join(t_timer, NULL); 546 if (ret != 0) 547 UNRESOLVED(ret, "Failed to join the timeout handler"); 548 549 ret = pthread_cond_destroy(&td->cnd); 550 if (ret != 0) 551 UNRESOLVED(ret, "Failed to destroy the condvar"); 552 553 ret = pthread_mutex_destroy(&td->mtx); 554 if (ret != 0) 555 UNRESOLVED(ret, "Failed to destroy the mutex"); 556 557 } 558 559 ret = pthread_attr_destroy(&ta); 560 if (ret != 0) 561 UNRESOLVED(ret, "Final thread attr destroy failed"); 562 563 PASSED; 564 } 565