1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <ztest.h>
8 #include <kernel.h>
9 #include <pthread.h>
10 #include <semaphore.h>
11 #include <sys/util.h>
12 
13 #ifndef min
14 #define min(a, b) ((a) < (b)) ? (a) : (b)
15 #endif
16 
17 #define N_THR_E 3
18 #define N_THR_T 4
19 #define BOUNCES 64
20 #define STACKS (1024 + CONFIG_TEST_EXTRA_STACKSIZE)
21 #define THREAD_PRIORITY 3
22 #define ONE_SECOND 1
23 
24 /* Macros to test invalid states */
25 #define PTHREAD_CANCEL_INVALID -1
26 #define SCHED_INVALID -1
27 #define PRIO_INVALID -1
28 
29 K_THREAD_STACK_ARRAY_DEFINE(stack_e, N_THR_E, STACKS);
30 K_THREAD_STACK_ARRAY_DEFINE(stack_t, N_THR_T, STACKS);
31 K_THREAD_STACK_ARRAY_DEFINE(stack_1, 1, 32);
32 
33 void *thread_top_exec(void *p1);
34 void *thread_top_term(void *p1);
35 
36 PTHREAD_MUTEX_DEFINE(lock);
37 
38 PTHREAD_COND_DEFINE(cvar0);
39 
40 PTHREAD_COND_DEFINE(cvar1);
41 
42 PTHREAD_BARRIER_DEFINE(barrier, N_THR_E);
43 
44 sem_t main_sem;
45 
46 static int bounce_failed;
47 static int bounce_done[N_THR_E];
48 
49 static int curr_bounce_thread;
50 
51 static int barrier_failed;
52 static int barrier_done[N_THR_E];
53 static int barrier_return[N_THR_E];
54 
55 /* First phase bounces execution between two threads using a condition
56  * variable, continuously testing that no other thread is mucking with
57  * the protected state.  This ends with all threads going back to
58  * sleep on the condition variable and being woken by main() for the
59  * second phase.
60  *
61  * Second phase simply lines up all the threads on a barrier, verifies
62  * that none run until the last one enters, and that all run after the
63  * exit.
64  *
65  * Test success is signaled to main() using a traditional semaphore.
66  */
67 
thread_top_exec(void * p1)68 void *thread_top_exec(void *p1)
69 {
70 	int i, j, id = (int) POINTER_TO_INT(p1);
71 	int policy;
72 	struct sched_param schedparam;
73 
74 	pthread_getschedparam(pthread_self(), &policy, &schedparam);
75 	printk("Thread %d starting with scheduling policy %d & priority %d\n",
76 		 id, policy, schedparam.sched_priority);
77 	/* Try a double-lock here to exercise the failing case of
78 	 * trylock.  We don't support RECURSIVE locks, so this is
79 	 * guaranteed to fail.
80 	 */
81 	pthread_mutex_lock(&lock);
82 
83 	if (!pthread_mutex_trylock(&lock)) {
84 		printk("pthread_mutex_trylock inexplicably succeeded\n");
85 		bounce_failed = 1;
86 	}
87 
88 	pthread_mutex_unlock(&lock);
89 
90 	for (i = 0; i < BOUNCES; i++) {
91 
92 		pthread_mutex_lock(&lock);
93 
94 		/* Wait for the current owner to signal us, unless we
95 		 * are the very first thread, in which case we need to
96 		 * wait a bit to be sure the other threads get
97 		 * scheduled and wait on cvar0.
98 		 */
99 		if (!(id == 0 && i == 0)) {
100 			zassert_equal(0, pthread_cond_wait(&cvar0, &lock), "");
101 		} else {
102 			pthread_mutex_unlock(&lock);
103 			usleep(USEC_PER_MSEC * 500U);
104 			pthread_mutex_lock(&lock);
105 		}
106 
107 		/* Claim ownership, then try really hard to give someone
108 		 * else a shot at hitting this if they are racing.
109 		 */
110 		curr_bounce_thread = id;
111 		for (j = 0; j < 1000; j++) {
112 			if (curr_bounce_thread != id) {
113 				printk("Racing bounce threads\n");
114 				bounce_failed = 1;
115 				sem_post(&main_sem);
116 				pthread_mutex_unlock(&lock);
117 				return NULL;
118 			}
119 			sched_yield();
120 		}
121 
122 		/* Next one's turn, go back to the top and wait.  */
123 		pthread_cond_signal(&cvar0);
124 		pthread_mutex_unlock(&lock);
125 	}
126 
127 	/* Signal we are complete to main(), then let it wake us up.  Note
128 	 * that we are using the same mutex with both cvar0 and cvar1,
129 	 * which is non-standard but kosher per POSIX (and it works fine
130 	 * in our implementation
131 	 */
132 	pthread_mutex_lock(&lock);
133 	bounce_done[id] = 1;
134 	sem_post(&main_sem);
135 	pthread_cond_wait(&cvar1, &lock);
136 	pthread_mutex_unlock(&lock);
137 
138 	/* Now just wait on the barrier.  Make sure no one else finished
139 	 * before we wait on it, then signal that we're done
140 	 */
141 	for (i = 0; i < N_THR_E; i++) {
142 		if (barrier_done[i]) {
143 			printk("Barrier exited early\n");
144 			barrier_failed = 1;
145 			sem_post(&main_sem);
146 		}
147 	}
148 	barrier_return[id] = pthread_barrier_wait(&barrier);
149 	barrier_done[id] = 1;
150 	sem_post(&main_sem);
151 	pthread_exit(p1);
152 
153 	return NULL;
154 }
155 
bounce_test_done(void)156 int bounce_test_done(void)
157 {
158 	int i;
159 
160 	if (bounce_failed) {
161 		return 1;
162 	}
163 
164 	for (i = 0; i < N_THR_E; i++) {
165 		if (!bounce_done[i]) {
166 			return 0;
167 		}
168 	}
169 
170 	return 1;
171 }
172 
barrier_test_done(void)173 int barrier_test_done(void)
174 {
175 	int i;
176 
177 	if (barrier_failed) {
178 		return 1;
179 	}
180 
181 	for (i = 0; i < N_THR_E; i++) {
182 		if (!barrier_done[i]) {
183 			return 0;
184 		}
185 	}
186 
187 	return 1;
188 }
189 
thread_top_term(void * p1)190 void *thread_top_term(void *p1)
191 {
192 	pthread_t self;
193 	int oldstate, policy, ret;
194 	int id = POINTER_TO_INT(p1);
195 	struct sched_param param, getschedparam;
196 
197 	param.sched_priority = N_THR_T - id;
198 
199 	self = pthread_self();
200 
201 	/* Change priority of thread */
202 	zassert_false(pthread_setschedparam(self, SCHED_RR, &param),
203 		      "Unable to set thread priority!");
204 
205 	zassert_false(pthread_getschedparam(self, &policy, &getschedparam),
206 			"Unable to get thread priority!");
207 
208 	printk("Thread %d starting with a priority of %d\n",
209 			id,
210 			getschedparam.sched_priority);
211 
212 	if (id % 2) {
213 		ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
214 		zassert_false(ret, "Unable to set cancel state!");
215 	}
216 
217 	if (id >= 2) {
218 		ret = pthread_detach(self);
219 		if (id == 2) {
220 			zassert_equal(ret, EINVAL, "re-detached thread!");
221 		}
222 	}
223 
224 	printk("Cancelling thread %d\n", id);
225 	pthread_cancel(self);
226 	printk("Thread %d could not be cancelled\n", id);
227 	sleep(ONE_SECOND);
228 	pthread_exit(p1);
229 	return NULL;
230 }
231 
test_posix_pthread_execution(void)232 void test_posix_pthread_execution(void)
233 {
234 	int i, ret, min_prio, max_prio;
235 	int dstate, policy;
236 	pthread_attr_t attr[N_THR_E] = {};
237 	struct sched_param schedparam, getschedparam;
238 	pthread_t newthread[N_THR_E];
239 	int schedpolicy = SCHED_FIFO;
240 	void *retval, *stackaddr;
241 	size_t stacksize;
242 	int serial_threads = 0;
243 	static const char thr_name[] = "thread name";
244 	char thr_name_buf[CONFIG_THREAD_MAX_NAME_LEN];
245 
246 	sem_init(&main_sem, 0, 1);
247 	schedparam.sched_priority = CONFIG_NUM_COOP_PRIORITIES - 1;
248 	min_prio = sched_get_priority_min(schedpolicy);
249 	max_prio = sched_get_priority_max(schedpolicy);
250 
251 	ret = (min_prio < 0 || max_prio < 0 ||
252 			schedparam.sched_priority < min_prio ||
253 			schedparam.sched_priority > max_prio);
254 
255 	/* TESTPOINT: Check if scheduling priority is valid */
256 	zassert_false(ret,
257 			"Scheduling priority outside valid priority range");
258 
259 	/* TESTPOINTS: Try setting attributes before init */
260 	ret = pthread_attr_setschedparam(&attr[0], &schedparam);
261 	zassert_equal(ret, EINVAL, "uninitialized attr set!");
262 
263 	ret = pthread_attr_setdetachstate(&attr[0], PTHREAD_CREATE_JOINABLE);
264 	zassert_equal(ret, EINVAL, "uninitialized attr set!");
265 
266 	ret = pthread_attr_setschedpolicy(&attr[0], schedpolicy);
267 	zassert_equal(ret, EINVAL, "uninitialized attr set!");
268 
269 	/* TESTPOINT: Try setting attribute with empty stack */
270 	ret = pthread_attr_setstack(&attr[0], 0, STACKS);
271 	zassert_equal(ret, EACCES, "empty stack set!");
272 
273 	/* TESTPOINTS: Try getting attributes before init */
274 	ret = pthread_attr_getschedparam(&attr[0], &getschedparam);
275 	zassert_equal(ret, EINVAL, "uninitialized attr retrieved!");
276 
277 	ret = pthread_attr_getdetachstate(&attr[0], &dstate);
278 	zassert_equal(ret, EINVAL, "uninitialized attr retrieved!");
279 
280 	ret = pthread_attr_getschedpolicy(&attr[0], &policy);
281 	zassert_equal(ret, EINVAL, "uninitialized attr retrieved!");
282 
283 	ret = pthread_attr_getstack(&attr[0], &stackaddr, &stacksize);
284 	zassert_equal(ret, EINVAL, "uninitialized attr retrieved!");
285 
286 	ret = pthread_attr_getstacksize(&attr[0], &stacksize);
287 	zassert_equal(ret, EINVAL, "uninitialized attr retrieved!");
288 
289 	/* TESTPOINT: Try destroying attr before init */
290 	ret = pthread_attr_destroy(&attr[0]);
291 	zassert_equal(ret, EINVAL, "uninitialized attr destroyed!");
292 
293 	/* TESTPOINT: Try getting name of NULL thread (aka uninitialized
294 	 * thread var).
295 	 */
296 	ret = pthread_getname_np(NULL, thr_name_buf, sizeof(thr_name_buf));
297 	zassert_equal(ret, ESRCH, "uninitialized getname!");
298 
299 	/* TESTPOINT: Try setting name of NULL thread (aka uninitialized
300 	 * thread var).
301 	 */
302 	ret = pthread_setname_np(NULL, thr_name);
303 	zassert_equal(ret, ESRCH, "uninitialized setname!");
304 
305 	/* TESTPOINT: Try creating thread before attr init */
306 	ret = pthread_create(&newthread[0], &attr[0],
307 				thread_top_exec, NULL);
308 	zassert_equal(ret, EINVAL, "thread created before attr init!");
309 
310 	for (i = 0; i < N_THR_E; i++) {
311 		ret = pthread_attr_init(&attr[i]);
312 		if (ret != 0) {
313 			zassert_false(pthread_attr_destroy(&attr[i]),
314 				      "Unable to destroy pthread object attrib");
315 			zassert_false(pthread_attr_init(&attr[i]),
316 				      "Unable to create pthread object attrib");
317 		}
318 
319 		/* TESTPOINTS: Retrieve set stack attributes and compare */
320 		pthread_attr_setstack(&attr[i], &stack_e[i][0], STACKS);
321 		pthread_attr_getstack(&attr[i], &stackaddr, &stacksize);
322 		zassert_equal_ptr(attr[i].stack, stackaddr,
323 				"stack attribute addresses do not match!");
324 		zassert_equal(STACKS, stacksize, "stack sizes do not match!");
325 
326 		pthread_attr_getstacksize(&attr[i], &stacksize);
327 		zassert_equal(STACKS, stacksize, "stack sizes do not match!");
328 
329 		pthread_attr_setschedpolicy(&attr[i], schedpolicy);
330 		pthread_attr_getschedpolicy(&attr[i], &policy);
331 		zassert_equal(schedpolicy, policy,
332 				"scheduling policies do not match!");
333 
334 		pthread_attr_setschedparam(&attr[i], &schedparam);
335 		pthread_attr_getschedparam(&attr[i], &getschedparam);
336 		zassert_equal(schedparam.sched_priority,
337 			      getschedparam.sched_priority,
338 			      "scheduling priorities do not match!");
339 
340 		ret = pthread_create(&newthread[i], &attr[i], thread_top_exec,
341 				INT_TO_POINTER(i));
342 
343 		/* TESTPOINT: Check if thread is created successfully */
344 		zassert_false(ret, "Number of threads exceed max limit");
345 	}
346 
347 	/* TESTPOINT: Try getting thread name with no buffer */
348 	ret = pthread_getname_np(newthread[0], NULL, sizeof(thr_name_buf));
349 	zassert_equal(ret, EINVAL, "uninitialized getname!");
350 
351 	/* TESTPOINT: Try setting thread name with no buffer */
352 	ret = pthread_setname_np(newthread[0], NULL);
353 	zassert_equal(ret, EINVAL, "uninitialized setname!");
354 
355 	/* TESTPOINT: Try setting thread name */
356 	ret = pthread_setname_np(newthread[0], thr_name);
357 	zassert_false(ret, "Set thread name failed!");
358 
359 	/* TESTPOINT: Try getting thread name */
360 	ret = pthread_getname_np(newthread[0], thr_name_buf,
361 				 sizeof(thr_name_buf));
362 	zassert_false(ret, "Get thread name failed!");
363 
364 	/* TESTPOINT: Thread names match */
365 	ret = strncmp(thr_name, thr_name_buf, min(strlen(thr_name),
366 						  strlen(thr_name_buf)));
367 	zassert_false(ret, "Thread names don't match!");
368 
369 	while (!bounce_test_done()) {
370 		sem_wait(&main_sem);
371 	}
372 
373 	/* TESTPOINT: Check if bounce test passes */
374 	zassert_false(bounce_failed, "Bounce test failed");
375 
376 	printk("Bounce test OK\n");
377 
378 	/* Wake up the worker threads */
379 	pthread_mutex_lock(&lock);
380 	pthread_cond_broadcast(&cvar1);
381 	pthread_mutex_unlock(&lock);
382 
383 	while (!barrier_test_done()) {
384 		sem_wait(&main_sem);
385 	}
386 
387 	/* TESTPOINT: Check if barrier test passes */
388 	zassert_false(barrier_failed, "Barrier test failed");
389 
390 	for (i = 0; i < N_THR_E; i++) {
391 		pthread_join(newthread[i], &retval);
392 	}
393 
394 	for (i = 0; i < N_THR_E; i++) {
395 		if (barrier_return[i] == PTHREAD_BARRIER_SERIAL_THREAD) {
396 			++serial_threads;
397 		}
398 	}
399 
400 	/* TESTPOINT: Check only one PTHREAD_BARRIER_SERIAL_THREAD returned. */
401 	zassert_true(serial_threads == 1, "Bungled barrier return value(s)");
402 
403 	printk("Barrier test OK\n");
404 }
405 
test_posix_pthread_error_condition(void)406 void test_posix_pthread_error_condition(void)
407 {
408 	pthread_attr_t attr;
409 	struct sched_param param;
410 	void *stackaddr;
411 	size_t stacksize;
412 	int policy, detach;
413 	static pthread_once_t key = 1;
414 
415 	/* TESTPOINT: invoke pthread APIs with NULL */
416 	zassert_equal(pthread_attr_destroy(NULL), EINVAL,
417 		      "pthread destroy NULL error");
418 	zassert_equal(pthread_attr_getschedparam(NULL, &param), EINVAL,
419 		      "get scheduling param error");
420 	zassert_equal(pthread_attr_getstack(NULL, &stackaddr, &stacksize),
421 		      EINVAL, "get stack attributes error");
422 	zassert_equal(pthread_attr_getstacksize(NULL, &stacksize),
423 		      EINVAL, "get stack size error");
424 	zassert_equal(pthread_attr_setschedpolicy(NULL, 2),
425 		      EINVAL, "set scheduling policy error");
426 	zassert_equal(pthread_attr_getschedpolicy(NULL, &policy),
427 		      EINVAL, "get scheduling policy error");
428 	zassert_equal(pthread_attr_setdetachstate(NULL, 0),
429 		      EINVAL, "pthread set detach state with NULL error");
430 	zassert_equal(pthread_attr_getdetachstate(NULL, &detach),
431 		      EINVAL, "get datach state error");
432 	zassert_equal(pthread_detach(NULL), ESRCH, "detach with NULL error");
433 	zassert_equal(pthread_attr_init(NULL), ENOMEM,
434 		      "init with NULL error");
435 	zassert_equal(pthread_attr_setschedparam(NULL, &param), EINVAL,
436 		      "set sched param with NULL error");
437 	zassert_equal(pthread_cancel(NULL), ESRCH,
438 		      "cancel NULL error");
439 	zassert_equal(pthread_join(NULL, NULL), ESRCH,
440 		      "join with NULL has error");
441 	zassert_false(pthread_once(&key, NULL),
442 		      "pthread dynamic package initialization error");
443 	zassert_equal(pthread_getschedparam(NULL, &policy, &param), ESRCH,
444 		      "get schedparam with NULL error");
445 	zassert_equal(pthread_setschedparam(NULL, policy, &param), ESRCH,
446 		      "set schedparam with NULL error");
447 
448 	attr.initialized = 0U;
449 	zassert_equal(pthread_attr_getdetachstate(&attr, &detach),
450 		      EINVAL, "get datach state error");
451 
452 	/* Initialise thread attribute to ensure won't be return with init error */
453 	zassert_false(pthread_attr_init(&attr),
454 		      "Unable to create pthread object attr");
455 	zassert_false(pthread_attr_setschedpolicy(&attr, 0),
456 		      "set scheduling policy error");
457 	zassert_false(pthread_attr_setschedpolicy(&attr, 1),
458 		      "set scheduling policy error");
459 	zassert_equal(pthread_attr_setschedpolicy(&attr, 2),
460 		      EINVAL, "set scheduling policy error");
461 	zassert_false(pthread_attr_setdetachstate(&attr, 1),
462 		      "set detach state error");
463 	zassert_false(pthread_attr_setdetachstate(&attr, 2),
464 		      "set detach state error");
465 	zassert_equal(pthread_attr_setdetachstate(&attr, 3),
466 		      EINVAL, "set detach state error");
467 	zassert_false(pthread_attr_getdetachstate(&attr, &detach),
468 		      "get datach state error");
469 }
470 
test_posix_pthread_termination(void)471 void test_posix_pthread_termination(void)
472 {
473 	int32_t i, ret;
474 	int oldstate, policy;
475 	pthread_attr_t attr[N_THR_T];
476 	struct sched_param schedparam;
477 	pthread_t newthread[N_THR_T];
478 	void *retval;
479 
480 	/* Creating 4 threads with lowest application priority */
481 	for (i = 0; i < N_THR_T; i++) {
482 		ret = pthread_attr_init(&attr[i]);
483 		if (ret != 0) {
484 			zassert_false(pthread_attr_destroy(&attr[i]),
485 				      "Unable to destroy pthread object attrib");
486 			zassert_false(pthread_attr_init(&attr[i]),
487 				      "Unable to create pthread object attrib");
488 		}
489 
490 		if (i == 2) {
491 			pthread_attr_setdetachstate(&attr[i],
492 						    PTHREAD_CREATE_DETACHED);
493 		}
494 
495 		schedparam.sched_priority = 2;
496 		pthread_attr_setschedparam(&attr[i], &schedparam);
497 		pthread_attr_setstack(&attr[i], &stack_t[i][0], STACKS);
498 		ret = pthread_create(&newthread[i], &attr[i], thread_top_term,
499 				     INT_TO_POINTER(i));
500 
501 		zassert_false(ret, "Not enough space to create new thread");
502 	}
503 
504 	/* TESTPOINT: Try setting invalid cancel state to current thread */
505 	ret = pthread_setcancelstate(PTHREAD_CANCEL_INVALID, &oldstate);
506 	zassert_equal(ret, EINVAL, "invalid cancel state set!");
507 
508 	/* TESTPOINT: Try setting invalid policy */
509 	ret = pthread_setschedparam(newthread[0], SCHED_INVALID, &schedparam);
510 	zassert_equal(ret, EINVAL, "invalid policy set!");
511 
512 	/* TESTPOINT: Try setting invalid priority */
513 	schedparam.sched_priority = PRIO_INVALID;
514 	ret = pthread_setschedparam(newthread[0], SCHED_RR, &schedparam);
515 	zassert_equal(ret, EINVAL, "invalid priority set!");
516 
517 	for (i = 0; i < N_THR_T; i++) {
518 		pthread_join(newthread[i], &retval);
519 	}
520 
521 	/* TESTPOINT: Test for deadlock */
522 	ret = pthread_join(pthread_self(), &retval);
523 	zassert_equal(ret, EDEADLK, "thread joined with self inexplicably!");
524 
525 	/* TESTPOINT: Try canceling a terminated thread */
526 	ret = pthread_cancel(newthread[N_THR_T/2]);
527 	zassert_equal(ret, ESRCH, "cancelled a terminated thread!");
528 
529 	/* TESTPOINT: Try getting scheduling info from terminated thread */
530 	ret = pthread_getschedparam(newthread[N_THR_T/2], &policy, &schedparam);
531 	zassert_equal(ret, ESRCH, "got attr from terminated thread!");
532 }
533 
create_thread1(void * p1)534 static void *create_thread1(void *p1)
535 {
536 	/* do nothing */
537 	return NULL;
538 }
539 
test_posix_pthread_create_negative(void)540 void test_posix_pthread_create_negative(void)
541 {
542 	int ret;
543 	pthread_t pthread1;
544 	pthread_attr_t attr1;
545 
546 	/* create pthread without attr initialized */
547 	ret = pthread_create(&pthread1, NULL, create_thread1, (void *)1);
548 	zassert_equal(ret, EINVAL, "create thread with NULL successful");
549 
550 	/* initialized attr without set stack to create thread */
551 	ret = pthread_attr_init(&attr1);
552 	zassert_false(ret, "attr1 initialized failed");
553 
554 	attr1.stack = NULL;
555 	ret = pthread_create(&pthread1, &attr1, create_thread1, (void *)1);
556 	zassert_equal(ret, EINVAL, "create successful with NULL attr");
557 
558 	/* set stack size 0 to create thread */
559 	pthread_attr_setstack(&attr1, &stack_1, 0);
560 	ret = pthread_create(&pthread1, &attr1, create_thread1, (void *)1);
561 	zassert_equal(ret, EINVAL, "create thread with 0 size");
562 }
563 
test_pthread_descriptor_leak(void)564 void test_pthread_descriptor_leak(void)
565 {
566 	void *unused;
567 	pthread_t pthread1;
568 	pthread_attr_t attr;
569 
570 	zassert_ok(pthread_attr_init(&attr), NULL);
571 	zassert_ok(pthread_attr_setstack(&attr, &stack_e[0][0], STACKS), NULL);
572 
573 	/* If we are leaking descriptors, then this loop will never complete */
574 	for (size_t i = 0; i < CONFIG_MAX_PTHREAD_COUNT * 2; ++i) {
575 		zassert_ok(pthread_create(&pthread1, &attr, create_thread1, NULL),
576 			   "unable to create thread %zu", i);
577 		zassert_ok(pthread_join(pthread1, &unused), "unable to join thread %zu", i);
578 	}
579 }
580 
test_sched_policy(void)581 void test_sched_policy(void)
582 {
583 	/*
584 	 * TODO:
585 	 * 1. assert that _POSIX_PRIORITY_SCHEDULING is defined
586 	 * 2. if _POSIX_SPORADIC_SERVER or _POSIX_THREAD_SPORADIC_SERVER are defined,
587 	 *    also check SCHED_SPORADIC
588 	 * 3. SCHED_OTHER is mandatory (but may be equivalent to SCHED_FIFO or SCHED_RR,
589 	 *    and is implementation defined)
590 	 */
591 
592 	int pmin;
593 	int pmax;
594 	pthread_t th;
595 	pthread_attr_t attr;
596 	struct sched_param param;
597 	static const int policies[] = {
598 		SCHED_FIFO,
599 		SCHED_RR,
600 		SCHED_INVALID,
601 	};
602 	static const char *const policy_names[] = {
603 		"SCHED_FIFO",
604 		"SCHED_RR",
605 		"SCHED_INVALID",
606 	};
607 	static const bool policy_enabled[] = {
608 		IS_ENABLED(CONFIG_COOP_ENABLED),
609 		IS_ENABLED(CONFIG_PREEMPT_ENABLED),
610 		false,
611 	};
612 	static int nprio[] = {
613 		CONFIG_NUM_COOP_PRIORITIES,
614 		CONFIG_NUM_PREEMPT_PRIORITIES,
615 		42,
616 	};
617 	const char *const prios[] = {"pmin", "pmax"};
618 
619 	BUILD_ASSERT(!(SCHED_INVALID == SCHED_FIFO || SCHED_INVALID == SCHED_RR),
620 		     "SCHED_INVALID is itself invalid");
621 
622 	for (int policy = 0; policy < ARRAY_SIZE(policies); ++policy) {
623 		if (!policy_enabled[policy]) {
624 			/* test degenerate cases */
625 			errno = 0;
626 			zassert_equal(-1, sched_get_priority_min(policies[policy]),
627 				      "expected sched_get_priority_min(%s) to fail",
628 				      policy_names[policy]);
629 			zassert_equal(EINVAL, errno, "sched_get_priority_min(%s) did not set errno",
630 				      policy_names[policy]);
631 
632 			errno = 0;
633 			zassert_equal(-1, sched_get_priority_max(policies[policy]),
634 				      "expected sched_get_priority_max(%s) to fail",
635 				      policy_names[policy]);
636 			zassert_equal(EINVAL, errno, "sched_get_priority_max(%s) did not set errno",
637 				      policy_names[policy]);
638 			continue;
639 		}
640 
641 		/* get pmin and pmax for policies[policy] */
642 		for (int i = 0; i < 2; ++i) {
643 			errno = 0;
644 			if (i == 0) {
645 				pmin = sched_get_priority_min(policies[policy]);
646 				param.sched_priority = pmin;
647 			} else {
648 				pmax = sched_get_priority_max(policies[policy]);
649 				param.sched_priority = pmax;
650 			}
651 
652 			zassert_not_equal(-1, param.sched_priority,
653 					  "sched_get_priority_%s(%s) failed: %d",
654 					  i == 0 ? "min" : "max", policy_names[policy], errno);
655 			zassert_equal(0, errno, "sched_get_priority_%s(%s) set errno to %s",
656 				   i == 0 ? "min" : "max", policy_names[policy], errno);
657 		}
658 
659 		/*
660 		 * IEEE 1003.1-2008 Section 2.8.4
661 		 * conforming implementations should provide a range of at least 32 priorities
662 		 *
663 		 * Note: we relax this requirement
664 		 */
665 		zassert_true(pmax > pmin, "pmax (%d) <= pmin (%d)", pmax, pmin,
666 			     "%s min/max inconsistency: pmin: %d pmax: %d", policy_names[policy],
667 			     pmin, pmax);
668 
669 		/*
670 		 * Getting into the weeds a bit (i.e. whitebox testing), Zephyr
671 		 * cooperative threads use [-CONFIG_NUM_COOP_PRIORITIES,-1] and
672 		 * preemptive threads use [0, CONFIG_NUM_PREEMPT_PRIORITIES - 1],
673 		 * where the more negative thread has the higher priority. Since we
674 		 * cannot map those directly (a return value of -1 indicates error),
675 		 * we simply map those to the positive space.
676 		 */
677 		zassert_equal(pmin, 0, "unexpected pmin for %s", policy_names[policy]);
678 		zassert_equal(pmax, nprio[policy] - 1, "unexpected pmax for %s",
679 			      policy_names[policy]); /* test happy paths */
680 
681 		for (int i = 0; i < 2; ++i) {
682 			/* create threads with min and max priority levels */
683 			zassert_equal(0, pthread_attr_init(&attr),
684 				   "pthread_attr_init() failed for %s (%d) of %s", prios[i],
685 				   param.sched_priority, policy_names[policy]);
686 
687 			zassert_equal(0, pthread_attr_setschedpolicy(&attr, policies[policy]),
688 				   "pthread_attr_setschedpolicy() failed for %s (%d) of %s",
689 				   prios[i], param.sched_priority, policy_names[policy]);
690 
691 			zassert_equal(0, pthread_attr_setschedparam(&attr, &param),
692 				   "pthread_attr_setschedparam() failed for %s (%d) of %s",
693 				   prios[i], param.sched_priority, policy_names[policy]);
694 
695 			zassert_equal(0, pthread_attr_setstack(&attr, &stack_e[0][0], STACKS),
696 				   "pthread_attr_setstack() failed for %s (%d) of %s", prios[i],
697 				   param.sched_priority, policy_names[policy]);
698 
699 			zassert_equal(0, pthread_create(&th, &attr, create_thread1, NULL),
700 				   "pthread_create() failed for %s (%d) of %s", prios[i],
701 				   param.sched_priority, policy_names[policy]);
702 
703 			zassert_equal(0, pthread_join(th, NULL),
704 				   "pthread_join() failed for %s (%d) of %s", prios[i],
705 				   param.sched_priority, policy_names[policy]);
706 		}
707 	}
708 }
709