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