1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/portability/cmsis_os2.h>
10 #include <zephyr/portability/cmsis_types.h>
11 
12 #define STACKSZ CONFIG_CMSIS_V2_THREAD_MAX_STACK_SIZE
13 
14 /* This is used to check the thread yield functionality between 2 threads */
15 static int thread_yield_check;
16 static int thread_yield_check_dynamic;
17 
18 static K_THREAD_STACK_DEFINE(test_stack1, STACKSZ);
19 static osThreadAttr_t os_thread1_attr = {
20 	.name = "Thread1",
21 	.stack_mem = &test_stack1,
22 	.stack_size = STACKSZ,
23 	.priority = osPriorityHigh,
24 };
25 
26 static K_THREAD_STACK_DEFINE(test_stack2, STACKSZ);
27 static osThreadAttr_t os_thread2_attr = {
28 	.name = "Thread2",
29 	.stack_mem = &test_stack2,
30 	.stack_size = STACKSZ,
31 	.priority = osPriorityHigh,
32 };
33 
34 struct thread1_args {
35 	int *yield_check;
36 	const char *name;
37 };
38 
thread1(void * argument)39 static void thread1(void *argument)
40 {
41 	osStatus_t status;
42 	osThreadId_t thread_id;
43 	const char *name;
44 	struct thread1_args *args = (struct thread1_args *)argument;
45 
46 	thread_id = osThreadGetId();
47 	zassert_true(thread_id != NULL, "Failed getting Thread ID");
48 
49 	name = osThreadGetName(thread_id);
50 	zassert_str_equal(args->name, name, "Failed getting Thread name");
51 
52 	/* This thread starts off at a high priority (same as thread2) */
53 	(*args->yield_check)++;
54 	zassert_equal(*args->yield_check, 1);
55 
56 	/* Yield to thread2 which is of same priority */
57 	status = osThreadYield();
58 	zassert_true(status == osOK, "Error doing thread yield");
59 
60 	/* thread_yield_check should now be 2 as it was incremented
61 	 * in thread2.
62 	 */
63 	zassert_equal(*args->yield_check, 2);
64 
65 	osThreadExit();
66 }
67 
thread2(void * argument)68 static void thread2(void *argument)
69 {
70 	uint32_t i, num_threads, max_num_threads = 5U;
71 	osThreadId_t *thread_array;
72 	int *yield_check = (int *)argument;
73 
74 	/* By now thread1 would have set thread_yield_check to 1 and would
75 	 * have yielded the CPU. Incrementing it over here would essentially
76 	 * confirm that the yield was indeed executed.
77 	 */
78 	(*yield_check)++;
79 
80 	thread_array = k_calloc(max_num_threads, sizeof(osThreadId_t));
81 	num_threads = osThreadEnumerate(thread_array, max_num_threads);
82 	zassert_equal(num_threads, 2, "Incorrect number of cmsis rtos v2 threads");
83 
84 	for (i = 0U; i < num_threads; i++) {
85 		uint32_t size = osThreadGetStackSize(thread_array[i]);
86 		uint32_t space = osThreadGetStackSpace(thread_array[i]);
87 
88 		zassert_true(space < size, "stack size remaining is not what is expected");
89 	}
90 
91 	zassert_equal(osThreadGetState(thread_array[1]), osThreadReady,
92 		      "Thread not in ready state");
93 	zassert_equal(osThreadGetState(thread_array[0]), osThreadRunning,
94 		      "Thread not in running state");
95 
96 	zassert_equal(osThreadSuspend(thread_array[1]), osOK, "");
97 	zassert_equal(osThreadGetState(thread_array[1]), osThreadBlocked,
98 		      "Thread not in blocked state");
99 
100 	zassert_equal(osThreadResume(thread_array[1]), osOK, "");
101 	zassert_equal(osThreadGetState(thread_array[1]), osThreadReady,
102 		      "Thread not in ready state");
103 
104 	k_free(thread_array);
105 
106 	/* Yield back to thread1 which is of same priority */
107 	osThreadYield();
108 }
109 
thread_apis_common(int * yield_check,const char * thread1_name,osThreadAttr_t * thread1_attr,osThreadAttr_t * thread2_attr)110 static void thread_apis_common(int *yield_check, const char *thread1_name,
111 			       osThreadAttr_t *thread1_attr, osThreadAttr_t *thread2_attr)
112 {
113 	osThreadId_t id1;
114 	osThreadId_t id2;
115 
116 	struct thread1_args args = {.yield_check = yield_check, .name = thread1_name};
117 
118 	id1 = osThreadNew(thread1, &args, thread1_attr);
119 	zassert_true(id1 != NULL, "Failed creating thread1");
120 
121 	id2 = osThreadNew(thread2, yield_check, thread2_attr);
122 	zassert_true(id2 != NULL, "Failed creating thread2");
123 
124 	zassert_equal(osThreadGetCount(), 2, "Incorrect number of cmsis rtos v2 threads");
125 
126 	do {
127 		osDelay(100);
128 	} while (*yield_check != 2);
129 }
130 
ZTEST(cmsis_thread_apis,test_thread_apis_dynamic)131 ZTEST(cmsis_thread_apis, test_thread_apis_dynamic)
132 {
133 	thread_apis_common(&thread_yield_check_dynamic, "ZephyrThread", NULL, NULL);
134 }
135 
ZTEST(cmsis_thread_apis,test_thread_apis)136 ZTEST(cmsis_thread_apis, test_thread_apis)
137 {
138 	thread_apis_common(&thread_yield_check, os_thread1_attr.name, &os_thread1_attr,
139 			   &os_thread2_attr);
140 }
141 
142 static osPriority_t OsPriorityInvalid = 60;
143 
144 /* This is used to indicate the completion of processing for thread3 */
145 static int thread3_state;
146 static int thread3_state_dynamic;
147 
148 static K_THREAD_STACK_DEFINE(test_stack3, STACKSZ);
149 static osThreadAttr_t thread3_attr = {
150 	.name = "Thread3",
151 	.stack_mem = &test_stack3,
152 	.stack_size = STACKSZ,
153 	.priority = osPriorityNormal,
154 };
155 
thread3(void * argument)156 static void thread3(void *argument)
157 {
158 	osStatus_t status;
159 	osPriority_t rv;
160 	osThreadId_t id = osThreadGetId();
161 	osPriority_t prio = osThreadGetPriority(id);
162 	int *state = (int *)argument;
163 
164 	/* Lower the priority of the current thread */
165 	osThreadSetPriority(id, osPriorityBelowNormal);
166 	rv = osThreadGetPriority(id);
167 	zassert_equal(rv, osPriorityBelowNormal, "Expected priority to be changed to %d, not %d",
168 		      (int)osPriorityBelowNormal, (int)rv);
169 
170 	/* Increase the priority of the current thread */
171 	osThreadSetPriority(id, osPriorityAboveNormal);
172 	rv = osThreadGetPriority(id);
173 	zassert_equal(rv, osPriorityAboveNormal, "Expected priority to be changed to %d, not %d",
174 		      (int)osPriorityAboveNormal, (int)rv);
175 
176 	/* Restore the priority of the current thread */
177 	osThreadSetPriority(id, prio);
178 	rv = osThreadGetPriority(id);
179 	zassert_equal(rv, prio, "Expected priority to be changed to %d, not %d", (int)prio,
180 		      (int)rv);
181 
182 	/* Try to set unsupported priority and assert failure */
183 	status = osThreadSetPriority(id, OsPriorityInvalid);
184 	zassert_true(status == osErrorParameter, "Something's wrong with osThreadSetPriority!");
185 
186 	/* Indication that thread3 is done with its processing */
187 	*state = 1;
188 
189 	/* Keep looping till it gets killed */
190 	do {
191 		osDelay(100);
192 	} while (1);
193 }
194 
thread_prior_common(int * state,osThreadAttr_t * attr)195 static void thread_prior_common(int *state, osThreadAttr_t *attr)
196 {
197 	osStatus_t status;
198 	osThreadId_t id3;
199 
200 	id3 = osThreadNew(thread3, state, attr);
201 	zassert_true(id3 != NULL, "Failed creating thread3");
202 
203 	/* Keep delaying 10 milliseconds to ensure thread3 is done with
204 	 * its execution. It loops at the end and is terminated here.
205 	 */
206 	do {
207 		osDelay(10);
208 	} while (*state == 0);
209 
210 	status = osThreadTerminate(id3);
211 	zassert_true(status == osOK, "Error terminating thread3");
212 
213 	/* Try to set priority to inactive thread and assert failure */
214 	status = osThreadSetPriority(id3, osPriorityNormal);
215 	zassert_true(status == osErrorResource, "Something's wrong with osThreadSetPriority!");
216 
217 	/* Try to terminate inactive thread and assert failure */
218 	status = osThreadTerminate(id3);
219 	zassert_true(status == osErrorResource, "Something's wrong with osThreadTerminate!");
220 
221 	*state = 0;
222 }
223 
ZTEST(cmsis_thread_apis,test_thread_prio_dynamic)224 ZTEST(cmsis_thread_apis, test_thread_prio_dynamic)
225 {
226 	thread_prior_common(&thread3_state_dynamic, NULL);
227 }
228 
ZTEST(cmsis_thread_apis,test_thread_prio)229 ZTEST(cmsis_thread_apis, test_thread_prio)
230 {
231 	thread_prior_common(&thread3_state, &thread3_attr);
232 }
233 
234 #define DELAY_MS 1000
235 #define DELTA_MS 500
236 
thread5(void * argument)237 static void thread5(void *argument)
238 {
239 	printk(" * Thread B started.\n");
240 	osDelay(k_ms_to_ticks_ceil32(DELAY_MS));
241 	printk(" * Thread B joining...\n");
242 }
243 
thread4(void * argument)244 static void thread4(void *argument)
245 {
246 	osThreadId_t tB = argument;
247 	osStatus_t status;
248 
249 	printk(" + Thread A started.\n");
250 	status = osThreadJoin(tB);
251 	zassert_equal(status, osOK, "osThreadJoin thread B failed!");
252 	printk(" + Thread A joining...\n");
253 }
254 
ZTEST(cmsis_thread_apis,test_thread_join)255 ZTEST(cmsis_thread_apis, test_thread_join)
256 {
257 	osThreadAttr_t attr = {0};
258 	int64_t time_stamp;
259 	int64_t milliseconds_spent;
260 	osThreadId_t tA, tB;
261 	osStatus_t status;
262 
263 	attr.attr_bits = osThreadJoinable;
264 
265 	time_stamp = k_uptime_get();
266 
267 	printk(" - Creating thread B...\n");
268 	tB = osThreadNew(thread5, NULL, &attr);
269 	zassert_not_null(tB, "Failed to create thread B with osThreadNew!");
270 
271 	printk(" - Creating thread A...\n");
272 	attr.priority = osPriorityLow;
273 	tA = osThreadNew(thread4, tB, &attr);
274 	zassert_not_null(tA, "Failed to create thread A with osThreadNew!");
275 
276 	printk(" - Waiting for thread B to join...\n");
277 	status = osThreadJoin(tB);
278 	zassert_equal(status, osOK, "osThreadJoin thread B failed!");
279 
280 	if (status == osOK) {
281 		printk(" - Thread B joined.\n");
282 	}
283 
284 	milliseconds_spent = k_uptime_delta(&time_stamp);
285 	zassert_true((milliseconds_spent >= DELAY_MS - DELTA_MS) &&
286 			     (milliseconds_spent <= DELAY_MS + DELTA_MS),
287 		     "Join completed but was too fast or too slow.");
288 
289 	printk(" - Waiting for thread A to join...\n");
290 	status = osThreadJoin(tA);
291 	zassert_equal(status, osOK, "osThreadJoin thread A failed!");
292 
293 	if (status == osOK) {
294 		printk(" - Thread A joined.\n");
295 	}
296 }
297 
ZTEST(cmsis_thread_apis,test_thread_detached)298 ZTEST(cmsis_thread_apis, test_thread_detached)
299 {
300 	osThreadId_t thread;
301 	osStatus_t status;
302 
303 	thread = osThreadNew(thread5, NULL, NULL); /* osThreadDetached */
304 	zassert_not_null(thread, "Failed to create thread with osThreadNew!");
305 
306 	osDelay(k_ms_to_ticks_ceil32(DELAY_MS - DELTA_MS));
307 
308 	status = osThreadJoin(thread);
309 	zassert_equal(status, osErrorResource, "Incorrect status returned from osThreadJoin!");
310 
311 	osDelay(k_ms_to_ticks_ceil32(DELTA_MS));
312 }
313 
thread6(void * argument)314 void thread6(void *argument)
315 {
316 	osThreadId_t thread = argument;
317 	osStatus_t status;
318 
319 	/* Thread passed as argument may or may not have already terminated, but is expected to be
320 	 * joinable for this test as it was created with osThreadJoinable attr_bits.
321 	 */
322 	status = osThreadJoin(thread);
323 	zassert_equal(status, osOK, "Incorrect status returned from osThreadJoin!");
324 }
325 
ZTEST(cmsis_thread_apis,test_thread_joinable_detach)326 ZTEST(cmsis_thread_apis, test_thread_joinable_detach)
327 {
328 	osThreadAttr_t attr = {0};
329 	osThreadId_t tA, tB;
330 	osStatus_t status;
331 
332 	attr.attr_bits = osThreadJoinable;
333 
334 	tA = osThreadNew(thread5, NULL, &attr);
335 	zassert_not_null(tA, "Failed to create thread with osThreadNew!");
336 
337 	tB = osThreadNew(thread6, tA, &attr);
338 	zassert_not_null(tB, "Failed to create thread with osThreadNew!");
339 
340 	osDelay(k_ms_to_ticks_ceil32(DELAY_MS - DELTA_MS));
341 
342 	status = osThreadDetach(tA);
343 	zassert_equal(status, osOK, "osThreadDetach failed.");
344 
345 	osDelay(k_ms_to_ticks_ceil32(DELTA_MS));
346 }
347 
ZTEST(cmsis_thread_apis,test_thread_joinable_terminate)348 ZTEST(cmsis_thread_apis, test_thread_joinable_terminate)
349 {
350 	osThreadAttr_t attr = {0};
351 	osThreadId_t tA, tB;
352 	osStatus_t status;
353 
354 	attr.attr_bits = osThreadJoinable;
355 
356 	tA = osThreadNew(thread5, NULL, &attr);
357 	zassert_not_null(tA, "Failed to create thread with osThreadNew!");
358 
359 	tB = osThreadNew(thread6, tA, &attr);
360 	zassert_not_null(tB, "Failed to create thread with osThreadNew!");
361 
362 	osDelay(k_ms_to_ticks_ceil32(DELAY_MS - DELTA_MS));
363 
364 	status = osThreadTerminate(tA);
365 	zassert_equal(status, osOK, "osThreadTerminate failed.");
366 
367 	osDelay(k_ms_to_ticks_ceil32(DELTA_MS));
368 }
369 
370 static K_THREAD_STACK_DEFINE(test_stack7, STACKSZ);
371 static struct cmsis_rtos_thread_cb test_cb7;
372 static const osThreadAttr_t os_thread7_attr = {
373 	.name = "Thread7",
374 	.cb_mem = &test_cb7,
375 	.cb_size = sizeof(test_cb7),
376 	.stack_mem = &test_stack7,
377 	.stack_size = STACKSZ,
378 	.priority = osPriorityNormal,
379 };
thread7(void * argument)380 static void thread7(void *argument)
381 {
382 	printf("Thread 7 ran\n");
383 }
ZTEST(cmsis_thread_apis,test_thread_apis_static_allocation)384 ZTEST(cmsis_thread_apis, test_thread_apis_static_allocation)
385 {
386 	osThreadId_t id;
387 
388 	id = osThreadNew(thread7, NULL, &os_thread7_attr);
389 	zassert_not_null(id, "Failed to create thread with osThreadNew using static cb/stack");
390 }
391 
392 static K_THREAD_STACK_DEFINE(test_stack8, STACKSZ);
393 static struct cmsis_rtos_thread_cb test_cb8;
394 static const osThreadAttr_t os_thread8_attr = {
395 	.name = "Thread8",
396 	.attr_bits = osThreadJoinable,
397 	.cb_mem = &test_cb8,
398 	.cb_size = sizeof(test_cb8),
399 	.stack_mem = &test_stack8,
400 	.stack_size = STACKSZ,
401 	.priority = osPriorityNormal,
402 };
thread8(void * argument)403 static void thread8(void *argument)
404 {
405 	printf("Thread 8 ran\n");
406 	osDelay(k_ms_to_ticks_ceil32(DELAY_MS));
407 	osThreadExit();
408 }
ZTEST(cmsis_thread_apis,test_thread_apis_join_after_exit)409 ZTEST(cmsis_thread_apis, test_thread_apis_join_after_exit)
410 {
411 	osThreadId_t id;
412 	osStatus_t status;
413 
414 	id = osThreadNew(thread8, NULL, &os_thread8_attr);
415 	zassert_not_null(id, "Failed to create thread with osThreadNew using static cb/stack");
416 
417 	status = osThreadJoin(id);
418 	zassert_equal(status, osOK, "osThreadJoin failed with status=%d!", status);
419 }
420 
421 static K_THREAD_STACK_DEFINE(test_stack9, STACKSZ);
422 static struct cmsis_rtos_thread_cb test_cb9;
423 static const osThreadAttr_t os_thread9_attr = {
424 	.name = "Thread9",
425 	.attr_bits = osThreadJoinable,
426 	.cb_mem = &test_cb9,
427 	.cb_size = sizeof(test_cb9),
428 	.stack_mem = &test_stack9,
429 	.stack_size = STACKSZ,
430 	.priority = osPriorityNormal,
431 };
thread9(void * argument)432 static void thread9(void *argument)
433 {
434 	osThreadExit();
435 }
ZTEST(cmsis_thread_apis,test_thread_apis_multiple_new_static)436 ZTEST(cmsis_thread_apis, test_thread_apis_multiple_new_static)
437 {
438 	osThreadId_t id;
439 	osStatus_t status;
440 
441 	for (int i = 0; i < 100; i++) {
442 		id = osThreadNew(thread9, NULL, &os_thread9_attr);
443 		zassert_not_null(id,
444 				 "Failed to create thread with osThreadNew using static cb/stack");
445 
446 		status = osThreadJoin(id);
447 		zassert_equal(status, osOK, "osThreadJoin failed with status=%d!", status);
448 	}
449 }
450 ZTEST_SUITE(cmsis_thread_apis, NULL, NULL, NULL, NULL, NULL);
451