1/*	$NetBSD: t_threads.c,v 1.9 2017/01/13 05:18:22 christos Exp $	*/
2
3/*-
4 * Copyright (c) 2016 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29
30#include <sys/cdefs.h>
31__RCSID("$NetBSD: t_threads.c,v 1.9 2017/01/13 05:18:22 christos Exp $");
32
33#include <dlfcn.h>
34#include <pthread.h>
35#include <pthread_dbg.h>
36#include <stdio.h>
37#include <string.h>
38#include <unistd.h>
39
40#include <atf-c.h>
41
42#include "h_common.h"
43
44#define MAX_THREADS (size_t)10
45
46ATF_TC(threads1);
47ATF_TC_HEAD(threads1, tc)
48{
49
50	atf_tc_set_md_var(tc, "descr",
51	    "Asserts that td_thr_iter() call without extra logic works");
52}
53
54static volatile int exiting1;
55
56static void *
57busyFunction1(void *arg)
58{
59
60	while (exiting1 == 0)
61		usleep(50000);
62
63	return NULL;
64}
65
66static int
67iterateThreads1(td_thread_t *thread, void *arg)
68{
69
70	return TD_ERR_OK;
71}
72
73ATF_TC_BODY(threads1, tc)
74{
75	struct td_proc_callbacks_t dummy_callbacks;
76	td_proc_t *main_ta;
77	size_t i;
78	pthread_t threads[MAX_THREADS];
79
80	dummy_callbacks.proc_read	= basic_proc_read;
81	dummy_callbacks.proc_write	= basic_proc_write;
82	dummy_callbacks.proc_lookup	= basic_proc_lookup;
83	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
84	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
85	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
86
87	for (i = 0; i < MAX_THREADS; i++) {
88		printf("Creating thread %zu\n", i);
89		PTHREAD_REQUIRE
90		    (pthread_create(&threads[i], NULL, busyFunction1, NULL));
91	}
92
93	printf("Calling td_open(3)\n");
94	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
95
96	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads1, NULL) == TD_ERR_OK);
97
98	exiting1 = 1;
99
100	printf("Calling td_close(3)\n");
101	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
102}
103
104ATF_TC(threads2);
105ATF_TC_HEAD(threads2, tc)
106{
107
108	atf_tc_set_md_var(tc, "descr",
109	    "Asserts that td_thr_iter() call is executed for each thread once");
110}
111
112static volatile int exiting2;
113
114static void *
115busyFunction2(void *arg)
116{
117
118	while (exiting2 == 0)
119		usleep(50000);
120
121	return NULL;
122}
123
124static int
125iterateThreads2(td_thread_t *thread, void *arg)
126{
127	int *counter = (int *)arg;
128
129	++(*counter);
130
131	return TD_ERR_OK;
132}
133
134ATF_TC_BODY(threads2, tc)
135{
136	struct td_proc_callbacks_t dummy_callbacks;
137	td_proc_t *main_ta;
138	size_t i;
139	pthread_t threads[MAX_THREADS];
140	int count = 0;
141
142	dummy_callbacks.proc_read	= basic_proc_read;
143	dummy_callbacks.proc_write	= basic_proc_write;
144	dummy_callbacks.proc_lookup	= basic_proc_lookup;
145	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
146	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
147	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
148
149
150	for (i = 0; i < MAX_THREADS; i++) {
151		printf("Creating thread %zu\n", i);
152		PTHREAD_REQUIRE
153		    (pthread_create(&threads[i], NULL, busyFunction2, NULL));
154	}
155
156	printf("Calling td_open(3)\n");
157	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
158
159	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads2, &count) == TD_ERR_OK);
160
161	exiting2 = 1;
162
163	printf("Calling td_close(3)\n");
164	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
165
166	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
167	    "counted threads (%d) != expected threads (%zu)",
168	    count, MAX_THREADS + 1);
169}
170
171ATF_TC(threads3);
172ATF_TC_HEAD(threads3, tc)
173{
174
175	atf_tc_set_md_var(tc, "descr",
176	    "Asserts that for each td_thr_iter() call td_thr_info() is valid");
177}
178
179static volatile int exiting3;
180
181static void *
182busyFunction3(void *arg)
183{
184
185	while (exiting3 == 0)
186		usleep(50000);
187
188	return NULL;
189}
190
191static int
192iterateThreads3(td_thread_t *thread, void *arg)
193{
194	int *counter = (int *)arg;
195	td_thread_info_t info;
196
197	ATF_REQUIRE(td_thr_info(thread, &info) == TD_ERR_OK);
198
199	++(*counter);
200
201	return TD_ERR_OK;
202}
203
204ATF_TC_BODY(threads3, tc)
205{
206	struct td_proc_callbacks_t dummy_callbacks;
207	td_proc_t *main_ta;
208	size_t i;
209	pthread_t threads[MAX_THREADS];
210	int count = 0;
211
212	dummy_callbacks.proc_read	= basic_proc_read;
213	dummy_callbacks.proc_write	= basic_proc_write;
214	dummy_callbacks.proc_lookup	= basic_proc_lookup;
215	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
216	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
217	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
218
219
220	for (i = 0; i < MAX_THREADS; i++) {
221		printf("Creating thread %zu\n", i);
222		PTHREAD_REQUIRE
223		    (pthread_create(&threads[i], NULL, busyFunction3, NULL));
224	}
225
226	printf("Calling td_open(3)\n");
227	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
228
229	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads3, &count) == TD_ERR_OK);
230
231	exiting3 = 1;
232
233	printf("Calling td_close(3)\n");
234	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
235
236	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
237	    "counted threads (%d) != expected threads (%zu)",
238	    count, MAX_THREADS + 1);
239}
240
241ATF_TC(threads4);
242ATF_TC_HEAD(threads4, tc)
243{
244
245	atf_tc_set_md_var(tc, "descr",
246	    "Asserts that for each td_thr_iter() call td_thr_getname() is "
247	    "valid");
248}
249
250static volatile int exiting4;
251
252static void *
253busyFunction4(void *arg)
254{
255
256	while (exiting4 == 0)
257		usleep(50000);
258
259	return NULL;
260}
261
262static int
263iterateThreads4(td_thread_t *thread, void *arg)
264{
265	int *counter = (int *)arg;
266	char name[PTHREAD_MAX_NAMELEN_NP];
267
268	ATF_REQUIRE(td_thr_getname(thread, name, sizeof(name)) == TD_ERR_OK);
269
270	printf("Thread name: %s\n", name);
271
272	++(*counter);
273
274	return TD_ERR_OK;
275}
276
277ATF_TC_BODY(threads4, tc)
278{
279	struct td_proc_callbacks_t dummy_callbacks;
280	td_proc_t *main_ta;
281	size_t i;
282	pthread_t threads[MAX_THREADS];
283	int count = 0;
284
285	dummy_callbacks.proc_read	= basic_proc_read;
286	dummy_callbacks.proc_write	= basic_proc_write;
287	dummy_callbacks.proc_lookup	= basic_proc_lookup;
288	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
289	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
290	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
291
292	for (i = 0; i < MAX_THREADS; i++) {
293		printf("Creating thread %zu\n", i);
294		PTHREAD_REQUIRE
295		    (pthread_create(&threads[i], NULL, busyFunction4, NULL));
296	}
297
298	for (i = 0; i < MAX_THREADS; i++) {
299		PTHREAD_REQUIRE
300		    (pthread_setname_np(threads[i], "test_%d", (void*)i));
301	}
302
303	printf("Calling td_open(3)\n");
304	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
305
306	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads4, &count) == TD_ERR_OK);
307
308	exiting4 = 1;
309
310	printf("Calling td_close(3)\n");
311	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
312
313	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
314	    "counted threads (%d) != expected threads (%zu)",
315	    count, MAX_THREADS + 1);
316}
317
318ATF_TC(threads5);
319ATF_TC_HEAD(threads5, tc)
320{
321
322	atf_tc_set_md_var(tc, "descr",
323	    "Asserts that td_thr_getname() handles shorter buffer parameter "
324	    "and the result is properly truncated");
325}
326
327static volatile int exiting5;
328
329static void *
330busyFunction5(void *arg)
331{
332
333	while (exiting5 == 0)
334		usleep(50000);
335
336	return NULL;
337}
338
339static int
340iterateThreads5(td_thread_t *thread, void *arg)
341{
342	int *counter = (int *)arg;
343	/* Arbitrarily short string buffer */
344	char name[3];
345
346	ATF_REQUIRE(td_thr_getname(thread, name, sizeof(name)) == TD_ERR_OK);
347
348	printf("Thread name: %s\n", name);
349
350	/* strlen(3) does not count including a '\0' character */
351	ATF_REQUIRE(strlen(name) < sizeof(name));
352
353	++(*counter);
354
355	return TD_ERR_OK;
356}
357
358ATF_TC_BODY(threads5, tc)
359{
360	struct td_proc_callbacks_t dummy_callbacks;
361	td_proc_t *main_ta;
362	size_t i;
363	pthread_t threads[MAX_THREADS];
364	int count = 0;
365
366	dummy_callbacks.proc_read	= basic_proc_read;
367	dummy_callbacks.proc_write	= basic_proc_write;
368	dummy_callbacks.proc_lookup	= basic_proc_lookup;
369	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
370	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
371	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
372
373	for (i = 0; i < MAX_THREADS; i++) {
374		printf("Creating thread %zu\n", i);
375		PTHREAD_REQUIRE
376		    (pthread_create(&threads[i], NULL, busyFunction5, NULL));
377	}
378
379	for (i = 0; i < MAX_THREADS; i++) {
380		PTHREAD_REQUIRE
381		    (pthread_setname_np(threads[i], "test_%d", (void*)i));
382	}
383
384	printf("Calling td_open(3)\n");
385	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
386
387	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads5, &count) == TD_ERR_OK);
388
389	exiting5 = 1;
390
391	printf("Calling td_close(3)\n");
392	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
393
394	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
395	    "counted threads (%d) != expected threads (%zu)",
396	    count, MAX_THREADS + 1);
397}
398
399ATF_TC(threads6);
400ATF_TC_HEAD(threads6, tc)
401{
402
403	atf_tc_set_md_var(tc, "descr",
404	    "Asserts that pthread_t can be translated with td_map_pth2thr() "
405	    "to td_thread_t -- and assert earlier that td_thr_iter() call is "
406	    "valid");
407}
408
409static volatile int exiting6;
410
411static void *
412busyFunction6(void *arg)
413{
414
415	while (exiting6 == 0)
416		usleep(50000);
417
418	return NULL;
419}
420
421static int
422iterateThreads6(td_thread_t *thread, void *arg)
423{
424	int *counter = (int *)arg;
425
426	++(*counter);
427
428	return TD_ERR_OK;
429}
430
431ATF_TC_BODY(threads6, tc)
432{
433	struct td_proc_callbacks_t dummy_callbacks;
434	td_proc_t *main_ta;
435	size_t i;
436	pthread_t threads[MAX_THREADS];
437	int count = 0;
438
439	dummy_callbacks.proc_read	= basic_proc_read;
440	dummy_callbacks.proc_write	= basic_proc_write;
441	dummy_callbacks.proc_lookup	= basic_proc_lookup;
442	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
443	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
444	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
445
446	for (i = 0; i < MAX_THREADS; i++) {
447		printf("Creating thread %zu\n", i);
448		PTHREAD_REQUIRE
449		    (pthread_create(&threads[i], NULL, busyFunction6, NULL));
450	}
451
452	printf("Calling td_open(3)\n");
453	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
454
455	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads6, &count) == TD_ERR_OK);
456
457	for (i = 0; i < MAX_THREADS; i++) {
458		td_thread_t *td_thread;
459		ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread)
460		    == TD_ERR_OK);
461	}
462
463	exiting6 = 1;
464
465	printf("Calling td_close(3)\n");
466	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
467
468	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
469	    "counted threads (%d) != expected threads (%zu)",
470	    count, MAX_THREADS + 1);
471}
472
473ATF_TC(threads7);
474ATF_TC_HEAD(threads7, tc)
475{
476
477	atf_tc_set_md_var(tc, "descr",
478	    "Asserts that pthread_t can be translated with td_map_pth2thr() "
479	    "to td_thread_t -- and assert later that td_thr_iter() call is "
480	    "valid");
481}
482
483static volatile int exiting7;
484
485static void *
486busyFunction7(void *arg)
487{
488
489	while (exiting7 == 0)
490		usleep(50000);
491
492	return NULL;
493}
494
495static int
496iterateThreads7(td_thread_t *thread, void *arg)
497{
498	int *counter = (int *)arg;
499
500	++(*counter);
501
502	return TD_ERR_OK;
503}
504
505ATF_TC_BODY(threads7, tc)
506{
507	struct td_proc_callbacks_t dummy_callbacks;
508	td_proc_t *main_ta;
509	size_t i;
510	pthread_t threads[MAX_THREADS];
511	int count = 0;
512
513	dummy_callbacks.proc_read	= basic_proc_read;
514	dummy_callbacks.proc_write	= basic_proc_write;
515	dummy_callbacks.proc_lookup	= basic_proc_lookup;
516	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
517	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
518	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
519
520	for (i = 0; i < MAX_THREADS; i++) {
521		printf("Creating thread %zu\n", i);
522		PTHREAD_REQUIRE
523		    (pthread_create(&threads[i], NULL, busyFunction7, NULL));
524	}
525
526	printf("Calling td_open(3)\n");
527	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
528
529	for (i = 0; i < MAX_THREADS; i++) {
530		td_thread_t *td_thread;
531		ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread)
532		    == TD_ERR_OK);
533	}
534
535	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads7, &count) == TD_ERR_OK);
536
537	exiting7 = 1;
538
539	printf("Calling td_close(3)\n");
540	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
541
542	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
543	    "counted threads (%d) != expected threads (%zu)",
544	    count, MAX_THREADS + 1);
545}
546
547ATF_TC(threads8);
548ATF_TC_HEAD(threads8, tc)
549{
550
551	atf_tc_set_md_var(tc, "descr",
552	    "Asserts that pthread_t can be translated with td_map_pth2thr() "
553	    "to td_thread_t -- compare thread's name of pthread_t and "
554	    "td_thread_t");
555}
556
557static volatile int exiting8;
558
559static void *
560busyFunction8(void *arg)
561{
562
563	while (exiting8 == 0)
564		usleep(50000);
565
566	return NULL;
567}
568
569static int
570iterateThreads8(td_thread_t *thread, void *arg)
571{
572	int *counter = (int *)arg;
573
574	++(*counter);
575
576	return TD_ERR_OK;
577}
578
579ATF_TC_BODY(threads8, tc)
580{
581	struct td_proc_callbacks_t dummy_callbacks;
582	td_proc_t *main_ta;
583	size_t i;
584	pthread_t threads[MAX_THREADS];
585	int count = 0;
586
587	dummy_callbacks.proc_read	= basic_proc_read;
588	dummy_callbacks.proc_write	= basic_proc_write;
589	dummy_callbacks.proc_lookup	= basic_proc_lookup;
590	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
591	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
592	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
593
594	for (i = 0; i < MAX_THREADS; i++) {
595		printf("Creating thread %zu\n", i);
596		PTHREAD_REQUIRE
597		    (pthread_create(&threads[i], NULL, busyFunction8, NULL));
598	}
599
600	printf("Calling td_open(3)\n");
601	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
602
603	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads8, &count) == TD_ERR_OK);
604
605	for (i = 0; i < MAX_THREADS; i++) {
606		td_thread_t *td_thread;
607		char td_threadname[PTHREAD_MAX_NAMELEN_NP];
608		char pth_threadname[PTHREAD_MAX_NAMELEN_NP];
609		ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread)
610		    == TD_ERR_OK);
611		ATF_REQUIRE(td_thr_getname(td_thread, td_threadname,
612		    sizeof(td_threadname)) == TD_ERR_OK);
613		PTHREAD_REQUIRE(pthread_getname_np(threads[i], pth_threadname,
614		    sizeof(pth_threadname)));
615		ATF_REQUIRE(strcmp(td_threadname, pth_threadname) == 0);
616	}
617
618	exiting8 = 1;
619
620	printf("Calling td_close(3)\n");
621	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
622
623	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
624	    "counted threads (%d) != expected threads (%zu)",
625	    count, MAX_THREADS + 1);
626}
627
628ATF_TC(threads9);
629ATF_TC_HEAD(threads9, tc)
630{
631
632	atf_tc_set_md_var(tc, "descr",
633	    "Asserts that pthread_t can be translated with td_map_pth2thr() "
634	    "to td_thread_t -- assert that thread is in the TD_STATE_RUNNING "
635            "state");
636}
637
638static volatile int exiting9;
639
640static void *
641busyFunction9(void *arg)
642{
643
644	while (exiting9 == 0)
645		usleep(50000);
646
647	return NULL;
648}
649
650static int
651iterateThreads9(td_thread_t *thread, void *arg)
652{
653	int *counter = (int *)arg;
654
655	++(*counter);
656
657	return TD_ERR_OK;
658}
659
660ATF_TC_BODY(threads9, tc)
661{
662	struct td_proc_callbacks_t dummy_callbacks;
663	td_proc_t *main_ta;
664	size_t i;
665	pthread_t threads[MAX_THREADS];
666	int count = 0;
667
668	dummy_callbacks.proc_read	= basic_proc_read;
669	dummy_callbacks.proc_write	= basic_proc_write;
670	dummy_callbacks.proc_lookup	= basic_proc_lookup;
671	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
672	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
673	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
674
675	for (i = 0; i < MAX_THREADS; i++) {
676		printf("Creating thread %zu\n", i);
677		PTHREAD_REQUIRE
678		    (pthread_create(&threads[i], NULL, busyFunction9, NULL));
679	}
680
681	printf("Calling td_open(3)\n");
682	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
683
684	for (i = 0; i < MAX_THREADS; i++) {
685		td_thread_t *td_thread;
686		td_thread_info_t info;
687		ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread)
688		    == TD_ERR_OK);
689		ATF_REQUIRE(td_thr_info(td_thread, &info) == TD_ERR_OK);
690		ATF_REQUIRE_EQ(info.thread_state, TD_STATE_RUNNING);
691	}
692
693	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads9, &count) == TD_ERR_OK);
694
695	exiting9 = 1;
696
697	printf("Calling td_close(3)\n");
698	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
699
700	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
701	    "counted threads (%d) != expected threads (%zu)",
702	    count, MAX_THREADS + 1);
703}
704
705ATF_TP_ADD_TCS(tp)
706{
707
708	ATF_TP_ADD_TC(tp, threads1);
709	ATF_TP_ADD_TC(tp, threads2);
710	ATF_TP_ADD_TC(tp, threads3);
711	ATF_TP_ADD_TC(tp, threads4);
712	ATF_TP_ADD_TC(tp, threads5);
713	ATF_TP_ADD_TC(tp, threads6);
714	ATF_TP_ADD_TC(tp, threads7);
715	ATF_TP_ADD_TC(tp, threads8);
716	ATF_TP_ADD_TC(tp, threads9);
717
718	return atf_no_error();
719}
720