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