• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 tests the following assertion:
18  *
19  * The pthread_cond_broadcast function unblocks all the threads blocked on the
20  * conditional variable.
21 
22  * The steps are:
23  *  -> Create as many threads as possible which will wait on a condition
24  *     variable
25  *  -> broadcast the condition and check that all threads are awaken
26  *
27  *  The test will fail when the threads are not terminated within a certain
28  *  duration.
29  *
30  */
31 
32 #include <pthread.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 
38 #include <errno.h>
39 #include <signal.h>
40 #include <string.h>
41 #include <time.h>
42 #include <sys/mman.h>
43 #include <sys/wait.h>
44 #include <semaphore.h>
45 #ifdef	__linux__
46 #include <sys/sysinfo.h>
47 #endif
48 
49 #include "../testfrmw/testfrmw.h"
50 #include "../testfrmw/testfrmw.c"
51 
52 #define UNRESOLVED_KILLALL(error, text) {		\
53 	if (td->fork) {					\
54 		children_t *tmp;			\
55 		while (children->next != NULL) {	\
56 			tmp = children->next;		\
57 			children->next = tmp->next;	\
58 			kill(tmp->data.p, SIGKILL);	\
59 			free(tmp);			\
60 		}					\
61 	}						\
62 	UNRESOLVED(error, text);			\
63 	}
64 #define FAILED_KILLALL(text) {				\
65 	if (td->fork) {					\
66 		children_t *tmp;			\
67 		while (children->next != NULL) {	\
68 			tmp = children->next;		\
69 			children->next = tmp->next;	\
70 			kill(tmp->data.p, SIGKILL);	\
71 			free(tmp);			\
72 		}					\
73 	}						\
74 	FAILED(text);					\
75 	}
76 
77 #ifndef VERBOSE
78 #define VERBOSE 1
79 #endif
80 
81 /* Do not create more than this amount of children: */
82 static int max_process_children = 200;
83 static int max_thread_children = 1000;
84 
85 #define TIMEOUT  (180)
86 
87 #ifndef WITHOUT_ALTCLK
88 #define USE_ALTCLK		/* make tests with MONOTONIC CLOCK if supported */
89 #endif
90 
91 #ifdef WITHOUT_XOPEN
92 /* We define those to avoid compilation errors, but they won't be used */
93 #define PTHREAD_MUTEX_DEFAULT 0
94 #define PTHREAD_MUTEX_NORMAL 0
95 #define PTHREAD_MUTEX_ERRORCHECK 0
96 #define PTHREAD_MUTEX_RECURSIVE 0
97 
98 #endif
99 
100 static struct _scenar {
101 	int m_type;
102 	int mc_pshared;
103 	int c_clock;
104 	int fork;
105 	char *descr;
106 } scenarii[] = {
107 	{
108 	PTHREAD_MUTEX_DEFAULT, 0, 0, 0, "Default mutex"}, {
109 	PTHREAD_MUTEX_NORMAL, 0, 0, 0, "Normal mutex"}, {
110 	PTHREAD_MUTEX_ERRORCHECK, 0, 0, 0, "Errorcheck mutex"}, {
111 	PTHREAD_MUTEX_RECURSIVE, 0, 0, 0, "Recursive mutex"}, {
112 	PTHREAD_MUTEX_DEFAULT, 1, 0, 0, "PShared default mutex"}, {
113 	PTHREAD_MUTEX_NORMAL, 1, 0, 0, "Pshared normal mutex"}, {
114 	PTHREAD_MUTEX_ERRORCHECK, 1, 0, 0, "Pshared errorcheck mutex"}, {
115 	PTHREAD_MUTEX_RECURSIVE, 1, 0, 0, "Pshared recursive mutex"}, {
116 	PTHREAD_MUTEX_DEFAULT, 1, 0, 1,
117 		    "Pshared default mutex across processes"}, {
118 	PTHREAD_MUTEX_NORMAL, 1, 0, 1, "Pshared normal mutex across processes"},
119 	{
120 	PTHREAD_MUTEX_ERRORCHECK, 1, 0, 1,
121 		    "Pshared errorcheck mutex across processes"}, {
122 	PTHREAD_MUTEX_RECURSIVE, 1, 0, 1,
123 		    "Pshared recursive mutex across processes"},
124 #ifdef USE_ALTCLK
125 	{
126 	PTHREAD_MUTEX_DEFAULT, 1, 1, 1,
127 		    "Pshared default mutex and alt clock condvar across processes"},
128 	{
129 	PTHREAD_MUTEX_NORMAL, 1, 1, 1,
130 		    "Pshared normal mutex and alt clock condvar across processes"},
131 	{
132 	PTHREAD_MUTEX_ERRORCHECK, 1, 1, 1,
133 		    "Pshared errorcheck mutex and alt clock condvar across processes"},
134 	{
135 	PTHREAD_MUTEX_RECURSIVE, 1, 1, 1,
136 		    "Pshared recursive mutex and alt clock condvar across processes"},
137 	{
138 	PTHREAD_MUTEX_DEFAULT, 0, 1, 0, "Default mutex and alt clock condvar"},
139 	{
140 	PTHREAD_MUTEX_NORMAL, 0, 1, 0, "Normal mutex and alt clock condvar"},
141 	{
142 	PTHREAD_MUTEX_ERRORCHECK, 0, 1, 0,
143 		    "Errorcheck mutex and alt clock condvar"}, {
144 	PTHREAD_MUTEX_RECURSIVE, 0, 1, 0,
145 		    "Recursive mutex and alt clock condvar"}, {
146 	PTHREAD_MUTEX_DEFAULT, 1, 1, 0,
147 		    "PShared default mutex and alt clock condvar"}, {
148 	PTHREAD_MUTEX_NORMAL, 1, 1, 0,
149 		    "Pshared normal mutex and alt clock condvar"}, {
150 	PTHREAD_MUTEX_ERRORCHECK, 1, 1, 0,
151 		    "Pshared errorcheck mutex and alt clock condvar"}, {
152 	PTHREAD_MUTEX_RECURSIVE, 1, 1, 0,
153 		    "Pshared recursive mutex and alt clock condvar"},
154 #endif
155 };
156 
157 #define NSCENAR (sizeof(scenarii)/sizeof(scenarii[0]))
158 
159 struct testdata {
160 	int count;		/* number of children currently waiting */
161 	pthread_cond_t cnd;
162 	pthread_mutex_t mtx;
163 	int predicate;		/* Boolean associated to the condvar */
164 	clockid_t cid;		/* clock used in the condvar */
165 	char fork;		/* the children are processes */
166 };
167 static struct testdata *td;
168 
169 /* Child function (either in a thread or in a process) */
child(void * arg PTS_ATTRIBUTE_UNUSED)170 static void *child(void *arg PTS_ATTRIBUTE_UNUSED)
171 {
172 	int ret = 0;
173 	struct timespec ts;
174 	char timed;
175 
176 	/* lock the mutex */
177 	ret = pthread_mutex_lock(&td->mtx);
178 	if (ret != 0)
179 		UNRESOLVED(ret, "Failed to lock mutex in child");
180 
181 	/* increment count */
182 	td->count++;
183 
184 	timed = td->count & 1;
185 
186 	if (timed) {
187 		/* get current time if we are a timedwait */
188 		ret = clock_gettime(td->cid, &ts);
189 		if (ret != 0)
190 			UNRESOLVED(errno, "Unable to read clock");
191 		ts.tv_sec += TIMEOUT;
192 	}
193 
194 	do {
195 		/* Wait while the predicate is false */
196 		if (timed)
197 			ret = pthread_cond_timedwait(&td->cnd, &td->mtx, &ts);
198 		else
199 			ret = pthread_cond_wait(&td->cnd, &td->mtx);
200 #if VERBOSE > 5
201 		output("[child] Wokenup timed=%i, Predicate=%i, ret=%i\n",
202 		       timed, td->predicate, ret);
203 #endif
204 	} while ((ret == 0) && (td->predicate == 0));
205 	if (ret == ETIMEDOUT) {
206 		ret = pthread_mutex_unlock(&td->mtx);
207 		if (ret != 0)
208 			UNRESOLVED(ret, "Failed to unlock the mutex.");
209 		FAILED("Timeout occured. This means a cond signal was lost -- "
210 		       "or parent died");
211 	}
212 	if (ret != 0)
213 		UNRESOLVED(ret, "Failed to wait for the cond");
214 
215 	/* unlock the mutex */
216 	ret = pthread_mutex_unlock(&td->mtx);
217 	if (ret != 0)
218 		UNRESOLVED(ret, "Failed to unlock the mutex.");
219 
220 	return NULL;
221 }
222 
223 /* Structure used to store the children information */
224 typedef struct _children {
225 	union {
226 		pid_t p;
227 		pthread_t t;
228 	} data;
229 	struct _children *next;
230 } children_t;
231 
232 static children_t sentinel = {.next = NULL };
233 
234 static children_t *children = &sentinel;
235 
236 /* Timeout thread */
237 
238 static sem_t sem_tmr;
239 
timer(void * arg PTS_ATTRIBUTE_UNUSED)240 static void *timer(void *arg PTS_ATTRIBUTE_UNUSED)
241 {
242 	unsigned int to = TIMEOUT;
243 	int ret;
244 
245 	do {
246 		ret = sem_wait(&sem_tmr);
247 	} while ((ret != 0) && (errno == EINTR));
248 	if (ret != 0)
249 		UNRESOLVED(errno, "Failed to wait for the semaphore "
250 			   "in timer thread.");
251 
252 	do {
253 		to = sleep(to);
254 	} while (to > 0);
255 	FAILED_KILLALL("Operation timed out. A signal was lost.");
256 	return NULL;		/* For compiler */
257 }
258 
259 #ifdef __linux__
children_number(void)260 static void children_number(void)
261 {
262 	struct sysinfo sysinformation;
263 	int ret;
264 	int avail_number;
265 	unsigned long per_process;
266 	unsigned long min_stack;
267 
268 	min_stack = sysconf(_SC_THREAD_STACK_MIN);
269 
270 	ret = sysinfo(&sysinformation);
271 	if (ret != 0)
272 		UNRESOLVED(ret, "Failed to get system information.");
273 
274 	per_process = min_stack * max_thread_children;
275 	if (per_process > sysinformation.freeram)
276 		UNTESTED("Not enough memory.");
277 
278 	avail_number = sysinformation.freeram / per_process;
279 
280 	if (avail_number < 10)
281 		UNTESTED("Not enough memory.");
282 
283 	max_process_children = (avail_number < max_process_children ?
284 				avail_number : max_process_children);
285 
286 	return;
287 }
288 #else
children_number(void)289 static void children_number(void)
290 {
291 	return;
292 }
293 #endif
294 
main(void)295 int main(void)
296 {
297 	int ret;
298 
299 	pthread_mutexattr_t ma;
300 	pthread_condattr_t ca;
301 
302 	int scenar;
303 	long pshared, monotonic, cs, mf;
304 
305 	int child_count;
306 	children_t *tmp, *cur;
307 
308 	int ch;
309 	pid_t pid;
310 	int status;
311 
312 	pthread_t t_timer;
313 
314 	pthread_attr_t ta;
315 
316 	struct testdata alternativ;
317 
318 	output_init();
319 
320 	children_number();
321 
322 	/* check the system abilities */
323 	pshared = sysconf(_SC_THREAD_PROCESS_SHARED);
324 	cs = sysconf(_SC_CLOCK_SELECTION);
325 	monotonic = sysconf(_SC_MONOTONIC_CLOCK);
326 	mf = sysconf(_SC_MAPPED_FILES);
327 
328 #if VERBOSE > 0
329 	output("Test starting\n");
330 	output("System abilities:\n");
331 	output(" TPS : %li\n", pshared);
332 	output(" CS  : %li\n", cs);
333 	output(" MON : %li\n", monotonic);
334 	output(" MF  : %li\n", mf);
335 	if ((mf < 0) || (pshared < 0))
336 		output("Process-shared attributes won't be tested\n");
337 	if ((cs < 0) || (monotonic < 0))
338 		output("Alternative clock won't be tested\n");
339 	fflush(stdout);
340 #endif
341 
342 	if (monotonic < 0)
343 		cs = -1;
344 
345 #ifndef USE_ALTCLK
346 	if (cs > 0)
347 		output("Implementation supports the MONOTONIC CLOCK "
348 		       "but option is disabled in test.\n");
349 #endif
350 
351 /*
352  * Allocate space for the testdata structure
353  */
354 	if (mf < 0) {
355 		/* Cannot mmap a file, we use an alternative method */
356 		td = &alternativ;
357 		pshared = -1;	/* We won't do this testing anyway */
358 #if VERBOSE > 0
359 		output("Testdata allocated in the process memory.\n");
360 #endif
361 	} else {
362 		/* We will place the test data in a mmaped file */
363 		char filename[] = "/tmp/cond_broadcast-XXXXXX";
364 		size_t sz, ps;
365 		void *mmaped;
366 		int fd;
367 		char *tmp;
368 
369 		/* We now create the temp files */
370 		fd = mkstemp(filename);
371 		if (fd == -1)
372 			UNRESOLVED(errno,
373 				   "Temporary file could not be created");
374 
375 		/* and make sure the file will be deleted when closed */
376 		unlink(filename);
377 
378 #if VERBOSE > 1
379 		output("Temp file created (%s).\n", filename);
380 #endif
381 
382 		ps = (size_t) sysconf(_SC_PAGESIZE);
383 		sz = ((sizeof(struct testdata) / ps) + 1) * ps;
384 
385 		tmp = calloc(1, sz);
386 		if (tmp == NULL)
387 			UNRESOLVED(errno, "Memory allocation failed");
388 
389 		/* Write the data to the file.  */
390 		if (write(fd, tmp, sz) != (ssize_t) sz)
391 			UNRESOLVED(sz, "Writting to the file failed");
392 
393 		free(tmp);
394 
395 		/* Now we can map the file in memory */
396 		mmaped = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED,
397 			      fd, 0);
398 		if (mmaped == MAP_FAILED)
399 			UNRESOLVED(errno, "mmap failed");
400 
401 		td = (struct testdata *)mmaped;
402 
403 		/* Our datatest structure is now in shared memory */
404 #if VERBOSE > 1
405 		output("Testdata allocated in shared memory (%ib).\n",
406 		       sizeof(struct testdata));
407 #endif
408 	}
409 
410 	/* Initialize the thread attribute object */
411 	ret = pthread_attr_init(&ta);
412 	if (ret != 0)
413 		UNRESOLVED(ret, "[parent] Failed to initialize a thread "
414 			   "attribute object");
415 	ret = pthread_attr_setstacksize(&ta, sysconf(_SC_THREAD_STACK_MIN));
416 	if (ret != 0)
417 		UNRESOLVED(ret, "[parent] Failed to set thread stack size");
418 
419 	/* Initialize the semaphore for the timer thread */
420 	ret = sem_init(&sem_tmr, 0, 0);
421 	if (ret != 0)
422 		UNRESOLVED(ret, "Failed to initialize the semaphore");
423 
424 	/* Do the test for each test scenario */
425 	for (scenar = 0; scenar < (int)NSCENAR; scenar++) {
426 		/* set / reset everything */
427 		td->fork = 0;
428 		ret = pthread_mutexattr_init(&ma);
429 		if (ret != 0)
430 			UNRESOLVED(ret, "[parent] Unable to initialize the "
431 				   "mutex attribute object");
432 		ret = pthread_condattr_init(&ca);
433 		if (ret != 0)
434 			UNRESOLVED(ret, "[parent] Unable to initialize the "
435 				   "cond attribute object");
436 
437 #ifndef WITHOUT_XOPEN
438 		/* Set the mutex type */
439 		ret = pthread_mutexattr_settype(&ma, scenarii[scenar].m_type);
440 		if (ret != 0)
441 			UNRESOLVED(ret, "[parent] Unable to set mutex type");
442 #endif
443 
444 		/* Set the pshared attributes, if supported */
445 		if ((pshared > 0) && (scenarii[scenar].mc_pshared != 0)) {
446 			ret = pthread_mutexattr_setpshared(&ma,
447 							   PTHREAD_PROCESS_SHARED);
448 			if (ret != 0)
449 				UNRESOLVED(ret, "[parent] Unable to set the "
450 					   "mutex process-shared");
451 			ret = pthread_condattr_setpshared(&ca,
452 							  PTHREAD_PROCESS_SHARED);
453 			if (ret != 0)
454 				UNRESOLVED(ret, "[parent] Unable to set the "
455 					   "cond var process-shared");
456 		}
457 
458 		/* Set the alternative clock, if supported */
459 #ifdef USE_ALTCLK
460 		if ((cs > 0) && (scenarii[scenar].c_clock != 0)) {
461 			ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC);
462 			if (ret != 0)
463 				UNRESOLVED(ret, "[parent] Unable to set the "
464 					   "monotonic clock for the cond");
465 		}
466 		ret = pthread_condattr_getclock(&ca, &td->cid);
467 		if (ret != 0)
468 			UNRESOLVED(ret, "Unable to get clock from cond attr");
469 #else
470 		td->cid = CLOCK_REALTIME;
471 #endif
472 
473 		/* Tell whether the test will be across processes */
474 		if ((pshared > 0) && (scenarii[scenar].fork != 0))
475 			td->fork = 1;
476 
477 		/* initialize the condvar */
478 		ret = pthread_cond_init(&td->cnd, &ca);
479 		if (ret != 0)
480 			UNRESOLVED(ret, "Cond init failed");
481 
482 		/* initialize the mutex */
483 		ret = pthread_mutex_init(&td->mtx, &ma);
484 		if (ret != 0)
485 			UNRESOLVED(ret, "Mutex init failed");
486 
487 		/* Destroy the attributes */
488 		ret = pthread_condattr_destroy(&ca);
489 		if (ret != 0)
490 			UNRESOLVED(ret,
491 				   "Failed to destroy the cond var "
492 				   "attribute object");
493 
494 		ret = pthread_mutexattr_destroy(&ma);
495 		if (ret != 0)
496 			UNRESOLVED(ret,
497 				   "Failed to destroy the mutex "
498 				   "attribute object");
499 
500 #if VERBOSE > 2
501 		output("[parent] Starting test %s\n", scenarii[scenar].descr);
502 #endif
503 
504 		td->count = 0;
505 		child_count = 0;
506 		cur = children;
507 
508 		/* create the timeout thread */
509 		ret = pthread_create(&t_timer, NULL, timer, NULL);
510 		if (ret != 0)
511 			UNRESOLVED_KILLALL(ret,
512 					   "Unable to create timer thread");
513 
514 		/* Create all the children */
515 		if (td->fork == 0) {
516 			do {
517 				tmp = malloc(sizeof(children_t));
518 				if (tmp != NULL) {
519 					ret = pthread_create(&(tmp->data.t),
520 							     &ta, child, NULL);
521 					if (ret != 0) {
522 						free(tmp);
523 					} else {
524 						child_count++;
525 						tmp->next = NULL;
526 						cur->next = tmp;
527 						cur = tmp;
528 					}
529 				} else {
530 					ret = errno;
531 				}
532 			} while ((ret == 0)
533 				 && (child_count < max_thread_children));
534 #if VERBOSE > 2
535 			output("[parent] Created %i children threads\n",
536 			       child_count);
537 #endif
538 			if (child_count == 0)
539 				UNRESOLVED(ret, "Unable to create any thread");
540 		} else {
541 			do {
542 				tmp = malloc(sizeof(children_t));
543 				if (tmp != NULL) {
544 					tmp->data.p = fork();
545 					if (tmp->data.p == 0) {
546 						child(NULL);
547 						exit(0);
548 					}
549 					if (tmp->data.p == -1) {
550 						ret = errno;
551 						free(tmp);
552 					} else {
553 						ret = 0;
554 						child_count++;
555 						tmp->next = NULL;
556 						cur->next = tmp;
557 						cur = tmp;
558 					}
559 				} else {
560 					ret = errno;
561 				}
562 			} while ((ret == 0)
563 				 && (child_count < max_process_children));
564 #if VERBOSE > 2
565 			output("[parent] Created %i children processes\n",
566 			       child_count);
567 #endif
568 			if (child_count == 0)
569 				UNRESOLVED(ret, "Unable to create any process");
570 
571 		}
572 
573 		/* Make sure all children are waiting */
574 		ret = pthread_mutex_lock(&td->mtx);
575 		if (ret != 0)
576 			UNRESOLVED_KILLALL(ret, "Failed to lock mutex");
577 		ch = td->count;
578 		while (ch < child_count) {
579 			ret = pthread_mutex_unlock(&td->mtx);
580 			if (ret != 0)
581 				UNRESOLVED_KILLALL(ret,
582 						   "Failed to unlock mutex");
583 			sched_yield();
584 			ret = pthread_mutex_lock(&td->mtx);
585 			if (ret != 0)
586 				UNRESOLVED_KILLALL(ret, "Failed to lock mutex");
587 			ch = td->count;
588 		}
589 
590 #if VERBOSE > 4
591 		output("[parent] All children are waiting\n");
592 #endif
593 
594 		/* start the timer count */
595 		do {
596 			ret = sem_post(&sem_tmr);
597 		} while ((ret != 0) && (errno == EINTR));
598 		if (ret != 0)
599 			UNRESOLVED_KILLALL(errno,
600 					   "Failed to post the semaphore.");
601 
602 		/* Wakeup the children */
603 		td->predicate = 1;
604 		ret = pthread_cond_broadcast(&td->cnd);
605 		if (ret != 0)
606 			UNRESOLVED_KILLALL(ret,
607 					   "Failed to broadcast the condition.");
608 
609 #if VERBOSE > 4
610 		output("[parent] Condition was signaled\n");
611 #endif
612 
613 		ret = pthread_mutex_unlock(&td->mtx);
614 		if (ret != 0)
615 			UNRESOLVED_KILLALL(ret, "Failed to unlock mutex");
616 
617 #if VERBOSE > 4
618 		output("[parent] Joining the children\n");
619 #endif
620 
621 		/* join the children */
622 		while (children->next != NULL) {
623 			tmp = children->next;
624 			children->next = tmp->next;
625 			if (td->fork == 0) {
626 				ret |= pthread_join(tmp->data.t, NULL);
627 			} else {
628 				pid = waitpid(tmp->data.p, &status, 0);
629 				if (pid != tmp->data.p) {
630 					ret = errno;
631 					output("Waitpid failed (expected: %i, "
632 					       "got: %i)\n", tmp->data.p, pid);
633 					free(tmp);
634 					UNRESOLVED_KILLALL(ret,
635 							   "Waitpid failed");
636 				}
637 				if (WIFEXITED(status)) {
638 					/* the child should return only failed or unresolved or passed */
639 					if (ret != PTS_FAIL)
640 						ret |= WEXITSTATUS(status);
641 				}
642 			}
643 			free(tmp);
644 		}
645 		if (ret != 0) {
646 			output_fini();
647 			exit(ret);
648 		}
649 #if VERBOSE > 4
650 		output("[parent] All children terminated\n");
651 #endif
652 
653 		/* cancel the timeout thread */
654 		ret = pthread_cancel(t_timer);
655 		if (ret != 0)
656 			/* Strange error here... the thread cannot be terminated
657 			 * (app would be killed)
658 			 */
659 			UNRESOLVED(ret, "Failed to cancel the timeout handler");
660 
661 		/* join the timeout thread */
662 		ret = pthread_join(t_timer, NULL);
663 		if (ret != 0)
664 			UNRESOLVED(ret, "Failed to join the timeout handler");
665 
666 		/* Destroy the datas */
667 		ret = pthread_cond_destroy(&td->cnd);
668 		if (ret != 0)
669 			UNRESOLVED(ret, "Failed to destroy the condvar");
670 
671 		ret = pthread_mutex_destroy(&td->mtx);
672 		if (ret != 0)
673 			UNRESOLVED(ret, "Failed to destroy the mutex");
674 
675 	}
676 
677 	/* Destroy global data */
678 	ret = pthread_attr_destroy(&ta);
679 	if (ret != 0)
680 		UNRESOLVED(ret, "Final thread attr destroy failed");
681 
682 	ret = sem_destroy(&sem_tmr);
683 	if (ret != 0)
684 		UNRESOLVED(errno, "Final semaphore destroy failed");
685 
686 	/* exit */
687 	PASSED;
688 }
689