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