1 /*
2  * Copyright (c) 2023, Meta
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "thrd.h"
8 
9 #include <stdint.h>
10 #include <threads.h>
11 
12 #include <zephyr/sys_clock.h>
13 #include <zephyr/sys/timeutil.h>
14 #include <zephyr/ztest.h>
15 
16 static thrd_t     thr;
17 static uintptr_t  param;
18 
ZTEST(libc_thrd,test_thrd_sleep)19 ZTEST(libc_thrd, test_thrd_sleep)
20 {
21 	int64_t end;
22 	int64_t start;
23 	struct timespec duration = {0};
24 	struct timespec remaining;
25 	const uint16_t delay_ms[] = {0, 100, 200, 400};
26 
27 	if (false) {
28 		/* duration may not be NULL */
29 		zassert_not_equal(thrd_success, thrd_sleep(NULL, NULL));
30 	}
31 
32 	zassert_equal(thrd_success, thrd_sleep(&duration, NULL));
33 	zassert_equal(thrd_success, thrd_sleep(&duration, &duration));
34 
35 	for (int i = 0; i < ARRAY_SIZE(delay_ms); ++i) {
36 		timespec_from_timeout(K_MSEC(delay_ms[i]), &duration);
37 		remaining = (struct timespec){.tv_sec = 4242, .tv_nsec = 4242};
38 
39 		printk("sleeping %d ms\n", delay_ms[i]);
40 		start = k_uptime_get();
41 		zassert_equal(thrd_success, thrd_sleep(&duration, &remaining));
42 		end = k_uptime_get();
43 		zassert_equal(remaining.tv_sec, 0);
44 		zassert_equal(remaining.tv_nsec, 0);
45 		zassert_true(end - start >= delay_ms[i]);
46 	}
47 }
48 
thrd_create_join_fn(void * arg)49 static int thrd_create_join_fn(void *arg)
50 {
51 	uintptr_t *x = (uintptr_t *)arg;
52 
53 	if (x != NULL) {
54 		*x = BIOS_FOOD;
55 	}
56 
57 	return FORTY_TWO;
58 }
59 
ZTEST(libc_thrd,test_thrd_create_join)60 ZTEST(libc_thrd, test_thrd_create_join)
61 {
62 	int res = 0;
63 	thrd_start_t fun = thrd_create_join_fn;
64 
65 	param = 0;
66 
67 	if (false) {
68 		/* pthread_create() is not hardened for degenerate cases like this */
69 		zassert_equal(thrd_error, thrd_create(NULL, NULL, NULL));
70 		zassert_equal(thrd_error, thrd_create(NULL, NULL, &param));
71 		zassert_equal(thrd_error, thrd_create(NULL, fun, NULL));
72 		zassert_equal(thrd_error, thrd_create(NULL, fun, &param));
73 		zassert_equal(thrd_error, thrd_create(&thr, NULL, NULL));
74 		zassert_equal(thrd_error, thrd_create(&thr, NULL, &param));
75 	}
76 
77 	zassert_equal(thrd_success, thrd_create(&thr, fun, NULL));
78 	zassert_equal(thrd_success, thrd_join(thr, NULL));
79 
80 	zassert_equal(thrd_success, thrd_create(&thr, fun, &param));
81 	zassert_equal(thrd_success, thrd_join(thr, &res));
82 	zassert_equal(BIOS_FOOD, param, "expected: %u actual: %llu", BIOS_FOOD,
83 		     (unsigned long long)param);
84 	zassert_equal(FORTY_TWO, res);
85 }
86 
thrd_exit_fn(void * arg)87 static int thrd_exit_fn(void *arg)
88 {
89 	uintptr_t *x = (uintptr_t *)arg;
90 
91 	*x = BIOS_FOOD;
92 
93 	thrd_exit(SEVENTY_THREE);
94 
95 	return FORTY_TWO;
96 
97 	CODE_UNREACHABLE;
98 }
99 
ZTEST(libc_thrd,test_thrd_exit)100 ZTEST(libc_thrd, test_thrd_exit)
101 {
102 	int res = 0;
103 
104 	param = 0;
105 
106 	zassert_equal(thrd_success, thrd_create(&thr, thrd_exit_fn, &param));
107 	zassert_equal(thrd_success, thrd_join(thr, &res));
108 	zassert_equal(BIOS_FOOD, param);
109 	zassert_equal(SEVENTY_THREE, res);
110 }
111 
ZTEST(libc_thrd,test_thrd_yield)112 ZTEST(libc_thrd, test_thrd_yield)
113 {
114 	thrd_yield();
115 }
116 
117 static thrd_t child;
118 static thrd_t parent;
119 
thrd_current_equal_fn(void * arg)120 static int thrd_current_equal_fn(void *arg)
121 {
122 	ARG_UNUSED(arg);
123 
124 	zassert_equal(thrd_current(), child);
125 	zassert_not_equal(child, parent);
126 
127 	zassert_true(thrd_equal(thrd_current(), child));
128 	zassert_false(thrd_equal(child, parent));
129 
130 	return 0;
131 }
132 
ZTEST(libc_thrd,test_thrd_current_equal)133 ZTEST(libc_thrd, test_thrd_current_equal)
134 {
135 	parent = thrd_current();
136 
137 	zassert_equal(thrd_success, thrd_create(&child, thrd_current_equal_fn, NULL));
138 	zassert_equal(thrd_success, thrd_join(child, NULL));
139 }
140 
141 static bool detached_thread_is_probably_done;
142 
thrd_detach_fn(void * arg)143 static int thrd_detach_fn(void *arg)
144 {
145 	ARG_UNUSED(arg);
146 
147 	detached_thread_is_probably_done = true;
148 	return SEVENTY_THREE;
149 }
150 
ZTEST(libc_thrd,test_thrd_detach)151 ZTEST(libc_thrd, test_thrd_detach)
152 {
153 	zassert_equal(thrd_success, thrd_create(&thr, thrd_detach_fn, NULL));
154 	zassert_equal(thrd_success, thrd_detach(thr));
155 	zassert_equal(thrd_error, thrd_join(thr, NULL));
156 
157 	do {
158 		k_msleep(100);
159 	} while (!detached_thread_is_probably_done);
160 
161 	zassert_equal(thrd_error, thrd_join(thr, NULL));
162 }
163 
ZTEST(libc_thrd,test_thrd_reuse)164 ZTEST(libc_thrd, test_thrd_reuse)
165 {
166 	for (int i = 0; i < FORTY_TWO; ++i) {
167 		zassert_equal(thrd_success, thrd_create(&thr, thrd_create_join_fn, NULL));
168 		zassert_equal(thrd_success, thrd_join(thr, NULL));
169 	}
170 }
171 
172 ZTEST_SUITE(libc_thrd, NULL, NULL, NULL, NULL, NULL);
173