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 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