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