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 a cancel request unblocks the thread, 22 * it must not consume any pending condition signal request. 23 24 * The steps are: 25 * -> Create a bunch of threads waiting on a condvar. 26 * -> At the same time (using a barrier) one thread is canceled and the condition is signaled. 27 * -> Test checks that the cond signaling was not lost (at least one thread must have woken cleanly). 28 * -> Then everything is cleaned up and started again. 29 30 */ 31 32 33 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */ 34 #define _POSIX_C_SOURCE 200112L 35 36 /* We need the XSI extention for the mutex attributes */ 37#ifndef WITHOUT_XOPEN 38 #define _XOPEN_SOURCE 600 39#endif 40/********************************************************************************************/ 41/****************************** standard includes *****************************************/ 42/********************************************************************************************/ 43 #include <pthread.h> 44 #include <stdarg.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <unistd.h> 48 49 #include <errno.h> 50 #include <signal.h> 51 #include <string.h> 52 #include <time.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 77/********************************************************************************************/ 78/********************************** Configuration ******************************************/ 79/********************************************************************************************/ 80#ifndef SCALABILITY_FACTOR 81#define SCALABILITY_FACTOR 1 82#endif 83#ifndef VERBOSE 84#define VERBOSE 1 85#endif 86 87/* Size of the "bunch" of threads -- the real number will be 2 more threads per scenarii */ 88#define NCHILDREN (20) 89 90#define TIMEOUT (60) 91 92 93#ifndef WITHOUT_ALTCLK 94#define USE_ALTCLK /* make tests with MONOTONIC CLOCK if supported */ 95#endif 96 97/********************************************************************************************/ 98/*********************************** Test case *****************************************/ 99/********************************************************************************************/ 100 101#ifdef WITHOUT_XOPEN 102/* We define those to avoid compilation errors, but they won't be used */ 103#define PTHREAD_MUTEX_DEFAULT 0 104#define PTHREAD_MUTEX_NORMAL 0 105#define PTHREAD_MUTEX_ERRORCHECK 0 106#define PTHREAD_MUTEX_RECURSIVE 0 107 108#endif 109 110struct _scenar 111{ 112 int m_type; /* Mutex type to use */ 113 int mc_pshared; /* 0: mutex and cond are process-private (default) ~ !0: Both are process-shared, if supported */ 114 int c_clock; /* 0: cond uses the default clock. ~ !0: Cond uses monotonic clock, if supported. */ 115 int fork; /* 0: Test between threads. ~ !0: Test across processes, if supported (mmap) */ 116 char * descr; /* Case description */ 117} 118scenarii[] = 119{ 120 {PTHREAD_MUTEX_DEFAULT, 0, 0, 0, "Default mutex"} 121 ,{PTHREAD_MUTEX_NORMAL, 0, 0, 0, "Normal mutex"} 122 ,{PTHREAD_MUTEX_ERRORCHECK, 0, 0, 0, "Errorcheck mutex"} 123 ,{PTHREAD_MUTEX_RECURSIVE, 0, 0, 0, "Recursive mutex"} 124 125 ,{PTHREAD_MUTEX_DEFAULT, 1, 0, 0, "PShared default mutex"} 126 ,{PTHREAD_MUTEX_NORMAL, 1, 0, 0, "Pshared normal mutex"} 127 ,{PTHREAD_MUTEX_ERRORCHECK, 1, 0, 0, "Pshared errorcheck mutex"} 128 ,{PTHREAD_MUTEX_RECURSIVE, 1, 0, 0, "Pshared recursive mutex"} 129 130 ,{PTHREAD_MUTEX_DEFAULT, 1, 0, 1, "Pshared default mutex across processes"} 131 ,{PTHREAD_MUTEX_NORMAL, 1, 0, 1, "Pshared normal mutex across processes"} 132 ,{PTHREAD_MUTEX_ERRORCHECK, 1, 0, 1, "Pshared errorcheck mutex across processes"} 133 ,{PTHREAD_MUTEX_RECURSIVE, 1, 0, 1, "Pshared recursive mutex across processes"} 134 135#ifdef USE_ALTCLK 136 ,{PTHREAD_MUTEX_DEFAULT, 1, 1, 1, "Pshared default mutex and alt clock condvar across processes"} 137 ,{PTHREAD_MUTEX_NORMAL, 1, 1, 1, "Pshared normal mutex and alt clock condvar across processes"} 138 ,{PTHREAD_MUTEX_ERRORCHECK, 1, 1, 1, "Pshared errorcheck mutex and alt clock condvar across processes"} 139 ,{PTHREAD_MUTEX_RECURSIVE, 1, 1, 1, "Pshared recursive mutex and alt clock condvar across processes"} 140 141 ,{PTHREAD_MUTEX_DEFAULT, 0, 1, 0, "Default mutex and alt clock condvar"} 142 ,{PTHREAD_MUTEX_NORMAL, 0, 1, 0, "Normal mutex and alt clock condvar"} 143 ,{PTHREAD_MUTEX_ERRORCHECK, 0, 1, 0, "Errorcheck mutex and alt clock condvar"} 144 ,{PTHREAD_MUTEX_RECURSIVE, 0, 1, 0, "Recursive mutex and alt clock condvar"} 145 146 ,{PTHREAD_MUTEX_DEFAULT, 1, 1, 0, "PShared default mutex and alt clock condvar"} 147 ,{PTHREAD_MUTEX_NORMAL, 1, 1, 0, "Pshared normal mutex and alt clock condvar"} 148 ,{PTHREAD_MUTEX_ERRORCHECK, 1, 1, 0, "Pshared errorcheck mutex and alt clock condvar"} 149 ,{PTHREAD_MUTEX_RECURSIVE, 1, 1, 0, "Pshared recursive mutex and alt clock condvar"} 150#endif 151}; 152#define NSCENAR (sizeof(scenarii)/sizeof(scenarii[0])) 153 154 155/* This is the shared structure for all threads related to the same condvar */ 156struct celldata 157{ 158 pthread_t workers[NCHILDREN * SCALABILITY_FACTOR + 2]; 159 pthread_t signaler; 160 161 pthread_barrier_t bar; 162 pthread_mutex_t mtx; 163 pthread_cond_t cnd; 164 clockid_t cid; 165 166 int boolean; 167 int count; 168 169 long canceled; 170 long cancelfailed; 171 long cnttotal; 172} cells[NSCENAR * SCALABILITY_FACTOR]; 173 174char do_it=1; 175pthread_attr_t ta; 176 177 178void cleanup(void * arg) 179{ 180 int ret; 181 struct celldata * cd = (struct celldata *) arg; 182 183 /* Unlock the mutex */ 184 ret = pthread_mutex_unlock(&(cd->mtx)); 185if (ret != 0) { UNRESOLVED(ret, "Failed to unlock mutex in cancel handler"); } 186 187} 188 189void * worker(void * arg) 190{ 191 int ret; 192 struct celldata * cd = (struct celldata *) arg; 193 struct timespec ts; 194 195 /* lock the mutex */ 196 ret = pthread_mutex_lock(&(cd->mtx)); 197 if (ret != 0) { UNRESOLVED(ret, "Unable to lock mutex in worker"); } 198 199 /* Tell the cellmaster we are ready (count++) */ 200 cd->count += 1; 201 202 /* Timeout = now + TIMEOUT */ 203 ret = clock_gettime(cd->cid, &ts); 204 if (ret != 0) { UNRESOLVED(errno, "Gettime failed"); } 205 ts.tv_sec += TIMEOUT * SCALABILITY_FACTOR; 206 207 /* register cleanup handler */ 208 pthread_cleanup_push(cleanup, arg); 209 210 do 211 { 212 /* cond timedwait (while boolean == false)*/ 213 ret = pthread_cond_timedwait(&(cd->cnd), &(cd->mtx), &ts); 214 215 /* if timeout => failed (lost signal) */ 216 if (ret == ETIMEDOUT) 217 { 218 FAILED("Timeout occured. A condition signal was probably lost."); 219 } 220 221 if (ret != 0) { UNRESOLVED(ret, "Cond timedwait failed"); } 222 223 } while (cd->boolean == 0); 224 225 /* broadcast the condition */ 226 ret = pthread_cond_broadcast(&(cd->cnd)); 227 if (ret != 0) { UNRESOLVED(ret, "Broadcasting the condition failed"); } 228 229 /* unregister the cleanup */ 230 pthread_cleanup_pop(0); 231 232 /* unlock the mutex */ 233 ret = pthread_mutex_unlock(&(cd->mtx)); 234 if (ret != 0) { UNRESOLVED(ret, "Unable to unlock the mutex"); } 235 236 return NULL; 237} 238 239void * signaler(void * arg) 240{ 241 int ret; 242 struct celldata * cd = (struct celldata *) arg; 243 244 /* Lock the mutex if required */ 245 if (cd->boolean == -1) 246 { 247 ret = pthread_mutex_lock(&(cd->mtx)); 248 if (ret != 0) { UNRESOLVED(ret, "mutex lock failed in signaler"); } 249 } 250 251 /* wait the barrier */ 252 ret = pthread_barrier_wait(&(cd->bar)); 253 if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) { UNRESOLVED(ret, "Barrier wait failed"); } 254 255 /* signal the cond */ 256 ret = pthread_cond_signal(&(cd->cnd)); 257 if (ret != 0) { UNRESOLVED(ret, "Signaling the cond failed"); } 258 259 /* Unlock the mutex if required */ 260 if (cd->boolean == -1) 261 { 262 ret = pthread_mutex_unlock(&(cd->mtx)); 263 if (ret != 0) { UNRESOLVED(ret, "mutex unlock failed in signaler"); } 264 } 265 266 return NULL; 267} 268 269void * cellmanager(void * arg) 270{ 271 int ret, i; 272 struct celldata * cd = (struct celldata *) arg; 273 struct timespec ts; 274 int randval; 275 void * w_ret; 276 277 cd->canceled = 0; 278 cd->cancelfailed = 0; 279 cd->cnttotal = 0; 280 281 /* while do_it */ 282 while (do_it) 283 { 284 /* Initialize some stuff */ 285 cd->boolean = 0; 286 cd->count = 0; 287 cd->cnttotal += 1; 288 289 /* create the workers */ 290 for (i=0; i< NCHILDREN * SCALABILITY_FACTOR + 2; i++) 291 { 292 ret = pthread_create(&(cd->workers[i]), &ta, worker, arg); 293 if (ret != 0) { UNRESOLVED(ret, "Unable to create enough threads"); } 294 } 295 296 /* choose a (pseudo) random thread to cancel */ 297 ret = clock_gettime(cd->cid, &ts); 298 if (ret != 0) { UNRESOLVED(errno, "Failed to read clock"); } 299 randval = (ts.tv_sec + (ts.tv_nsec >> 10) ) % (NCHILDREN * SCALABILITY_FACTOR + 2); 300 301 /* wait for the workers to be ready */ 302 do 303 { 304 ret = pthread_mutex_lock(&(cd->mtx)); 305 if (ret != 0) { UNRESOLVED(ret, "Mutex lock failed"); } 306 307 i = cd->count; 308 309 ret = pthread_mutex_unlock(&(cd->mtx)); 310 if (ret != 0) { UNRESOLVED(ret, "Mutex unlock failed"); } 311 } while (i < NCHILDREN * SCALABILITY_FACTOR + 2); 312 313 /* Set the boolean ( 1 => no lock in signaler; -1 => lock ) */ 314 cd->boolean = (ts.tv_sec & 1)?-1:1; 315 316 /* create the signaler */ 317 ret = pthread_create(&(cd->signaler), &ta, signaler, arg); 318 if (ret != 0) { UNRESOLVED(ret, "Failed to create signaler thread"); } 319 320 /* wait the barrier */ 321 ret = pthread_barrier_wait(&(cd->bar)); 322 if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) { UNRESOLVED(ret, "Failed to wait for the barrier"); } 323 324 /* cancel the chosen thread */ 325 ret = pthread_cancel(cd->workers[randval]); 326 327 /* it is possible the thread is already terminated -- so we don't stop on error */ 328 if (ret != 0) 329 { 330 #if VERBOSE > 2 331 output("%d\n", randval); 332 #endif 333 cd->cancelfailed +=1; 334 } 335 336 /* join every threads */ 337 ret = pthread_join(cd->signaler, NULL); 338 if (ret != 0) { UNRESOLVED(ret, "Failed to join the signaler thread"); } 339 340 for (i=0; i< NCHILDREN * SCALABILITY_FACTOR + 2; i++) 341 { 342 ret = pthread_join(cd->workers[i], &w_ret); 343 if (ret != 0) { UNRESOLVED(ret, "Unable to join a worker"); } 344 if (w_ret == PTHREAD_CANCELED) 345 cd->canceled += 1; 346 } 347 } 348 349 return NULL; 350} 351 352void sighdl(int sig) 353{ 354 /* do_it = 0 */ 355 do { do_it = 0; } 356 while (do_it); 357} 358 359int main (int argc, char * argv[]) 360{ 361 int ret, i, j; 362 struct sigaction sa; 363 364 pthread_mutexattr_t ma; 365 pthread_condattr_t ca; 366 clockid_t cid = CLOCK_REALTIME; 367 long canceled = 0; 368 long cancelfailed = 0; 369 long cnttotal = 0; 370 371 long pshared, monotonic, cs; 372 373 pthread_t mngrs[NSCENAR * SCALABILITY_FACTOR]; 374 375 output_init(); 376 377 /* check the system abilities */ 378 pshared = sysconf(_SC_THREAD_PROCESS_SHARED); 379 cs = sysconf(_SC_CLOCK_SELECTION); 380 monotonic = sysconf(_SC_MONOTONIC_CLOCK); 381 382 #if VERBOSE > 0 383 output("Test starting\n"); 384 output("System abilities:\n"); 385 output(" TPS : %li\n", pshared); 386 output(" CS : %li\n", cs); 387 output(" MON : %li\n", monotonic); 388 if ((cs < 0) || (monotonic < 0)) 389 output("Alternative clock won't be tested\n"); 390 #endif 391 392 if (monotonic < 0) 393 cs = -1; 394 395 #ifndef USE_ALTCLK 396 if (cs > 0) 397 output("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n"); 398 #endif 399 400 401 /* Initialize the celldatas according to scenarii */ 402 for ( i=0; i< NSCENAR ; i++) 403 { 404 #if VERBOSE > 1 405 output("[parent] Preparing attributes for: %s\n", scenarii[i].descr); 406 #ifdef WITHOUT_XOPEN 407 output("[parent] Mutex attributes DISABLED -> not used\n"); 408 #endif 409 #endif 410 411 /* set / reset everything */ 412 ret = pthread_mutexattr_init(&ma); 413 if (ret != 0) { UNRESOLVED(ret, "[parent] Unable to initialize the mutex attribute object"); } 414 ret = pthread_condattr_init(&ca); 415 if (ret != 0) { UNRESOLVED(ret, "[parent] Unable to initialize the cond attribute object"); } 416 417 #ifndef WITHOUT_XOPEN 418 /* Set the mutex type */ 419 ret = pthread_mutexattr_settype(&ma, scenarii[i].m_type); 420 if (ret != 0) { UNRESOLVED(ret, "[parent] Unable to set mutex type"); } 421 #if VERBOSE > 1 422 output("[parent] Mutex type : %i\n", scenarii[i].m_type); 423 #endif 424 #endif 425 426 /* Set the pshared attributes, if supported */ 427 if ((pshared > 0) && (scenarii[i].mc_pshared != 0)) 428 { 429 ret = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED); 430 if (ret != 0) { UNRESOLVED(ret, "[parent] Unable to set the mutex process-shared"); } 431 ret = pthread_condattr_setpshared(&ca, PTHREAD_PROCESS_SHARED); 432 if (ret != 0) { UNRESOLVED(ret, "[parent] Unable to set the cond var process-shared"); } 433 #if VERBOSE > 1 434 output("[parent] Mutex & cond are process-shared\n"); 435 #endif 436 } 437 #if VERBOSE > 1 438 else { 439 output("[parent] Mutex & cond are process-private\n"); 440 } 441 #endif 442 443 /* Set the alternative clock, if supported */ 444 #ifdef USE_ALTCLK 445 if ((cs > 0) && (scenarii[i].c_clock != 0)) 446 { 447 ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC); 448 if (ret != 0) { UNRESOLVED(ret, "[parent] Unable to set the monotonic clock for the cond"); } 449 #if VERBOSE > 1 450 output("[parent] Cond uses the Monotonic clock\n"); 451 #endif 452 } 453 #if VERBOSE > 1 454 else { 455 output("[parent] Cond uses the default clock\n"); 456 } 457 #endif 458 ret = pthread_condattr_getclock(&ca, &cid); 459 if (ret != 0) { UNRESOLVED(ret, "Unable to get clock from cond attr"); } 460 #endif 461 462 /* Initialize all the mutex and condvars which uses those attributes */ 463 for (j=0; j < SCALABILITY_FACTOR; j++) 464 { 465 cells[i + j * NSCENAR].cid = cid; 466 467 /* initialize the condvar */ 468 ret = pthread_cond_init(&(cells[i + j * NSCENAR].cnd), &ca); 469 if (ret != 0) { UNRESOLVED(ret, "Cond init failed"); } 470 471 /* initialize the mutex */ 472 ret = pthread_mutex_init(&(cells[i + j * NSCENAR].mtx), &ma); 473 if (ret != 0) { UNRESOLVED(ret, "Mutex init failed"); } 474 475 /* initialize the barrier */ 476 ret = pthread_barrier_init(&(cells[i + j * NSCENAR].bar), NULL, 2); 477 if (ret != 0) { UNRESOLVED(ret, "Failed to init barrier"); } 478 } 479 480 ret = pthread_condattr_destroy(&ca); 481 if (ret != 0) { UNRESOLVED(ret, "Failed to destroy the cond var attribute object"); } 482 483 ret = pthread_mutexattr_destroy(&ma); 484 if (ret != 0) { UNRESOLVED(ret, "Failed to destroy the mutex attribute object"); } 485 } 486 #if VERBOSE > 1 487 output("[parent] All condvars & mutex are ready\n"); 488 #endif 489 490 /* register the signal handler */ 491 sigemptyset (&sa.sa_mask); 492 sa.sa_flags = 0; 493 sa.sa_handler = sighdl; 494 if ((ret = sigaction (SIGUSR1, &sa, NULL))) 495 { UNRESOLVED(ret, "Unable to register signal handler"); } 496 #if VERBOSE > 1 497 output("[parent] Signal handler registered\n"); 498 #endif 499 500 /* Initialize the thread attribute object */ 501 ret = pthread_attr_init(&ta); 502 if (ret != 0) { UNRESOLVED(ret, "[parent] Failed to initialize a thread attribute object"); } 503 ret = pthread_attr_setstacksize(&ta, sysconf(_SC_THREAD_STACK_MIN)); 504 if (ret != 0) { UNRESOLVED(ret, "[parent] Failed to set thread stack size"); } 505 506 /* create the NSCENAR * SCALABILITY_FACTOR manager threads */ 507 for (i=0; i<NSCENAR * SCALABILITY_FACTOR; i++) 508 { 509 ret = pthread_create( &mngrs[i], &ta, cellmanager, &(cells[i])); 510 /* In case of failure we can exit; the child process will die after a while */ 511 if (ret != 0) { UNRESOLVED(ret, "[Parent] Failed to create a thread"); } 512 513 #if VERBOSE > 1 514 if ((i % 4) == 0) 515 output("[parent] %i manager threads created...\n", i+1); 516 #endif 517 } 518 519 #if VERBOSE > 1 520 output("[parent] All %i manager threads are running...\n", NSCENAR * SCALABILITY_FACTOR); 521 #endif 522 523 /* join the manager threads and destroy the cells */ 524 for (i=0; i<NSCENAR * SCALABILITY_FACTOR; i++) 525 { 526 ret = pthread_join( mngrs[i], NULL); 527 if (ret != 0) { UNRESOLVED(ret, "[Parent] Failed to join a thread"); } 528 529 canceled += cells[i].canceled; 530 cancelfailed += cells[i].cancelfailed; 531 cnttotal += cells[i].cnttotal; 532 533 ret = pthread_barrier_destroy(&(cells[i].bar)); 534 if (ret != 0) { UNRESOLVED(ret, "Failed to destroy a barrier"); } 535 536 ret = pthread_cond_destroy(&(cells[i].cnd)); 537 if (ret != 0) { UNRESOLVED(ret, "Failed to destroy a cond"); } 538 539 ret = pthread_mutex_destroy(&(cells[i].mtx)); 540 if (ret != 0) { UNRESOLVED(ret, "Failed to destroy a mutex"); } 541 } 542 543 /* exit */ 544 #if VERBOSE > 0 545 output("Test passed\n"); 546 output(" Total loops : %8li\n", cnttotal); 547 #endif 548 #if VERBOSE > 1 549 output(" Failed cancel request: %8li\n", cancelfailed); 550 output(" Canceled threads : %8li\n", canceled); 551 #endif 552 553 PASSED; 554} 555 556