1 /*
2 * Copyright (c) 2024, Meta
3 * Copyright (c) 2024, Marvin Ouma <pancakesdeath@protonmail.com>
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <pthread.h>
9
10 #include <zephyr/sys/util.h>
11 #include <zephyr/ztest.h>
12
13 #define BIOS_FOOD 0xB105F00D
14
15 static bool attr_valid;
16 static pthread_attr_t attr;
17 static const pthread_attr_t uninit_attr;
18 static bool detached_thread_has_finished;
19
20 /*
21 * This should be discarded by the linker, in this specific testsuite, if
22 * CONFIG_DYNAMIC_THREAD_ALLOC is not set
23 */
24 #define STATIC_THREAD_STACK_SIZE (MAX(1024, PTHREAD_STACK_MIN + CONFIG_TEST_EXTRA_STACK_SIZE))
25 static K_THREAD_STACK_DEFINE(static_thread_stack, STATIC_THREAD_STACK_SIZE);
26
thread_entry(void * arg)27 static void *thread_entry(void *arg)
28 {
29 bool joinable = (bool)POINTER_TO_UINT(arg);
30
31 if (!joinable) {
32 detached_thread_has_finished = true;
33 }
34
35 return NULL;
36 }
37
create_thread_common_entry(const pthread_attr_t * attrp,bool expect_success,bool joinable,void * (* entry)(void * arg),void * arg)38 static void create_thread_common_entry(const pthread_attr_t *attrp, bool expect_success,
39 bool joinable, void *(*entry)(void *arg), void *arg)
40 {
41 pthread_t th;
42
43 if (!joinable) {
44 detached_thread_has_finished = false;
45 }
46
47 if (expect_success) {
48 zassert_ok(pthread_create(&th, attrp, entry, arg));
49 } else {
50 zassert_not_ok(pthread_create(&th, attrp, entry, arg));
51 return;
52 }
53
54 if (joinable) {
55 zassert_ok(pthread_join(th, NULL), "failed to join joinable thread");
56 return;
57 }
58
59 /* should not be able to join detached thread */
60 zassert_not_ok(pthread_join(th, NULL));
61
62 for (size_t i = 0; i < 10; ++i) {
63 k_msleep(2 * CONFIG_PTHREAD_RECYCLER_DELAY_MS);
64 if (detached_thread_has_finished) {
65 break;
66 }
67 }
68
69 zassert_true(detached_thread_has_finished, "detached thread did not seem to finish");
70 }
71
create_thread_common(const pthread_attr_t * attrp,bool expect_success,bool joinable)72 static void create_thread_common(const pthread_attr_t *attrp, bool expect_success, bool joinable)
73 {
74 create_thread_common_entry(attrp, expect_success, joinable, thread_entry,
75 UINT_TO_POINTER(joinable));
76 }
77
can_create_thread(const pthread_attr_t * attrp)78 static inline void can_create_thread(const pthread_attr_t *attrp)
79 {
80 create_thread_common(attrp, true, true);
81 }
82
ZTEST(xsi_threads_ext,test_pthread_attr_getstack)83 ZTEST(xsi_threads_ext, test_pthread_attr_getstack)
84 {
85 void *stackaddr = (void *)BIOS_FOOD;
86 size_t stacksize = BIOS_FOOD;
87
88 /* degenerate cases */
89 {
90 if (false) {
91 /* undefined behaviour */
92 zassert_equal(pthread_attr_getstack(NULL, NULL, NULL), EINVAL);
93 zassert_equal(pthread_attr_getstack(NULL, NULL, &stacksize), EINVAL);
94 zassert_equal(pthread_attr_getstack(NULL, &stackaddr, NULL), EINVAL);
95 zassert_equal(pthread_attr_getstack(NULL, &stackaddr, &stacksize), EINVAL);
96 zassert_equal(pthread_attr_getstack(&uninit_attr, &stackaddr, &stacksize),
97 EINVAL);
98 }
99 zassert_equal(pthread_attr_getstack(&attr, NULL, NULL), EINVAL);
100 zassert_equal(pthread_attr_getstack(&attr, NULL, &stacksize), EINVAL);
101 zassert_equal(pthread_attr_getstack(&attr, &stackaddr, NULL), EINVAL);
102 }
103
104 zassert_ok(pthread_attr_getstack(&attr, &stackaddr, &stacksize));
105 zassert_not_equal(stackaddr, (void *)BIOS_FOOD);
106 zassert_not_equal(stacksize, BIOS_FOOD);
107 }
108
ZTEST(xsi_threads_ext,test_pthread_attr_setstack)109 ZTEST(xsi_threads_ext, test_pthread_attr_setstack)
110 {
111 void *stackaddr;
112 size_t stacksize;
113 void *new_stackaddr;
114 size_t new_stacksize;
115
116 /* valid values */
117 zassert_ok(pthread_attr_getstack(&attr, &stackaddr, &stacksize));
118
119 /* degenerate cases */
120 {
121 if (false) {
122 /* undefined behaviour */
123 zassert_equal(pthread_attr_setstack(NULL, NULL, 0), EACCES);
124 zassert_equal(pthread_attr_setstack(NULL, NULL, stacksize), EINVAL);
125 zassert_equal(pthread_attr_setstack(NULL, stackaddr, 0), EINVAL);
126 zassert_equal(pthread_attr_setstack(NULL, stackaddr, stacksize), EINVAL);
127 zassert_equal(pthread_attr_setstack((pthread_attr_t *)&uninit_attr,
128 stackaddr, stacksize),
129 EINVAL);
130 }
131 zassert_equal(pthread_attr_setstack(&attr, NULL, 0), EACCES);
132 zassert_equal(pthread_attr_setstack(&attr, NULL, stacksize), EACCES);
133 zassert_equal(pthread_attr_setstack(&attr, stackaddr, 0), EINVAL);
134 }
135
136 /* ensure we can create and join a thread with the default attrs */
137 can_create_thread(&attr);
138
139 /* set stack / addr to the current values of stack / addr */
140 zassert_ok(pthread_attr_setstack(&attr, stackaddr, stacksize));
141 can_create_thread(&attr);
142
143 /* qemu_x86 seems to be unable to set thread stacks to be anything less than 4096 */
144 if (!IS_ENABLED(CONFIG_X86)) {
145 /*
146 * check we can set a smaller stacksize
147 * should not require dynamic reallocation
148 * size may get rounded up to some alignment internally
149 */
150 zassert_ok(pthread_attr_setstack(&attr, stackaddr, stacksize - 1));
151 /* ensure we read back the same values as we specified */
152 zassert_ok(pthread_attr_getstack(&attr, &new_stackaddr, &new_stacksize));
153 zassert_equal(new_stackaddr, stackaddr);
154 zassert_equal(new_stacksize, stacksize - 1);
155 can_create_thread(&attr);
156 }
157
158 if (IS_ENABLED(CONFIG_DYNAMIC_THREAD_ALLOC)) {
159 /* ensure we can set a dynamic stack */
160 k_thread_stack_t *stack;
161
162 stack = k_thread_stack_alloc(2 * stacksize, 0);
163 zassert_not_null(stack);
164
165 zassert_ok(pthread_attr_setstack(&attr, (void *)stack, 2 * stacksize));
166 /* ensure we read back the same values as we specified */
167 zassert_ok(pthread_attr_getstack(&attr, &new_stackaddr, &new_stacksize));
168 zassert_equal(new_stackaddr, (void *)stack);
169 zassert_equal(new_stacksize, 2 * stacksize);
170 can_create_thread(&attr);
171 }
172 }
173
ZTEST(xsi_threads_ext,test_pthread_set_get_concurrency)174 ZTEST(xsi_threads_ext, test_pthread_set_get_concurrency)
175 {
176 /* EINVAL if the value specified by new_level is negative */
177 zassert_equal(EINVAL, pthread_setconcurrency(-42));
178
179 /*
180 * Note: the special value 0 indicates the implementation will
181 * maintain the concurrency level at its own discretion.
182 *
183 * pthread_getconcurrency() should return a value of 0 on init.
184 */
185 zassert_equal(0, pthread_getconcurrency());
186
187 for (int i = 0; i <= CONFIG_MP_MAX_NUM_CPUS; ++i) {
188 zassert_ok(pthread_setconcurrency(i));
189 /* verify parameter is saved */
190 zassert_equal(i, pthread_getconcurrency());
191 }
192
193 /* EAGAIN if the a system resource to be exceeded */
194 zassert_equal(EAGAIN, pthread_setconcurrency(CONFIG_MP_MAX_NUM_CPUS + 1));
195 }
196
ZTEST(xsi_threads_ext,test_pthread_attr_getstacksize)197 ZTEST(xsi_threads_ext, test_pthread_attr_getstacksize)
198 {
199 size_t stacksize = BIOS_FOOD;
200
201 /* degenerate cases */
202 {
203 if (false) {
204 /* undefined behaviour */
205 zassert_equal(pthread_attr_getstacksize(NULL, NULL), EINVAL);
206 zassert_equal(pthread_attr_getstacksize(NULL, &stacksize), EINVAL);
207 zassert_equal(pthread_attr_getstacksize(&uninit_attr, &stacksize), EINVAL);
208 }
209 zassert_equal(pthread_attr_getstacksize(&attr, NULL), EINVAL);
210 }
211
212 zassert_ok(pthread_attr_getstacksize(&attr, &stacksize));
213 zassert_not_equal(stacksize, BIOS_FOOD);
214 }
215
ZTEST(xsi_threads_ext,test_pthread_attr_setstacksize)216 ZTEST(xsi_threads_ext, test_pthread_attr_setstacksize)
217 {
218 size_t stacksize;
219 size_t new_stacksize;
220
221 /* valid size */
222 zassert_ok(pthread_attr_getstacksize(&attr, &stacksize));
223
224 /* degenerate cases */
225 {
226 if (false) {
227 /* undefined behaviour */
228 zassert_equal(pthread_attr_setstacksize(NULL, 0), EINVAL);
229 zassert_equal(pthread_attr_setstacksize(NULL, stacksize), EINVAL);
230 zassert_equal(pthread_attr_setstacksize((pthread_attr_t *)&uninit_attr,
231 stacksize),
232 EINVAL);
233 }
234 zassert_equal(pthread_attr_setstacksize(&attr, 0), EINVAL);
235 }
236
237 /* ensure we can spin up a thread with the default stack size */
238 can_create_thread(&attr);
239
240 /* set stack / addr to the current values of stack / addr */
241 zassert_ok(pthread_attr_setstacksize(&attr, stacksize));
242 /* ensure we can read back the values we just set */
243 zassert_ok(pthread_attr_getstacksize(&attr, &new_stacksize));
244 zassert_equal(new_stacksize, stacksize);
245 can_create_thread(&attr);
246
247 /* qemu_x86 seems to be unable to set thread stacks to be anything less than 4096 */
248 if (!IS_ENABLED(CONFIG_X86)) {
249 zassert_ok(pthread_attr_setstacksize(&attr, stacksize - 1));
250 /* ensure we can read back the values we just set */
251 zassert_ok(pthread_attr_getstacksize(&attr, &new_stacksize));
252 zassert_equal(new_stacksize, stacksize - 1);
253 can_create_thread(&attr);
254 }
255
256 if (IS_ENABLED(CONFIG_DYNAMIC_THREAD_ALLOC)) {
257 zassert_ok(pthread_attr_setstacksize(&attr, 2 * stacksize));
258 /* ensure we read back the same values as we specified */
259 zassert_ok(pthread_attr_getstacksize(&attr, &new_stacksize));
260 zassert_equal(new_stacksize, 2 * stacksize);
261 can_create_thread(&attr);
262 }
263 }
264
before(void * arg)265 static void before(void *arg)
266 {
267 ARG_UNUSED(arg);
268
269 zassert_ok(pthread_attr_init(&attr));
270 if (!IS_ENABLED(CONFIG_DYNAMIC_THREAD_ALLOC)) {
271 zassert_ok(pthread_attr_setstack(&attr, &static_thread_stack,
272 STATIC_THREAD_STACK_SIZE));
273 }
274 attr_valid = true;
275 }
276
after(void * arg)277 static void after(void *arg)
278 {
279 ARG_UNUSED(arg);
280
281 if (attr_valid) {
282 (void)pthread_attr_destroy(&attr);
283 attr_valid = false;
284 }
285 }
286
287 ZTEST_SUITE(xsi_threads_ext, NULL, NULL, before, after, NULL);
288