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