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 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 waits for the condition .
     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 joins 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  /********************************************************************************************/
     45 /****************************** standard includes *****************************************/
     46 /********************************************************************************************/
     47 #include <pthread.h>
     48 #include <stdarg.h>
     49 #include <stdio.h>
     50 #include <stdlib.h>
     51 #include <unistd.h>
     52 
     53 #include <errno.h>
     54 #include <sys/wait.h>
     55 #include <sys/mman.h>
     56 #include <string.h>
     57 
     58 /********************************************************************************************/
     59 /******************************   Test framework   *****************************************/
     60 /********************************************************************************************/
     61 #include "../testfrmw/testfrmw.h"
     62 #include "../testfrmw/testfrmw.c"
     63  /* This header is responsible for defining the following macros:
     64   * UNRESOLVED(ret, descr);
     65   *    where descr is a description of the error and ret is an int (error code for example)
     66   * FAILED(descr);
     67   *    where descr is a short text saying why the test has failed.
     68   * PASSED();
     69   *    No parameter.
     70   *
     71   * Both three macros shall terminate the calling process.
     72   * The testcase shall not terminate in any other maneer.
     73   *
     74   * The other file defines the functions
     75   * void output_init()
     76   * void output(char * string, ...)
     77   *
     78   * Those may be used to output information.
     79   */
     80 
     81 /********************************************************************************************/
     82 /********************************** Configuration ******************************************/
     83 /********************************************************************************************/
     84 #ifndef VERBOSE
     85 #define VERBOSE 1
     86 #endif
     87 
     88 #ifndef WITHOUT_ALTCLK
     89 #define USE_ALTCLK		/* make tests with MONOTONIC CLOCK if supported */
     90 #endif
     91 
     92 /********************************************************************************************/
     93 /***********************************    Test case   *****************************************/
     94 /********************************************************************************************/
     95 #ifndef WITHOUT_XOPEN
     96 
     97 typedef struct {
     98 	pthread_mutex_t mtx;
     99 	pthread_cond_t cnd;
    100 	clockid_t cid;		/* Clock id used by the cond var */
    101 	int type;		/* Mutex type */
    102 	int ctrl;		/* checkpoints */
    103 	int bool;		/* Boolean predicate for the condition */
    104 	int status;		/* error code */
    105 } testdata_t;
    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 void *tf(void *arg)
    188 {
    189 	int ret = 0;
    190 
    191 	testdata_t *td = (testdata_t *) arg;
    192 
    193 	/* Lock the mutex */
    194 	ret = pthread_mutex_lock(&(td->mtx));
    195 	if (ret != 0) {
    196 		td->status = ret;
    197 		UNRESOLVED(ret, "[child] Unable to lock the mutex");
    198 	}
    199 
    200 	/* Tell the parent the mutex is locked */
    201 	td->ctrl = 1;
    202 
    203 	/* Enter the wait */
    204 	do {
    205 		ret = pthread_cond_wait(&(td->cnd), &(td->mtx));
    206 		td->ctrl = 2;
    207 	} while ((ret == 0) && (td->bool == 0));
    208 
    209 	td->ctrl = 3;
    210 
    211 	if (ret != 0) {
    212 		td->status = ret;
    213 		UNRESOLVED(ret, "[child] Cond wait returned an error");
    214 	}
    215 
    216 	/* Make sure we are owning the mutex */
    217 	ret = pthread_mutex_trylock(&(td->mtx));
    218 	if (td->type == PTHREAD_MUTEX_RECURSIVE) {
    219 #if VERBOSE > 1
    220 		output
    221 		    ("[child] Recursive mutex. Test if we are able to re-lock.\n");
    222 #endif
    223 		if (ret != 0) {
    224 			td->status = ret;
    225 			FAILED("[child] Unable to relock the recursive mutex");
    226 		}
    227 		ret = pthread_mutex_unlock(&(td->mtx));
    228 		if (ret != 0) {
    229 			td->status = ret;
    230 			UNRESOLVED(ret, "[child] Failed to unlock the mutex");
    231 		}
    232 	} else {		/* This was not a recursive mutex; the call must have failed */
    233 
    234 		if (ret == 0) {
    235 			td->status = -1;
    236 			FAILED
    237 			    ("[child] Thread did not owned the mutex after the wait return.");
    238 		}
    239 		if (ret != EBUSY) {
    240 			td->status = ret;
    241 			UNRESOLVED(ret,
    242 				   "[child] Mutex trylock did not return EBUSY");
    243 		}
    244 #if VERBOSE > 1
    245 		output("[child] The mutex was busy (normal).\n");
    246 #endif
    247 	}
    248 
    249 	ret = pthread_mutex_unlock(&(td->mtx));
    250 	if (ret != 0) {
    251 		td->status = ret;
    252 		output("[child] Got error %i: %s\n", ret, strerror(ret));
    253 		FAILED
    254 		    ("[child] Failed to unlock the mutex - owned by another thread?");
    255 	}
    256 
    257 	td->ctrl = 4;
    258 	return NULL;
    259 }
    260 
    261 int main(void)
    262 {
    263 	int ret, i;
    264 	pthread_mutexattr_t ma;
    265 	pthread_condattr_t ca;
    266 
    267 	testdata_t *td;
    268 	testdata_t alternativ;
    269 
    270 	int do_fork;
    271 
    272 	pid_t child_pr = 0, chkpid;
    273 	int status;
    274 	pthread_t child_th;
    275 
    276 	long pshared, monotonic, cs, mf;
    277 
    278 	output_init();
    279 	pshared = sysconf(_SC_THREAD_PROCESS_SHARED);
    280 	cs = sysconf(_SC_CLOCK_SELECTION);
    281 	monotonic = sysconf(_SC_MONOTONIC_CLOCK);
    282 	mf = sysconf(_SC_MAPPED_FILES);
    283 
    284 #if VERBOSE > 0
    285 	output("Test starting\n");
    286 	output("System abilities:\n");
    287 	output(" TPS : %li\n", pshared);
    288 	output(" CS  : %li\n", cs);
    289 	output(" MON : %li\n", monotonic);
    290 	output(" MF  : %li\n", mf);
    291 	if ((mf < 0) || (pshared < 0))
    292 		output("Process-shared attributes won't be tested\n");
    293 	if ((cs < 0) || (monotonic < 0))
    294 		output("Alternative clock won't be tested\n");
    295 #endif
    296 
    297 	/* We are not interested in testing the clock if we have no other clock available.. */
    298 	if (monotonic < 0)
    299 		cs = -1;
    300 
    301 #ifndef USE_ALTCLK
    302 	if (cs > 0)
    303 		output
    304 		    ("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n");
    305 #endif
    306 
    307 /**********
    308  * Allocate space for the testdata structure
    309  */
    310 	if (mf < 0) {
    311 		/* Cannot mmap a file, we use an alternative method */
    312 		td = &alternativ;
    313 		pshared = -1;	/* We won't do this testing anyway */
    314 #if VERBOSE > 0
    315 		output("Testdata allocated in the process memory.\n");
    316 #endif
    317 	} else {
    318 		/* We will place the test data in a mmaped file */
    319 		char filename[] = "/tmp/cond_wait_2-2-XXXXXX";
    320 		size_t sz;
    321 		void *mmaped;
    322 		int fd;
    323 		char *tmp;
    324 
    325 		/* We now create the temp files */
    326 		fd = mkstemp(filename);
    327 		if (fd == -1) {
    328 			UNRESOLVED(errno,
    329 				   "Temporary file could not be created");
    330 		}
    331 
    332 		/* and make sure the file will be deleted when closed */
    333 		unlink(filename);
    334 
    335 #if VERBOSE > 1
    336 		output("Temp file created (%s).\n", filename);
    337 #endif
    338 
    339 		sz = (size_t) sysconf(_SC_PAGESIZE);
    340 
    341 		tmp = calloc(1, sz);
    342 		if (tmp == NULL) {
    343 			UNRESOLVED(errno, "Memory allocation failed");
    344 		}
    345 
    346 		/* Write the data to the file.  */
    347 		if (write(fd, tmp, sz) != (ssize_t) sz) {
    348 			UNRESOLVED(sz, "Writting to the file failed");
    349 		}
    350 
    351 		free(tmp);
    352 
    353 		/* Now we can map the file in memory */
    354 		mmaped =
    355 		    mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    356 		if (mmaped == MAP_FAILED) {
    357 			UNRESOLVED(errno, "mmap failed");
    358 		}
    359 
    360 		td = (testdata_t *) mmaped;
    361 
    362 		/* Our datatest structure is now in shared memory */
    363 #if VERBOSE > 1
    364 		output("Testdata allocated in shared memory.\n");
    365 #endif
    366 	}
    367 
    368 /**********
    369  * For each test scenario, initialize the attributes and other variables.
    370  */
    371 	for (i = 0; i < (sizeof(scenarii) / sizeof(scenarii[0])); i++) {
    372 #if VERBOSE > 1
    373 		output("[parent] Preparing attributes for: %s\n",
    374 		       scenarii[i].descr);
    375 #endif
    376 		/* set / reset everything */
    377 		do_fork = 0;
    378 		ret = pthread_mutexattr_init(&ma);
    379 		if (ret != 0) {
    380 			UNRESOLVED(ret,
    381 				   "[parent] Unable to initialize the mutex attribute object");
    382 		}
    383 		ret = pthread_condattr_init(&ca);
    384 		if (ret != 0) {
    385 			UNRESOLVED(ret,
    386 				   "[parent] Unable to initialize the cond attribute object");
    387 		}
    388 
    389 		/* Set the mutex type */
    390 		ret = pthread_mutexattr_settype(&ma, scenarii[i].m_type);
    391 		if (ret != 0) {
    392 			UNRESOLVED(ret, "[parent] Unable to set mutex type");
    393 		}
    394 #if VERBOSE > 1
    395 		output("[parent] Mutex type : %i\n", scenarii[i].m_type);
    396 #endif
    397 
    398 		/* Set the pshared attributes, if supported */
    399 		if ((pshared > 0) && (scenarii[i].mc_pshared != 0)) {
    400 			ret =
    401 			    pthread_mutexattr_setpshared(&ma,
    402 							 PTHREAD_PROCESS_SHARED);
    403 			if (ret != 0) {
    404 				UNRESOLVED(ret,
    405 					   "[parent] Unable to set the mutex process-shared");
    406 			}
    407 			ret =
    408 			    pthread_condattr_setpshared(&ca,
    409 							PTHREAD_PROCESS_SHARED);
    410 			if (ret != 0) {
    411 				UNRESOLVED(ret,
    412 					   "[parent] Unable to set the cond var process-shared");
    413 			}
    414 #if VERBOSE > 1
    415 			output("[parent] Mutex & cond are process-shared\n");
    416 #endif
    417 		}
    418 #if VERBOSE > 1
    419 		else {
    420 			output("[parent] Mutex & cond are process-private\n");
    421 		}
    422 #endif
    423 
    424 		/* Set the alternative clock, if supported */
    425 #ifdef USE_ALTCLK
    426 		if ((cs > 0) && (scenarii[i].c_clock != 0)) {
    427 			ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC);
    428 			if (ret != 0) {
    429 				UNRESOLVED(ret,
    430 					   "[parent] Unable to set the monotonic clock for the cond");
    431 			}
    432 #if VERBOSE > 1
    433 			output("[parent] Cond uses the Monotonic clock\n");
    434 #endif
    435 		}
    436 #if VERBOSE > 1
    437 		else {
    438 			output("[parent] Cond uses the default clock\n");
    439 		}
    440 #endif
    441 #endif
    442 
    443 		/* Tell whether the test will be across processes */
    444 		if ((pshared > 0) && (scenarii[i].fork != 0)) {
    445 			do_fork = 1;
    446 #if VERBOSE > 1
    447 			output("[parent] Child will be a new process\n");
    448 #endif
    449 		}
    450 #if VERBOSE > 1
    451 		else {
    452 			output("[parent] Child will be a new thread\n");
    453 		}
    454 #endif
    455 
    456 /**********
    457  * Initialize the testdata_t structure with the previously defined attributes
    458  */
    459 		/* Initialize the mutex */
    460 		ret = pthread_mutex_init(&(td->mtx), &ma);
    461 		if (ret != 0) {
    462 			UNRESOLVED(ret, "[parent] Mutex init failed");
    463 		}
    464 
    465 		/* initialize the condvar */
    466 		ret = pthread_cond_init(&(td->cnd), &ca);
    467 		if (ret != 0) {
    468 			UNRESOLVED(ret, "[parent] Cond init failed");
    469 		}
    470 
    471 		/* Initialize the other datas from the test structure */
    472 #ifdef USE_ALTCLK
    473 		ret = pthread_condattr_getclock(&ca, &(td->cid));
    474 		if (ret != 0) {
    475 			UNRESOLVED(ret,
    476 				   "[parent] Unable to read cond clock attribute");
    477 		}
    478 #else
    479 		td->cid = CLOCK_REALTIME;
    480 #endif
    481 
    482 		ret = pthread_mutexattr_gettype(&ma, &(td->type));
    483 		if (ret != 0) {
    484 			UNRESOLVED(ret,
    485 				   "[parent] Unable to read mutex type attribute");
    486 		}
    487 
    488 		td->ctrl = 0;
    489 		td->bool = 0;
    490 		td->status = 0;
    491 
    492 /**********
    493  * Proceed to the actual testing
    494  */
    495 
    496 		/* Create the child */
    497 		if (do_fork != 0) {
    498 			/* We are testing across two processes */
    499 			child_pr = fork();
    500 			if (child_pr == -1) {
    501 				UNRESOLVED(errno, "[parent] Fork failed");
    502 			}
    503 
    504 			if (child_pr == 0) {
    505 #if VERBOSE > 1
    506 				output("[child] Child process starting...\n");
    507 #endif
    508 
    509 				if (tf((void *)td) != NULL) {
    510 					UNRESOLVED(-1,
    511 						   "[child] Got an unexpected return value from test function");
    512 				} else {
    513 					/* We cannot use the PASSED macro here since it would terminate the output */
    514 					exit(0);
    515 				}
    516 			}
    517 			/* Only the parent process goes further */
    518 		} else {	/* do_fork == 0 */
    519 
    520 			/* We are testing across two threads */
    521 			ret = pthread_create(&child_th, NULL, tf, td);
    522 			if (ret != 0) {
    523 				UNRESOLVED(ret,
    524 					   "[parent] Unable to create the child thread.");
    525 			}
    526 		}
    527 
    528 		/* Note: in case of an error, the child process will be alive for 10 sec then exit. */
    529 
    530 		/* Child is now running and will enter the timedwait */
    531 		/* We are waiting for this; and we have to monitor the status value as well. */
    532 		ret = pthread_mutex_lock(&(td->mtx));
    533 		if (ret != 0) {
    534 			UNRESOLVED(ret, "[parent] Unable to lock the mutex");
    535 		}
    536 
    537 		while ((td->ctrl == 0) && (td->status == 0)) {
    538 			ret = pthread_mutex_unlock(&(td->mtx));
    539 			if (ret != 0) {
    540 				UNRESOLVED(ret,
    541 					   "[parent] Unable to unlock the mutex");
    542 			}
    543 			sched_yield();
    544 			ret = pthread_mutex_lock(&(td->mtx));
    545 			if (ret != 0) {
    546 				UNRESOLVED(ret,
    547 					   "[parent] Unable to lock the mutex");
    548 			}
    549 		}
    550 
    551 		if ((td->ctrl == 2) && (td->status == 0)) {	/* Spurious wakeups hapenned */
    552 			output
    553 			    ("Spurious wake ups have happened. Maybe pthread_cond_wait is broken?\n");
    554 			td->ctrl = 1;
    555 		}
    556 
    557 		if (td->ctrl == 1) {	/* The child is inside the cond wait */
    558 			ret = pthread_cond_signal(&(td->cnd));
    559 			if (ret != 0) {
    560 				UNRESOLVED(ret,
    561 					   "[parent] Unable to signal the condition");
    562 			}
    563 
    564 			/* Let the child leave the wait function if something is broken */
    565 			usleep(100);
    566 
    567 			if (td->ctrl != 1) {
    568 				FAILED
    569 				    ("[parent] Child went out from pthread_cond_wait without locking the mutex");
    570 			}
    571 
    572 			/* Allow the child to continue */
    573 			td->bool = 1;
    574 		}
    575 
    576 		/* Let the child do its checking */
    577 		ret = pthread_mutex_unlock(&(td->mtx));
    578 		if (ret != 0) {
    579 			UNRESOLVED(ret, "[parent] Unable to unlock the mutex");
    580 		}
    581 
    582 		/* Wait for the child to terminate */
    583 		if (do_fork != 0) {
    584 			/* We were testing across two processes */
    585 			chkpid = waitpid(child_pr, &status, 0);
    586 			if (chkpid != child_pr) {
    587 				output("Expected pid: %i. Got %i\n",
    588 				       (int)child_pr, (int)chkpid);
    589 				UNRESOLVED(errno, "Waitpid failed");
    590 			}
    591 			if (WIFSIGNALED(status)) {
    592 				output("Child process killed with signal %d\n",
    593 				       WTERMSIG(status));
    594 				UNRESOLVED(td->status,
    595 					   "Child process was killed");
    596 			}
    597 
    598 			if (WIFEXITED(status)) {
    599 				ret = WEXITSTATUS(status);
    600 			} else {
    601 				UNRESOLVED(td->status,
    602 					   "Child process was neither killed nor exited");
    603 			}
    604 
    605 			if (ret != 0) {
    606 				exit(ret);	/* Output has already been closed in child */
    607 			}
    608 		} else {	/* child was a thread */
    609 
    610 			ret = pthread_join(child_th, NULL);
    611 			if (ret != 0) {
    612 				UNRESOLVED(ret,
    613 					   "[parent] Unable to join the thread");
    614 			}
    615 		}
    616 
    617 /**********
    618  * Destroy the data
    619  */
    620 		ret = pthread_cond_destroy(&(td->cnd));
    621 		if (ret != 0) {
    622 			UNRESOLVED(ret, "Failed to destroy the cond var");
    623 		}
    624 
    625 		ret = pthread_mutex_destroy(&(td->mtx));
    626 		if (ret != 0) {
    627 			UNRESOLVED(ret, "Failed to destroy the mutex");
    628 		}
    629 
    630 		ret = pthread_condattr_destroy(&ca);
    631 		if (ret != 0) {
    632 			UNRESOLVED(ret,
    633 				   "Failed to destroy the cond var attribute object");
    634 		}
    635 
    636 		ret = pthread_mutexattr_destroy(&ma);
    637 		if (ret != 0) {
    638 			UNRESOLVED(ret,
    639 				   "Failed to destroy the mutex attribute object");
    640 		}
    641 
    642 	}			/* Proceed to the next scenario */
    643 
    644 #if VERBOSE > 0
    645 	output("Test passed\n");
    646 #endif
    647 
    648 	PASSED;
    649 }
    650 
    651 #else /* WITHOUT_XOPEN */
    652 int main(void)
    653 {
    654 	output_init();
    655 	UNTESTED("This test requires XSI features");
    656 }
    657 #endif
    658