1 /*
2  * Copyright (c) 2024, Meta
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <pthread.h>
8 
9 #include <zephyr/sys/util.h>
10 #include <zephyr/ztest.h>
11 
12 #define BIOS_FOOD           0xB105F00D
13 #define SCHED_INVALID       4242
14 #define INVALID_DETACHSTATE 7373
15 
16 static bool attr_valid;
17 static pthread_attr_t attr;
18 static const pthread_attr_t uninit_attr;
19 static bool detached_thread_has_finished;
20 
21 /* TODO: this should be optional */
22 #define STATIC_THREAD_STACK_SIZE (MAX(1024, PTHREAD_STACK_MIN + CONFIG_TEST_EXTRA_STACK_SIZE))
23 static K_THREAD_STACK_DEFINE(static_thread_stack, STATIC_THREAD_STACK_SIZE);
24 
thread_entry(void * arg)25 static void *thread_entry(void *arg)
26 {
27 	bool joinable = (bool)POINTER_TO_UINT(arg);
28 
29 	if (!joinable) {
30 		detached_thread_has_finished = true;
31 	}
32 
33 	return NULL;
34 }
35 
create_thread_common_entry(const pthread_attr_t * attrp,bool expect_success,bool joinable,void * (* entry)(void * arg),void * arg)36 static void create_thread_common_entry(const pthread_attr_t *attrp, bool expect_success,
37 				       bool joinable, void *(*entry)(void *arg), void *arg)
38 {
39 	pthread_t th;
40 
41 	if (!joinable) {
42 		detached_thread_has_finished = false;
43 	}
44 
45 	if (expect_success) {
46 		zassert_ok(pthread_create(&th, attrp, entry, arg));
47 	} else {
48 		zassert_not_ok(pthread_create(&th, attrp, entry, arg));
49 		return;
50 	}
51 
52 	if (joinable) {
53 		zassert_ok(pthread_join(th, NULL), "failed to join joinable thread");
54 		return;
55 	}
56 
57 	/* should not be able to join detached thread */
58 	zassert_not_ok(pthread_join(th, NULL));
59 
60 	for (size_t i = 0; i < 10; ++i) {
61 		k_msleep(2 * CONFIG_PTHREAD_RECYCLER_DELAY_MS);
62 		if (detached_thread_has_finished) {
63 			break;
64 		}
65 	}
66 
67 	zassert_true(detached_thread_has_finished, "detached thread did not seem to finish");
68 }
69 
create_thread_common(const pthread_attr_t * attrp,bool expect_success,bool joinable)70 static void create_thread_common(const pthread_attr_t *attrp, bool expect_success, bool joinable)
71 {
72 	create_thread_common_entry(attrp, expect_success, joinable, thread_entry,
73 				   UINT_TO_POINTER(joinable));
74 }
75 
can_create_thread(const pthread_attr_t * attrp)76 static inline void can_create_thread(const pthread_attr_t *attrp)
77 {
78 	create_thread_common(attrp, true, true);
79 }
80 
cannot_create_thread(const pthread_attr_t * attrp)81 static inline void cannot_create_thread(const pthread_attr_t *attrp)
82 {
83 	create_thread_common(attrp, false, true);
84 }
85 
ZTEST(pthread_attr,test_null_attr)86 ZTEST(pthread_attr, test_null_attr)
87 {
88 	/*
89 	 * This test can only succeed when it is possible to call pthread_create() with a NULL
90 	 * pthread_attr_t* (I.e. when we have the ability to allocate thread stacks dynamically).
91 	 */
92 	create_thread_common(NULL, IS_ENABLED(CONFIG_DYNAMIC_THREAD) ? true : false, true);
93 }
94 
ZTEST(pthread_attr,test_pthread_attr_static_corner_cases)95 ZTEST(pthread_attr, test_pthread_attr_static_corner_cases)
96 {
97 	pthread_attr_t attr1;
98 
99 	Z_TEST_SKIP_IFDEF(CONFIG_DYNAMIC_THREAD);
100 
101 	/*
102 	 * These tests are specifically for when dynamic thread stacks are disabled, so passing
103 	 * a NULL pthread_attr_t* should fail.
104 	 */
105 	cannot_create_thread(NULL);
106 
107 	/*
108 	 * Additionally, without calling pthread_attr_setstack(), thread creation should fail.
109 	 */
110 	zassert_ok(pthread_attr_init(&attr1));
111 	cannot_create_thread(&attr1);
112 }
113 
ZTEST(pthread_attr,test_pthread_attr_init_destroy)114 ZTEST(pthread_attr, test_pthread_attr_init_destroy)
115 {
116 	/* attr has already been initialized in before() */
117 
118 	if (false) {
119 		/* undefined behaviour */
120 		zassert_ok(pthread_attr_init(&attr));
121 	}
122 
123 	/* cannot destroy an uninitialized attr */
124 	zassert_equal(pthread_attr_destroy((pthread_attr_t *)&uninit_attr), EINVAL);
125 
126 	can_create_thread(&attr);
127 
128 	/* can destroy an initialized attr */
129 	zassert_ok(pthread_attr_destroy(&attr), "failed to destroy an initialized attr");
130 	attr_valid = false;
131 
132 	cannot_create_thread(&attr);
133 
134 	if (false) {
135 		/* undefined behaviour */
136 		zassert_ok(pthread_attr_destroy(&attr));
137 	}
138 
139 	/* can re-initialize a destroyed attr */
140 	zassert_ok(pthread_attr_init(&attr));
141 	/* TODO: pthread_attr_init() should be sufficient to initialize a thread by itself */
142 	zassert_ok(pthread_attr_setstack(&attr, &static_thread_stack, STATIC_THREAD_STACK_SIZE));
143 	attr_valid = true;
144 
145 	can_create_thread(&attr);
146 
147 	/* note: attr is still valid and is destroyed in after() */
148 }
149 
ZTEST(pthread_attr,test_pthread_attr_getschedparam)150 ZTEST(pthread_attr, test_pthread_attr_getschedparam)
151 {
152 	struct sched_param param = {
153 		.sched_priority = BIOS_FOOD,
154 	};
155 
156 	/* degenerate cases */
157 	{
158 		if (false) {
159 			/* undefined behaviour */
160 			zassert_equal(pthread_attr_getschedparam(NULL, NULL), EINVAL);
161 			zassert_equal(pthread_attr_getschedparam(NULL, &param), EINVAL);
162 			zassert_equal(pthread_attr_getschedparam(&uninit_attr, &param), EINVAL);
163 		}
164 		zassert_equal(pthread_attr_getschedparam(&attr, NULL), EINVAL);
165 	}
166 
167 	/* only check to see that the function succeeds and sets param */
168 	zassert_ok(pthread_attr_getschedparam(&attr, &param));
169 	zassert_not_equal(BIOS_FOOD, param.sched_priority);
170 }
171 
ZTEST(pthread_attr,test_pthread_attr_setschedparam)172 ZTEST(pthread_attr, test_pthread_attr_setschedparam)
173 {
174 	struct sched_param param = {0};
175 
176 	/* degenerate cases */
177 	{
178 		if (false) {
179 			/* undefined behaviour */
180 			zassert_equal(pthread_attr_setschedparam(NULL, NULL), EINVAL);
181 			zassert_equal(pthread_attr_setschedparam(NULL, &param), EINVAL);
182 			zassert_equal(
183 				pthread_attr_setschedparam((pthread_attr_t *)&uninit_attr, &param),
184 				EINVAL);
185 		}
186 		zassert_equal(pthread_attr_setschedparam(&attr, NULL), EINVAL);
187 	}
188 
189 	zassert_ok(pthread_attr_setschedparam(&attr, &param));
190 
191 	can_create_thread(&attr);
192 }
193 
ZTEST(pthread_attr,test_pthread_attr_getschedpolicy)194 ZTEST(pthread_attr, test_pthread_attr_getschedpolicy)
195 {
196 	int policy = BIOS_FOOD;
197 
198 	/* degenerate cases */
199 	{
200 		if (false) {
201 			/* undefined behaviour */
202 			zassert_equal(pthread_attr_getschedpolicy(NULL, NULL), EINVAL);
203 			zassert_equal(pthread_attr_getschedpolicy(NULL, &policy), EINVAL);
204 			zassert_equal(pthread_attr_getschedpolicy(&uninit_attr, &policy), EINVAL);
205 		}
206 		zassert_equal(pthread_attr_getschedpolicy(&attr, NULL), EINVAL);
207 	}
208 
209 	/* only check to see that the function succeeds and sets policy */
210 	zassert_ok(pthread_attr_getschedpolicy(&attr, &policy));
211 	zassert_not_equal(BIOS_FOOD, policy);
212 }
213 
ZTEST(pthread_attr,test_pthread_attr_setschedpolicy)214 ZTEST(pthread_attr, test_pthread_attr_setschedpolicy)
215 {
216 	int policy = SCHED_OTHER;
217 
218 	/* degenerate cases */
219 	{
220 		if (false) {
221 			/* undefined behaviour */
222 			zassert_equal(pthread_attr_setschedpolicy(NULL, SCHED_INVALID), EINVAL);
223 			zassert_equal(pthread_attr_setschedpolicy(NULL, policy), EINVAL);
224 			zassert_equal(
225 				pthread_attr_setschedpolicy((pthread_attr_t *)&uninit_attr, policy),
226 				EINVAL);
227 		}
228 		zassert_equal(pthread_attr_setschedpolicy(&attr, SCHED_INVALID), EINVAL);
229 	}
230 
231 	zassert_ok(pthread_attr_setschedpolicy(&attr, SCHED_OTHER));
232 	/* read back the same policy we just wrote */
233 	policy = SCHED_INVALID;
234 	zassert_ok(pthread_attr_getschedpolicy(&attr, &policy));
235 	zassert_equal(policy, SCHED_OTHER);
236 
237 	can_create_thread(&attr);
238 }
239 
ZTEST(pthread_attr,test_pthread_attr_getscope)240 ZTEST(pthread_attr, test_pthread_attr_getscope)
241 {
242 	int contentionscope = BIOS_FOOD;
243 
244 	/* degenerate cases */
245 	{
246 		if (false) {
247 			/* undefined behaviour */
248 			zassert_equal(pthread_attr_getscope(NULL, NULL), EINVAL);
249 			zassert_equal(pthread_attr_getscope(NULL, &contentionscope), EINVAL);
250 			zassert_equal(pthread_attr_getscope(&uninit_attr, &contentionscope),
251 				      EINVAL);
252 		}
253 		zassert_equal(pthread_attr_getscope(&attr, NULL), EINVAL);
254 	}
255 
256 	zassert_ok(pthread_attr_getscope(&attr, &contentionscope));
257 	zassert_equal(contentionscope, PTHREAD_SCOPE_SYSTEM);
258 }
259 
ZTEST(pthread_attr,test_pthread_attr_setscope)260 ZTEST(pthread_attr, test_pthread_attr_setscope)
261 {
262 	int contentionscope = BIOS_FOOD;
263 
264 	/* degenerate cases */
265 	{
266 		if (false) {
267 			/* undefined behaviour */
268 			zassert_equal(pthread_attr_setscope(NULL, PTHREAD_SCOPE_SYSTEM), EINVAL);
269 			zassert_equal(pthread_attr_setscope(NULL, contentionscope), EINVAL);
270 			zassert_equal(pthread_attr_setscope((pthread_attr_t *)&uninit_attr,
271 							    contentionscope),
272 				      EINVAL);
273 		}
274 		zassert_equal(pthread_attr_setscope(&attr, 3), EINVAL);
275 	}
276 
277 	zassert_equal(pthread_attr_setscope(&attr, PTHREAD_SCOPE_PROCESS), ENOTSUP);
278 	zassert_ok(pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM));
279 	zassert_ok(pthread_attr_getscope(&attr, &contentionscope));
280 	zassert_equal(contentionscope, PTHREAD_SCOPE_SYSTEM);
281 }
282 
ZTEST(pthread_attr,test_pthread_attr_getinheritsched)283 ZTEST(pthread_attr, test_pthread_attr_getinheritsched)
284 {
285 	int inheritsched = BIOS_FOOD;
286 
287 	/* degenerate cases */
288 	{
289 		if (false) {
290 			/* undefined behaviour */
291 			zassert_equal(pthread_attr_getinheritsched(NULL, NULL), EINVAL);
292 			zassert_equal(pthread_attr_getinheritsched(NULL, &inheritsched), EINVAL);
293 			zassert_equal(pthread_attr_getinheritsched(&uninit_attr, &inheritsched),
294 				      EINVAL);
295 		}
296 		zassert_equal(pthread_attr_getinheritsched(&attr, NULL), EINVAL);
297 	}
298 
299 	zassert_ok(pthread_attr_getinheritsched(&attr, &inheritsched));
300 	zassert_equal(inheritsched, PTHREAD_INHERIT_SCHED);
301 }
302 
inheritsched_entry(void * arg)303 static void *inheritsched_entry(void *arg)
304 {
305 	int prio;
306 	int inheritsched;
307 	int pprio = POINTER_TO_INT(arg);
308 
309 	zassert_ok(pthread_attr_getinheritsched(&attr, &inheritsched));
310 
311 	prio = k_thread_priority_get(k_current_get());
312 
313 	if (inheritsched == PTHREAD_INHERIT_SCHED) {
314 		/*
315 		 * There will be numerical overlap between posix priorities in different scheduler
316 		 * policies so only check the Zephyr priority here. The posix policy and posix
317 		 * priority are derived from the Zephyr priority in any case.
318 		 */
319 		zassert_equal(prio, pprio, "actual priority: %d, expected priority: %d", prio,
320 			      pprio);
321 		return NULL;
322 	}
323 
324 	/* inheritsched == PTHREAD_EXPLICIT_SCHED */
325 	int act_prio;
326 	int exp_prio;
327 	int act_policy;
328 	int exp_policy;
329 	struct sched_param param;
330 
331 	/* get the actual policy, param, etc */
332 	zassert_ok(pthread_getschedparam(pthread_self(), &act_policy, &param));
333 	act_prio = param.sched_priority;
334 
335 	/* get the expected policy, param, etc */
336 	zassert_ok(pthread_attr_getschedpolicy(&attr, &exp_policy));
337 	zassert_ok(pthread_attr_getschedparam(&attr, &param));
338 	exp_prio = param.sched_priority;
339 
340 	/* compare actual vs expected */
341 	zassert_equal(act_policy, exp_policy, "actual policy: %d, expected policy: %d", act_policy,
342 		      exp_policy);
343 	zassert_equal(act_prio, exp_prio, "actual priority: %d, expected priority: %d", act_prio,
344 		      exp_prio);
345 
346 	return NULL;
347 }
348 
test_pthread_attr_setinheritsched_common(bool inheritsched)349 static void test_pthread_attr_setinheritsched_common(bool inheritsched)
350 {
351 	int prio;
352 	int policy;
353 	struct sched_param param;
354 
355 	extern int zephyr_to_posix_priority(int priority, int *policy);
356 
357 	prio = k_thread_priority_get(k_current_get());
358 	zassert_not_equal(prio, K_LOWEST_APPLICATION_THREAD_PRIO);
359 
360 	/*
361 	 * values affected by inheritsched are policy / priority / contentionscope
362 	 *
363 	 * we only support PTHREAD_SCOPE_SYSTEM, so no need to set contentionscope
364 	 */
365 	prio = K_LOWEST_APPLICATION_THREAD_PRIO;
366 	param.sched_priority = zephyr_to_posix_priority(prio, &policy);
367 
368 	zassert_ok(pthread_attr_setschedpolicy(&attr, policy));
369 	zassert_ok(pthread_attr_setschedparam(&attr, &param));
370 	zassert_ok(pthread_attr_setinheritsched(&attr, inheritsched));
371 	create_thread_common_entry(&attr, true, true, inheritsched_entry,
372 				   UINT_TO_POINTER(k_thread_priority_get(k_current_get())));
373 }
374 
ZTEST(pthread_attr,test_pthread_attr_setinheritsched)375 ZTEST(pthread_attr, test_pthread_attr_setinheritsched)
376 {
377 	/* degenerate cases */
378 	{
379 		if (false) {
380 			/* undefined behaviour */
381 			zassert_equal(pthread_attr_setinheritsched(NULL, PTHREAD_EXPLICIT_SCHED),
382 				      EINVAL);
383 			zassert_equal(pthread_attr_setinheritsched(NULL, PTHREAD_INHERIT_SCHED),
384 				      EINVAL);
385 			zassert_equal(pthread_attr_setinheritsched((pthread_attr_t *)&uninit_attr,
386 								   PTHREAD_INHERIT_SCHED),
387 				      EINVAL);
388 		}
389 		zassert_equal(pthread_attr_setinheritsched(&attr, 3), EINVAL);
390 	}
391 
392 	/* valid cases */
393 	test_pthread_attr_setinheritsched_common(PTHREAD_INHERIT_SCHED);
394 	test_pthread_attr_setinheritsched_common(PTHREAD_EXPLICIT_SCHED);
395 }
396 
ZTEST(pthread_attr,test_pthread_attr_large_stacksize)397 ZTEST(pthread_attr, test_pthread_attr_large_stacksize)
398 {
399 	size_t actual_size;
400 	const size_t expect_size = BIT(CONFIG_POSIX_PTHREAD_ATTR_STACKSIZE_BITS);
401 
402 	if (pthread_attr_setstacksize(&attr, expect_size) != 0) {
403 		TC_PRINT("Unable to allocate large stack of size %zu (skipping)\n", expect_size);
404 		ztest_test_skip();
405 		return;
406 	}
407 
408 	zassert_ok(pthread_attr_getstacksize(&attr, &actual_size));
409 	zassert_equal(actual_size, expect_size);
410 }
411 
ZTEST(pthread_attr,test_pthread_attr_getdetachstate)412 ZTEST(pthread_attr, test_pthread_attr_getdetachstate)
413 {
414 	int detachstate;
415 
416 	/* degenerate cases */
417 	{
418 		if (false) {
419 			/* undefined behaviour */
420 			zassert_equal(pthread_attr_getdetachstate(NULL, NULL), EINVAL);
421 			zassert_equal(pthread_attr_getdetachstate(NULL, &detachstate), EINVAL);
422 			zassert_equal(pthread_attr_getdetachstate(&uninit_attr, &detachstate),
423 				      EINVAL);
424 		}
425 		zassert_equal(pthread_attr_getdetachstate(&attr, NULL), EINVAL);
426 	}
427 
428 	/* default detachstate is joinable */
429 	zassert_ok(pthread_attr_getdetachstate(&attr, &detachstate));
430 	zassert_equal(detachstate, PTHREAD_CREATE_JOINABLE);
431 	can_create_thread(&attr);
432 }
433 
ZTEST(pthread_attr,test_pthread_attr_setdetachstate)434 ZTEST(pthread_attr, test_pthread_attr_setdetachstate)
435 {
436 	int detachstate = PTHREAD_CREATE_JOINABLE;
437 
438 	/* degenerate cases */
439 	{
440 		if (false) {
441 			/* undefined behaviour */
442 			zassert_equal(pthread_attr_setdetachstate(NULL, INVALID_DETACHSTATE),
443 				      EINVAL);
444 			zassert_equal(pthread_attr_setdetachstate(NULL, detachstate), EINVAL);
445 			zassert_equal(pthread_attr_setdetachstate((pthread_attr_t *)&uninit_attr,
446 								  detachstate),
447 				      EINVAL);
448 		}
449 		zassert_equal(pthread_attr_setdetachstate(&attr, INVALID_DETACHSTATE), EINVAL);
450 	}
451 
452 	/* read back detachstate just written */
453 	zassert_ok(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
454 	zassert_ok(pthread_attr_getdetachstate(&attr, &detachstate));
455 	zassert_equal(detachstate, PTHREAD_CREATE_DETACHED);
456 	create_thread_common(&attr, true, false);
457 }
458 
ZTEST(pthread_attr,test_pthread_attr_policy_and_priority_limits)459 ZTEST(pthread_attr, test_pthread_attr_policy_and_priority_limits)
460 {
461 	int pmin = -1;
462 	int pmax = -1;
463 	struct sched_param param;
464 	static const int policies[] = {
465 		SCHED_FIFO,
466 		SCHED_RR,
467 		SCHED_OTHER,
468 		SCHED_INVALID,
469 	};
470 	static const char *const policy_names[] = {
471 		"SCHED_FIFO",
472 		"SCHED_RR",
473 		"SCHED_OTHER",
474 		"SCHED_INVALID",
475 	};
476 	static const bool policy_enabled[] = {
477 		CONFIG_NUM_COOP_PRIORITIES > 0,
478 		CONFIG_NUM_PREEMPT_PRIORITIES > 0,
479 		CONFIG_NUM_PREEMPT_PRIORITIES > 0,
480 		false,
481 	};
482 	static int nprio[] = {
483 		CONFIG_NUM_COOP_PRIORITIES,
484 		CONFIG_NUM_PREEMPT_PRIORITIES,
485 		CONFIG_NUM_PREEMPT_PRIORITIES,
486 		42,
487 	};
488 	const char *const prios[] = {"pmin", "pmax"};
489 
490 	BUILD_ASSERT(!(SCHED_INVALID == SCHED_FIFO || SCHED_INVALID == SCHED_RR ||
491 		       SCHED_INVALID == SCHED_OTHER),
492 		     "SCHED_INVALID is itself invalid");
493 
494 	ARRAY_FOR_EACH(policies, policy) {
495 		/* get pmin and pmax for policies[policy] */
496 		ARRAY_FOR_EACH(prios, i) {
497 			errno = 0;
498 			if (i == 0) {
499 				pmin = sched_get_priority_min(policies[policy]);
500 				param.sched_priority = pmin;
501 			} else {
502 				pmax = sched_get_priority_max(policies[policy]);
503 				param.sched_priority = pmax;
504 			}
505 
506 			if (policy == 3) {
507 				/* invalid policy */
508 				zassert_equal(-1, param.sched_priority);
509 				zassert_equal(errno, EINVAL);
510 				continue;
511 			}
512 
513 			zassert_not_equal(-1, param.sched_priority,
514 					  "sched_get_priority_%s(%s) failed: %d",
515 					  i == 0 ? "min" : "max", policy_names[policy], errno);
516 			zassert_ok(errno, "sched_get_priority_%s(%s) set errno to %s",
517 				   i == 0 ? "min" : "max", policy_names[policy], errno);
518 		}
519 
520 		if (policy != 3) {
521 			/* this will not work for SCHED_INVALID */
522 
523 			/*
524 			 * IEEE 1003.1-2008 Section 2.8.4
525 			 * conforming implementations should provide a range of at least 32
526 			 * priorities
527 			 *
528 			 * Note: we relax this requirement
529 			 */
530 			zassert_true(pmax > pmin, "pmax (%d) <= pmin (%d)", pmax, pmin,
531 				     "%s min/max inconsistency: pmin: %d pmax: %d",
532 				     policy_names[policy], pmin, pmax);
533 
534 			/*
535 			 * Getting into the weeds a bit (i.e. whitebox testing), Zephyr
536 			 * cooperative threads use [-CONFIG_NUM_COOP_PRIORITIES,-1] and
537 			 * preemptive threads use [0, CONFIG_NUM_PREEMPT_PRIORITIES - 1],
538 			 * where the more negative thread has the higher priority. Since we
539 			 * cannot map those directly (a return value of -1 indicates error),
540 			 * we simply map those to the positive space.
541 			 */
542 			zassert_equal(pmin, 0, "unexpected pmin for %s", policy_names[policy]);
543 			zassert_equal(pmax, nprio[policy] - 1, "unexpected pmax for %s",
544 				      policy_names[policy]); /* test happy paths */
545 		}
546 
547 		/* create threads with min and max priority levels for each policy */
548 		ARRAY_FOR_EACH(prios, i) {
549 			param.sched_priority = (i == 0) ? pmin : pmax;
550 
551 			if (!policy_enabled[policy]) {
552 				zassert_not_ok(
553 					pthread_attr_setschedpolicy(&attr, policies[policy]));
554 				zassert_not_ok(
555 					pthread_attr_setschedparam(&attr, &param),
556 					"pthread_attr_setschedparam() failed for %s (%d) of %s",
557 					prios[i], param.sched_priority, policy_names[policy]);
558 				continue;
559 			}
560 
561 			/* set policy */
562 			zassert_ok(pthread_attr_setschedpolicy(&attr, policies[policy]),
563 				   "pthread_attr_setschedpolicy() failed for %s (%d) of %s",
564 				   prios[i], param.sched_priority, policy_names[policy]);
565 
566 			/* set priority */
567 			zassert_ok(pthread_attr_setschedparam(&attr, &param),
568 				   "pthread_attr_setschedparam() failed for %s (%d) of %s",
569 				   prios[i], param.sched_priority, policy_names[policy]);
570 
571 			can_create_thread(&attr);
572 		}
573 	}
574 }
575 
before(void * arg)576 static void before(void *arg)
577 {
578 	ARG_UNUSED(arg);
579 
580 	zassert_ok(pthread_attr_init(&attr));
581 	/* TODO: pthread_attr_init() should be sufficient to initialize a thread by itself */
582 	zassert_ok(pthread_attr_setstack(&attr, &static_thread_stack, STATIC_THREAD_STACK_SIZE));
583 	attr_valid = true;
584 }
585 
after(void * arg)586 static void after(void *arg)
587 {
588 	ARG_UNUSED(arg);
589 
590 	if (attr_valid) {
591 		(void)pthread_attr_destroy(&attr);
592 		attr_valid = false;
593 	}
594 }
595 
596 ZTEST_SUITE(pthread_attr, NULL, NULL, before, after, NULL);
597