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