1 #include <errno.h>
2
3 #include "freertos/FreeRTOS.h"
4 #include "freertos/task.h"
5
6 #include "esp_pthread.h"
7 #include <pthread.h>
8
9 #include "unity.h"
10
compute_square(void * arg)11 static void *compute_square(void *arg)
12 {
13 int *num = (int *) arg;
14 *num = (*num) * (*num);
15 pthread_exit((void *) num);
16 return NULL;
17 }
18
19 TEST_CASE("pthread create join", "[pthread]")
20 {
21 int res = 0;
22 volatile int num = 7;
23 volatile bool attr_init = false;
24 void *thread_rval = NULL;
25 pthread_t new_thread = (pthread_t)NULL;
26 pthread_attr_t attr;
27
28 if (TEST_PROTECT()) {
29 res = pthread_attr_init(&attr);
30 TEST_ASSERT_EQUAL_INT(0, res);
31 attr_init = true;
32
33 res = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
34 TEST_ASSERT_EQUAL_INT(0, res);
35
36 res = pthread_create(&new_thread, &attr, compute_square, (void *) &num);
37 TEST_ASSERT_EQUAL_INT(0, res);
38
39 res = pthread_join(new_thread, &thread_rval);
40 TEST_ASSERT_EQUAL_INT(EDEADLK, res);
41
42 vTaskDelay(100 / portTICK_PERIOD_MS);
43 TEST_ASSERT_EQUAL_INT(49, num);
44
45 res = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
46 TEST_ASSERT_EQUAL_INT(0, res);
47
48 res = pthread_create(&new_thread, &attr, compute_square, (void *) &num);
49 TEST_ASSERT_EQUAL_INT(0, res);
50
51 res = pthread_join(new_thread, &thread_rval);
52 TEST_ASSERT_EQUAL_INT(0, res);
53
54 TEST_ASSERT_EQUAL_INT(2401, num);
55 TEST_ASSERT_EQUAL_PTR(&num, thread_rval);
56 }
57
58 if (attr_init) {
59 pthread_attr_destroy(&attr);
60 }
61 }
62
waiting_thread(void * arg)63 static void *waiting_thread(void *arg)
64 {
65 TaskHandle_t *task_handle = (TaskHandle_t *)arg;
66 TaskHandle_t parent_task = *task_handle;
67
68 *task_handle = xTaskGetCurrentTaskHandle();
69
70 xTaskNotify(parent_task, 0, eNoAction);
71 xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
72 return NULL;
73 }
74
75 TEST_CASE("pthread detach", "[pthread]")
76 {
77 int res = 0;
78 pthread_t new_thread = (pthread_t)NULL;
79 TaskHandle_t task_handle = NULL;
80 const int task_count = uxTaskGetNumberOfTasks();
81 bool detach_works = false;
82
83 if (TEST_PROTECT()) {
84 task_handle = xTaskGetCurrentTaskHandle();
85 res = pthread_create(&new_thread, NULL, waiting_thread, (void *)&task_handle);
86 TEST_ASSERT_EQUAL_INT(0, res);
87
88 res = xTaskNotifyWait(0, 0, NULL, 100 / portTICK_PERIOD_MS);
89 TEST_ASSERT_EQUAL_INT(pdTRUE, res);
90
91 xTaskNotify(task_handle, 0, eNoAction);
92 vTaskDelay(100 / portTICK_PERIOD_MS);
93
94 res = pthread_detach(new_thread);
95 TEST_ASSERT_EQUAL_INT(0, res);
96
97 res = uxTaskGetNumberOfTasks();
98 TEST_ASSERT_EQUAL_INT(task_count, res);
99 detach_works = true;
100 }
101
102 if (!detach_works) {
103 vTaskDelete(task_handle);
104 } else {
105 detach_works = false;
106 }
107
108 if (TEST_PROTECT()) {
109 task_handle = xTaskGetCurrentTaskHandle();
110 res = pthread_create(&new_thread, NULL, waiting_thread, (void *)&task_handle);
111 TEST_ASSERT_EQUAL_INT(0, res);
112
113 res = xTaskNotifyWait(0, 0, NULL, 100 / portTICK_PERIOD_MS);
114 TEST_ASSERT_EQUAL_INT(pdTRUE, res);
115
116 res = pthread_detach(new_thread);
117 TEST_ASSERT_EQUAL_INT(0, res);
118
119 xTaskNotify(task_handle, 0, eNoAction);
120 vTaskDelay(100 / portTICK_PERIOD_MS);
121
122 res = uxTaskGetNumberOfTasks();
123 TEST_ASSERT_EQUAL_INT(task_count, res);
124 detach_works = true;
125 }
126
127 if (!detach_works) {
128 vTaskDelete(task_handle);
129 }
130 }
131
132 TEST_CASE("pthread attr init destroy", "[pthread]")
133 {
134 int res = 0;
135 size_t stack_size_1 = 0, stack_size_2 = 0;
136 volatile bool attr_init = pdFALSE;
137 pthread_attr_t attr;
138
139 if (TEST_PROTECT()) {
140 res = pthread_attr_init(&attr);
141 TEST_ASSERT_EQUAL_INT(0, res);
142 attr_init = true;
143
144 res = pthread_attr_getstacksize(&attr, &stack_size_1);
145 TEST_ASSERT_EQUAL_INT(0, res);
146 res = pthread_attr_setstacksize(&attr, stack_size_1);
147 TEST_ASSERT_EQUAL_INT(0, res);
148 res = pthread_attr_getstacksize(&attr, &stack_size_2);
149 TEST_ASSERT_EQUAL_INT(0, res);
150 TEST_ASSERT_EQUAL_INT(stack_size_2, stack_size_1);
151
152 stack_size_1 = PTHREAD_STACK_MIN - 1;
153 res = pthread_attr_setstacksize(&attr, stack_size_1);
154 TEST_ASSERT_EQUAL_INT(EINVAL, res);
155 }
156
157 if (attr_init) {
158 TEST_ASSERT_EQUAL_INT(0, pthread_attr_destroy(&attr));
159 }
160 }
161
unlock_mutex(void * arg)162 static void *unlock_mutex(void *arg)
163 {
164 pthread_mutex_t *mutex = (pthread_mutex_t *) arg;
165 intptr_t res = (intptr_t) pthread_mutex_unlock(mutex);
166 pthread_exit((void *) res);
167 return NULL;
168 }
169
test_mutex_lock_unlock(int mutex_type)170 static void test_mutex_lock_unlock(int mutex_type)
171 {
172 int res = 0;
173 int set_type = -1;
174 volatile bool attr_created = false;
175 volatile bool mutex_created = false;
176 volatile intptr_t thread_rval = 0;
177 pthread_mutex_t mutex;
178 pthread_mutexattr_t attr;
179 pthread_t new_thread;
180
181 if (TEST_PROTECT()) {
182 res = pthread_mutexattr_init(&attr);
183 TEST_ASSERT_EQUAL_INT(0, res);
184 attr_created = true;
185
186 res = pthread_mutexattr_settype(&attr, mutex_type);
187 TEST_ASSERT_EQUAL_INT(0, res);
188
189 res = pthread_mutexattr_gettype(&attr, &set_type);
190 TEST_ASSERT_EQUAL_INT(0, res);
191 TEST_ASSERT_EQUAL_INT(mutex_type, set_type);
192
193 res = pthread_mutex_init(&mutex, &attr);
194 TEST_ASSERT_EQUAL_INT(0, res);
195 mutex_created = true;
196
197 res = pthread_mutex_lock(&mutex);
198 TEST_ASSERT_EQUAL_INT(0, res);
199
200 res = pthread_mutex_lock(&mutex);
201
202 if(mutex_type == PTHREAD_MUTEX_ERRORCHECK) {
203 TEST_ASSERT_EQUAL_INT(EDEADLK, res);
204 } else {
205 TEST_ASSERT_EQUAL_INT(0, res);
206
207 res = pthread_mutex_unlock(&mutex);
208 TEST_ASSERT_EQUAL_INT(0, res);
209 }
210
211 pthread_create(&new_thread, NULL, unlock_mutex, &mutex);
212
213 pthread_join(new_thread, (void **) &thread_rval);
214 TEST_ASSERT_EQUAL_INT(EPERM, (int) thread_rval);
215
216 res = pthread_mutex_unlock(&mutex);
217 TEST_ASSERT_EQUAL_INT(0, res);
218 }
219
220 if (attr_created) {
221 pthread_mutexattr_destroy(&attr);
222 }
223
224 if (mutex_created) {
225 pthread_mutex_destroy(&mutex);
226 }
227 }
228
229 TEST_CASE("pthread mutex lock unlock", "[pthread]")
230 {
231 int res = 0;
232
233 /* Present behavior of mutex initializer is unlike what is
234 * defined in Posix standard, ie. calling pthread_mutex_lock
235 * on such a mutex would internally cause dynamic allocation.
236 * Therefore pthread_mutex_destroy needs to be called in
237 * order to avoid memory leak. */
238 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
239
240 res = pthread_mutex_lock(&mutex);
241 TEST_ASSERT_EQUAL_INT(0, res);
242
243 res = pthread_mutex_unlock(&mutex);
244 TEST_ASSERT_EQUAL_INT(0, res);
245
246 /* This deviates from the Posix standard static mutex behavior.
247 * This needs to be removed in the future when standard mutex
248 * initializer is supported */
249 pthread_mutex_destroy(&mutex);
250
251 test_mutex_lock_unlock(PTHREAD_MUTEX_ERRORCHECK);
252 test_mutex_lock_unlock(PTHREAD_MUTEX_RECURSIVE);
253 }
254
timespec_add_nano(struct timespec * out,struct timespec * in,long val)255 static void timespec_add_nano(struct timespec * out, struct timespec * in, long val)
256 {
257 out->tv_nsec = val + in->tv_nsec;
258 if (out->tv_nsec < (in->tv_nsec)) {
259 out->tv_sec += 1;
260 }
261 }
262
263 TEST_CASE("pthread mutex trylock timedlock", "[pthread]")
264 {
265 int res = 0;
266 volatile bool mutex_created = false;
267 pthread_mutex_t mutex;
268 struct timespec abs_timeout;
269
270 if (TEST_PROTECT()) {
271 res = pthread_mutex_init(&mutex, NULL);
272 TEST_ASSERT_EQUAL_INT(0, res);
273 mutex_created = true;
274
275 res = pthread_mutex_trylock(&mutex);
276 TEST_ASSERT_EQUAL_INT(0, res);
277
278 res = pthread_mutex_trylock(&mutex);
279 TEST_ASSERT_EQUAL_INT(EBUSY, res);
280
281 clock_gettime(CLOCK_REALTIME, &abs_timeout);
282 timespec_add_nano(&abs_timeout, &abs_timeout, 100000000LL);
283
284 res = pthread_mutex_timedlock(&mutex, &abs_timeout);
285 TEST_ASSERT_EQUAL_INT(ETIMEDOUT, res);
286
287 res = pthread_mutex_unlock(&mutex);
288 TEST_ASSERT_EQUAL_INT(0, res);
289 }
290
291 if (mutex_created) {
292 pthread_mutex_destroy(&mutex);
293 }
294 }
295