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