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 sample test aims to check the following assertion: 18 * 19 * When the function fails because of a timeout, 20 * it must reacquire the mutex (as when it is successful) 21 22 * The steps are: 23 * -> For each mutex type; 24 * -> with and without process-shared primitive if this is supported; 25 * -> with different clocks if this is supported, 26 * -> Initialize a condvar and a mutex. 27 * -> Create a new thread (or process for process-shared condvars & mutex) 28 * -> The new thread (process) locks the mutex, then enters a timedwait which will timeout soon. 29 * -> The parent thread (process) then locks the mutex, ensures that the child is waiting, 30 * then sleeps until the timeout is terminated; and checks the child does not leave the wait function. 31 * -> The parent unlocks the mutex then waits for the child. 32 * -> The child checks that it owns the mutex; then it leaves. 33 */ 34 35 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */ 36 #define _POSIX_C_SOURCE 200112L 37 38 /* We need the XSI extention for the mutex attributes 39 and the mkstemp() routine */ 40 #ifndef WITHOUT_XOPEN 41 #define _XOPEN_SOURCE 600 42 #endif 43 44 #include <pthread.h> 45 #include <stdarg.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <unistd.h> 49 50 #include <errno.h> 51 #include <sys/wait.h> 52 #include <sys/mman.h> 53 #include <string.h> 54 #include <time.h> 55 56 #include "../testfrmw/testfrmw.h" 57 #include "../testfrmw/testfrmw.c" 58 59 #ifndef VERBOSE 60 #define VERBOSE 1 61 #endif 62 63 #define TIMEOUT (20) /* Number of microseconds to wait */ 64 65 #ifndef WITHOUT_ALTCLK 66 #define USE_ALTCLK /* make tests with MONOTONIC CLOCK if supported */ 67 #endif 68 69 #ifndef WITHOUT_XOPEN 70 71 typedef struct { 72 pthread_mutex_t mtx; 73 pthread_cond_t cnd; 74 clockid_t cid; /* Clock id used by the cond var */ 75 int type; /* Mutex type */ 76 int ctrl; /* checkpoints */ 77 int bool; /* Boolean predicate for the condition */ 78 int status; /* error code */ 79 } testdata_t; 80 81 struct _scenar { 82 int m_type; /* Mutex type to use */ 83 int mc_pshared; /* 0: mutex and cond are process-private (default) ~ !0: Both are process-shared, if supported */ 84 int c_clock; /* 0: cond uses the default clock. ~ !0: Cond uses monotonic clock, if supported. */ 85 int fork; /* 0: Test between threads. ~ !0: Test across processes, if supported (mmap) */ 86 char *descr; /* Case description */ 87 } scenarii[] = { 88 { 89 PTHREAD_MUTEX_DEFAULT, 0, 0, 0, "Default mutex"} 90 , { 91 PTHREAD_MUTEX_NORMAL, 0, 0, 0, "Normal mutex"} 92 , { 93 PTHREAD_MUTEX_ERRORCHECK, 0, 0, 0, "Errorcheck mutex"} 94 , { 95 PTHREAD_MUTEX_RECURSIVE, 0, 0, 0, "Recursive mutex"} 96 97 , { 98 PTHREAD_MUTEX_DEFAULT, 1, 0, 0, "PShared default mutex"} 99 , { 100 PTHREAD_MUTEX_NORMAL, 1, 0, 0, "Pshared normal mutex"} 101 , { 102 PTHREAD_MUTEX_ERRORCHECK, 1, 0, 0, "Pshared errorcheck mutex"} 103 , { 104 PTHREAD_MUTEX_RECURSIVE, 1, 0, 0, "Pshared recursive mutex"} 105 106 , { 107 PTHREAD_MUTEX_DEFAULT, 1, 0, 1, 108 "Pshared default mutex across processes"} 109 , { 110 PTHREAD_MUTEX_NORMAL, 1, 0, 1, 111 "Pshared normal mutex across processes"} 112 , { 113 PTHREAD_MUTEX_ERRORCHECK, 1, 0, 1, 114 "Pshared errorcheck mutex across processes"} 115 , { 116 PTHREAD_MUTEX_RECURSIVE, 1, 0, 1, 117 "Pshared recursive mutex across processes"} 118 119 #ifdef USE_ALTCLK 120 , { 121 PTHREAD_MUTEX_DEFAULT, 1, 1, 1, 122 "Pshared default mutex and alt clock condvar across processes"} 123 , { 124 PTHREAD_MUTEX_NORMAL, 1, 1, 1, 125 "Pshared normal mutex and alt clock condvar across processes"} 126 , { 127 PTHREAD_MUTEX_ERRORCHECK, 1, 1, 1, 128 "Pshared errorcheck mutex and alt clock condvar across processes"} 129 , { 130 PTHREAD_MUTEX_RECURSIVE, 1, 1, 1, 131 "Pshared recursive mutex and alt clock condvar across processes"} 132 133 , { 134 PTHREAD_MUTEX_DEFAULT, 0, 1, 0, 135 "Default mutex and alt clock condvar"} 136 , { 137 PTHREAD_MUTEX_NORMAL, 0, 1, 0, 138 "Normal mutex and alt clock condvar"} 139 , { 140 PTHREAD_MUTEX_ERRORCHECK, 0, 1, 0, 141 "Errorcheck mutex and alt clock condvar"} 142 , { 143 PTHREAD_MUTEX_RECURSIVE, 0, 1, 0, 144 "Recursive mutex and alt clock condvar"} 145 146 , { 147 PTHREAD_MUTEX_DEFAULT, 1, 1, 0, 148 "PShared default mutex and alt clock condvar"} 149 , { 150 PTHREAD_MUTEX_NORMAL, 1, 1, 0, 151 "Pshared normal mutex and alt clock condvar"} 152 , { 153 PTHREAD_MUTEX_ERRORCHECK, 1, 1, 0, 154 "Pshared errorcheck mutex and alt clock condvar"} 155 , { 156 PTHREAD_MUTEX_RECURSIVE, 1, 1, 0, 157 "Pshared recursive mutex and alt clock condvar"} 158 #endif 159 }; 160 161 void *tf(void *arg) 162 { 163 int ret = 0; 164 struct timespec ts; 165 166 testdata_t *td = (testdata_t *) arg; 167 168 /* Lock the mutex */ 169 ret = pthread_mutex_lock(&(td->mtx)); 170 if (ret != 0) { 171 td->status = ret; 172 UNRESOLVED(ret, "[child] Unable to lock the mutex"); 173 } 174 175 /* Tell the parent the mutex is locked */ 176 td->ctrl = 1; 177 178 /* Prepare the timeout parameter */ 179 ret = clock_gettime(td->cid, &ts); 180 if (ret != 0) { 181 td->status = ret; 182 UNRESOLVED(errno, "[child] Unable get clock time"); 183 } 184 185 /* Compute the timeout */ 186 ts.tv_nsec += TIMEOUT * 1000; 187 if (ts.tv_nsec >= 1000000000) { 188 ts.tv_sec++; 189 ts.tv_nsec -= 1000000000; 190 } 191 #if VERBOSE > 1 192 output("[child] Will timeout at %i.%09i\n", ts.tv_sec, ts.tv_nsec); 193 #endif 194 195 /* Enter the timed wait */ 196 do { 197 ret = pthread_cond_timedwait(&(td->cnd), &(td->mtx), &ts); 198 td->ctrl = 2; 199 } while ((ret == 0) && (td->bool == 0)); 200 201 td->ctrl = 3; 202 203 if (ret != ETIMEDOUT) { 204 td->status = ret; 205 UNRESOLVED(ret, "[child] Cond timedwait returned an error"); 206 } 207 208 /* Make sure we are owning the mutex */ 209 ret = pthread_mutex_trylock(&(td->mtx)); 210 if (td->type == PTHREAD_MUTEX_RECURSIVE) { 211 #if VERBOSE > 1 212 output 213 ("[child] Recursive mutex. Test if we are able to re-lock.\n"); 214 #endif 215 if (ret != 0) { 216 td->status = ret; 217 FAILED("[child] Unable to relock the recursive mutex"); 218 } 219 ret = pthread_mutex_unlock(&(td->mtx)); 220 if (ret != 0) { 221 td->status = ret; 222 UNRESOLVED(ret, "[child] Failed to unlock the mutex"); 223 } 224 } else { /* This was not a recursive mutex; the call must have failed */ 225 226 if (ret == 0) { 227 td->status = -1; 228 FAILED 229 ("[child] Thread did not owned the mutex after the timedwait return."); 230 } 231 if (ret != EBUSY) { 232 td->status = ret; 233 UNRESOLVED(ret, 234 "[child] Mutex trylock did not return EBUSY"); 235 } 236 #if VERBOSE > 1 237 output("[child] The mutex was busy (normal).\n"); 238 #endif 239 } 240 241 ret = pthread_mutex_unlock(&(td->mtx)); 242 if (ret != 0) { 243 td->status = ret; 244 output("[child] Got error %i: %s\n", ret, strerror(ret)); 245 FAILED 246 ("[child] Failed to unlock the mutex - owned by another thread?"); 247 } 248 249 td->ctrl = 4; 250 return NULL; 251 } 252 253 int main(void) 254 { 255 int ret; 256 unsigned int i; 257 pthread_mutexattr_t ma; 258 pthread_condattr_t ca; 259 260 testdata_t *td; 261 testdata_t alternativ; 262 263 int do_fork; 264 265 pid_t child_pr = 0, chkpid; 266 int status; 267 pthread_t child_th; 268 269 long pshared, monotonic, cs, mf; 270 271 output_init(); 272 pshared = sysconf(_SC_THREAD_PROCESS_SHARED); 273 cs = sysconf(_SC_CLOCK_SELECTION); 274 monotonic = sysconf(_SC_MONOTONIC_CLOCK); 275 mf = sysconf(_SC_MAPPED_FILES); 276 277 #if VERBOSE > 0 278 output("Test starting\n"); 279 output("System abilities:\n"); 280 output(" TPS : %li\n", pshared); 281 output(" CS : %li\n", cs); 282 output(" MON : %li\n", monotonic); 283 output(" MF : %li\n", mf); 284 if ((mf < 0) || (pshared < 0)) 285 output("Process-shared attributes won't be tested\n"); 286 if ((cs < 0) || (monotonic < 0)) 287 output("Alternative clock won't be tested\n"); 288 #endif 289 290 /* We are not interested in testing the clock if we have no other clock available.. */ 291 if (monotonic < 0) 292 cs = -1; 293 #ifndef USE_ALTCLK 294 if (cs > 0) 295 output 296 ("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n"); 297 #endif 298 299 /********** 300 * Allocate space for the testdata structure 301 */ 302 if (mf < 0) { 303 /* Cannot mmap a file, we use an alternative method */ 304 td = &alternativ; 305 pshared = -1; /* We won't do this testing anyway */ 306 #if VERBOSE > 0 307 output("Testdata allocated in the process memory.\n"); 308 #endif 309 } else { 310 /* We will place the test data in a mmaped file */ 311 char filename[] = "/tmp/cond_timedwait_2-4-XXXXXX"; 312 size_t sz; 313 void *mmaped; 314 int fd; 315 char *tmp; 316 317 /* We now create the temp files */ 318 fd = mkstemp(filename); 319 if (fd == -1) { 320 UNRESOLVED(errno, 321 "Temporary file could not be created"); 322 } 323 324 /* and make sure the file will be deleted when closed */ 325 unlink(filename); 326 327 #if VERBOSE > 1 328 output("Temp file created (%s).\n", filename); 329 #endif 330 331 sz = (size_t) sysconf(_SC_PAGESIZE); 332 333 tmp = calloc(1, sz); 334 if (tmp == NULL) { 335 UNRESOLVED(errno, "Memory allocation failed"); 336 } 337 338 /* Write the data to the file. */ 339 if (write(fd, tmp, sz) != (ssize_t) sz) { 340 UNRESOLVED(sz, "Writting to the file failed"); 341 } 342 343 free(tmp); 344 345 /* Now we can map the file in memory */ 346 mmaped = 347 mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 348 if (mmaped == MAP_FAILED) { 349 UNRESOLVED(errno, "mmap failed"); 350 } 351 352 td = (testdata_t *) mmaped; 353 354 /* Our datatest structure is now in shared memory */ 355 #if VERBOSE > 1 356 output("Testdata allocated in shared memory.\n"); 357 #endif 358 } 359 360 /********** 361 * For each test scenario, initialize the attributes and other variables. 362 */ 363 for (i = 0; i < (sizeof(scenarii) / sizeof(scenarii[0])); i++) { 364 #if VERBOSE > 1 365 output("[parent] Preparing attributes for scenario %i\n", i); 366 #endif 367 /* set / reset everything */ 368 do_fork = 0; 369 ret = pthread_mutexattr_init(&ma); 370 if (ret != 0) { 371 UNRESOLVED(ret, 372 "[parent] Unable to initialize the mutex attribute object"); 373 } 374 ret = pthread_condattr_init(&ca); 375 if (ret != 0) { 376 UNRESOLVED(ret, 377 "[parent] Unable to initialize the cond attribute object"); 378 } 379 380 /* Set the mutex type */ 381 ret = pthread_mutexattr_settype(&ma, scenarii[i].m_type); 382 if (ret != 0) { 383 UNRESOLVED(ret, "[parent] Unable to set mutex type"); 384 } 385 #if VERBOSE > 1 386 output("[parent] Mutex type : %i\n", scenarii[i].m_type); 387 #endif 388 389 /* Set the pshared attributes, if supported */ 390 if ((pshared > 0) && (scenarii[i].mc_pshared != 0)) { 391 ret = 392 pthread_mutexattr_setpshared(&ma, 393 PTHREAD_PROCESS_SHARED); 394 if (ret != 0) { 395 UNRESOLVED(ret, 396 "[parent] Unable to set the mutex process-shared"); 397 } 398 ret = 399 pthread_condattr_setpshared(&ca, 400 PTHREAD_PROCESS_SHARED); 401 if (ret != 0) { 402 UNRESOLVED(ret, 403 "[parent] Unable to set the cond var process-shared"); 404 } 405 #if VERBOSE > 1 406 output("[parent] Mutex & cond are process-shared\n"); 407 #endif 408 } 409 #if VERBOSE > 1 410 else { 411 output("[parent] Mutex & cond are process-private\n"); 412 } 413 #endif 414 415 /* Set the alternative clock, if supported */ 416 #ifdef USE_ALTCLK 417 if ((cs > 0) && (scenarii[i].c_clock != 0)) { 418 ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC); 419 if (ret != 0) { 420 UNRESOLVED(ret, 421 "[parent] Unable to set the monotonic clock for the cond"); 422 } 423 #if VERBOSE > 1 424 output("[parent] Cond uses the Monotonic clock\n"); 425 #endif 426 } 427 #if VERBOSE > 1 428 else { 429 output("[parent] Cond uses the default clock\n"); 430 } 431 #endif 432 #endif 433 434 /* Tell whether the test will be across processes */ 435 if ((pshared > 0) && (scenarii[i].fork != 0)) { 436 do_fork = 1; 437 #if VERBOSE > 1 438 output("[parent] Child will be a new process\n"); 439 #endif 440 } 441 #if VERBOSE > 1 442 else { 443 output("[parent] Child will be a new thread\n"); 444 } 445 #endif 446 447 /********** 448 * Initialize the testdata_t structure with the previously defined attributes 449 */ 450 /* Initialize the mutex */ 451 ret = pthread_mutex_init(&(td->mtx), &ma); 452 if (ret != 0) { 453 UNRESOLVED(ret, "[parent] Mutex init failed"); 454 } 455 456 /* initialize the condvar */ 457 ret = pthread_cond_init(&(td->cnd), &ca); 458 if (ret != 0) { 459 UNRESOLVED(ret, "[parent] Cond init failed"); 460 } 461 462 /* Initialize the other datas from the test structure */ 463 #ifdef USE_ALTCLK 464 ret = pthread_condattr_getclock(&ca, &(td->cid)); 465 if (ret != 0) { 466 UNRESOLVED(ret, 467 "[parent] Unable to read cond clock attribute"); 468 } 469 #else 470 td->cid = CLOCK_REALTIME; 471 #endif 472 473 ret = pthread_mutexattr_gettype(&ma, &(td->type)); 474 if (ret != 0) { 475 UNRESOLVED(ret, 476 "[parent] Unable to read mutex type attribute"); 477 } 478 479 td->ctrl = 0; 480 td->bool = 0; 481 td->status = 0; 482 483 /********** 484 * Proceed to the actual testing 485 */ 486 487 /* Create the child */ 488 if (do_fork != 0) { 489 /* We are testing across two processes */ 490 child_pr = fork(); 491 if (child_pr == -1) { 492 UNRESOLVED(errno, "[parent] Fork failed"); 493 } 494 495 if (child_pr == 0) { 496 #if VERBOSE > 1 497 output("[child] Child process starting...\n"); 498 #endif 499 500 if (tf((void *)td) != NULL) { 501 UNRESOLVED(-1, 502 "[child] Got an unexpected return value from test function"); 503 } else { 504 /* We cannot use the PASSED macro here since it would terminate the output */ 505 exit(0); 506 } 507 } 508 /* Only the parent process goes further */ 509 } else { /* do_fork == 0 */ 510 511 /* We are testing across two threads */ 512 ret = pthread_create(&child_th, NULL, tf, td); 513 if (ret != 0) { 514 UNRESOLVED(ret, 515 "[parent] Unable to create the child thread."); 516 } 517 } 518 519 /* Note: in case of an error, the child process will be alive for 1 sec then exit. */ 520 521 /* Child is now running and will enter the timedwait */ 522 /* We are waiting for this; and we have to monitor the status value as well. */ 523 ret = pthread_mutex_lock(&(td->mtx)); 524 if (ret != 0) { 525 UNRESOLVED(ret, "[parent] Unable to lock the mutex"); 526 } 527 528 while ((td->ctrl == 0) && (td->status == 0)) { 529 ret = pthread_mutex_unlock(&(td->mtx)); 530 if (ret != 0) { 531 UNRESOLVED(ret, 532 "[parent] Unable to unlock the mutex"); 533 } 534 sched_yield(); 535 ret = pthread_mutex_lock(&(td->mtx)); 536 if (ret != 0) { 537 UNRESOLVED(ret, 538 "[parent] Unable to lock the mutex"); 539 } 540 } 541 542 if ((td->ctrl == 2) && (td->status == 0)) { /* Spurious wakeups hapenned */ 543 output 544 ("Spurious wake ups have happened. Maybe pthread_cond_timedwait is broken?\n"); 545 td->ctrl = 1; 546 } 547 548 if (td->ctrl == 1) { /* The child is inside the cond timedwait */ 549 #if VERBOSE > 1 550 output 551 ("[parent] Child has not timedout yet, we will wait.\n"); 552 #endif 553 554 /* Let the child leave the wait function if something is broken */ 555 usleep(TIMEOUT); 556 557 if (td->ctrl != 1) { 558 FAILED 559 ("[parent] Child went out from pthread_cond_timedwait without locking the mutex"); 560 } 561 562 } 563 564 /* Let the child do its checking */ 565 ret = pthread_mutex_unlock(&(td->mtx)); 566 if (ret != 0) { 567 UNRESOLVED(ret, "[parent] Unable to unlock the mutex"); 568 } 569 570 /* Wait for the child to terminate */ 571 if (do_fork != 0) { 572 /* We were testing across two processes */ 573 chkpid = waitpid(child_pr, &status, 0); 574 if (chkpid != child_pr) { 575 output("Expected pid: %i. Got %i\n", 576 (int)child_pr, (int)chkpid); 577 UNRESOLVED(errno, "Waitpid failed"); 578 } 579 if (WIFSIGNALED(status)) { 580 output("Child process killed with signal %d\n", 581 WTERMSIG(status)); 582 UNRESOLVED(td->status, 583 "Child process was killed"); 584 } 585 586 if (WIFEXITED(status)) { 587 ret = WEXITSTATUS(status); 588 } else { 589 UNRESOLVED(td->status, 590 "Child process was neither killed nor exited"); 591 } 592 593 if (ret != 0) { 594 exit(ret); /* Output has already been closed in child */ 595 } 596 } else { /* child was a thread */ 597 598 ret = pthread_join(child_th, NULL); 599 if (ret != 0) { 600 UNRESOLVED(ret, 601 "[parent] Unable to join the thread"); 602 } 603 } 604 605 /********** 606 * Destroy the data 607 */ 608 ret = pthread_cond_destroy(&(td->cnd)); 609 if (ret != 0) { 610 UNRESOLVED(ret, "Failed to destroy the cond var"); 611 } 612 613 ret = pthread_mutex_destroy(&(td->mtx)); 614 if (ret != 0) { 615 UNRESOLVED(ret, "Failed to destroy the mutex"); 616 } 617 618 ret = pthread_condattr_destroy(&ca); 619 if (ret != 0) { 620 UNRESOLVED(ret, 621 "Failed to destroy the cond var attribute object"); 622 } 623 624 ret = pthread_mutexattr_destroy(&ma); 625 if (ret != 0) { 626 UNRESOLVED(ret, 627 "Failed to destroy the mutex attribute object"); 628 } 629 630 } /* Proceed to the next scenario */ 631 632 #if VERBOSE > 0 633 output("Test passed\n"); 634 #endif 635 636 PASSED; 637 } 638 639 #else /* WITHOUT_XOPEN */ 640 int main(void) 641 { 642 output_init(); 643 UNTESTED("This test requires XSI features"); 644 } 645 #endif 646