1 /*
2  * SPDX-FileCopyrightText: 2018-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <time.h>
8 #include <errno.h>
9 #include <pthread.h>
10 #include <string.h>
11 #include "esp_err.h"
12 #include "esp_attr.h"
13 #include "sys/queue.h"
14 #include "freertos/FreeRTOS.h"
15 #include "freertos/task.h"
16 #include "freertos/semphr.h"
17 #include "soc/soc_memory_layout.h"
18 
19 #include "pthread_internal.h"
20 #include "esp_pthread.h"
21 
22 #include "esp_log.h"
23 const static char *TAG = "pthread";
24 
25 /** task state */
26 enum esp_pthread_task_state {
27     PTHREAD_TASK_STATE_RUN,
28     PTHREAD_TASK_STATE_EXIT
29 };
30 
31 /** pthread thread FreeRTOS wrapper */
32 typedef struct esp_pthread_entry {
33     SLIST_ENTRY(esp_pthread_entry)  list_node;  ///< Tasks list node struct.
34     TaskHandle_t                handle;         ///< FreeRTOS task handle
35     TaskHandle_t                join_task;      ///< Handle of the task waiting to join
36     enum esp_pthread_task_state state;          ///< pthread task state
37     bool                        detached;       ///< True if pthread is detached
38     void                       *retval;         ///< Value supplied to calling thread during join
39     void                       *task_arg;       ///< Task arguments
40 } esp_pthread_t;
41 
42 /** pthread wrapper task arg */
43 typedef struct {
44     void *(*func)(void *);  ///< user task entry
45     void *arg;              ///< user task argument
46     esp_pthread_cfg_t cfg;  ///< pthread configuration
47 } esp_pthread_task_arg_t;
48 
49 /** pthread mutex FreeRTOS wrapper */
50 typedef struct {
51     SemaphoreHandle_t   sem;        ///< Handle of the task waiting to join
52     int                 type;       ///< Mutex type. Currently supported PTHREAD_MUTEX_NORMAL and PTHREAD_MUTEX_RECURSIVE
53 } esp_pthread_mutex_t;
54 
55 static SemaphoreHandle_t s_threads_mux  = NULL;
56 portMUX_TYPE pthread_lazy_init_lock  = portMUX_INITIALIZER_UNLOCKED; // Used for mutexes and cond vars and rwlocks
57 static SLIST_HEAD(esp_thread_list_head, esp_pthread_entry) s_threads_list
58                                         = SLIST_HEAD_INITIALIZER(s_threads_list);
59 static pthread_key_t s_pthread_cfg_key;
60 
61 
62 static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickType_t tmo);
63 
esp_pthread_cfg_key_destructor(void * value)64 static void esp_pthread_cfg_key_destructor(void *value)
65 {
66     free(value);
67 }
68 
esp_pthread_init(void)69 esp_err_t esp_pthread_init(void)
70 {
71     if (pthread_key_create(&s_pthread_cfg_key, esp_pthread_cfg_key_destructor) != 0) {
72         return ESP_ERR_NO_MEM;
73     }
74     s_threads_mux = xSemaphoreCreateMutex();
75     if (s_threads_mux == NULL) {
76         pthread_key_delete(s_pthread_cfg_key);
77         return ESP_ERR_NO_MEM;
78     }
79     return ESP_OK;
80 }
81 
pthread_list_find_item(void * (* item_check)(esp_pthread_t *,void * arg),void * check_arg)82 static void *pthread_list_find_item(void *(*item_check)(esp_pthread_t *, void *arg), void *check_arg)
83 {
84     esp_pthread_t *it;
85     SLIST_FOREACH(it, &s_threads_list, list_node) {
86         void *val = item_check(it, check_arg);
87         if (val) {
88             return val;
89         }
90     }
91     return NULL;
92 }
93 
pthread_get_handle_by_desc(esp_pthread_t * item,void * desc)94 static void *pthread_get_handle_by_desc(esp_pthread_t *item, void *desc)
95 {
96     if (item == desc) {
97         return item->handle;
98     }
99     return NULL;
100 }
101 
pthread_get_desc_by_handle(esp_pthread_t * item,void * hnd)102 static void *pthread_get_desc_by_handle(esp_pthread_t *item, void *hnd)
103 {
104     if (hnd == item->handle) {
105         return item;
106     }
107     return NULL;
108 }
109 
pthread_find_handle(pthread_t thread)110 static inline TaskHandle_t pthread_find_handle(pthread_t thread)
111 {
112     return pthread_list_find_item(pthread_get_handle_by_desc, (void *)thread);
113 }
114 
pthread_find(TaskHandle_t task_handle)115 static esp_pthread_t *pthread_find(TaskHandle_t task_handle)
116 {
117     return pthread_list_find_item(pthread_get_desc_by_handle, task_handle);
118 }
119 
pthread_delete(esp_pthread_t * pthread)120 static void pthread_delete(esp_pthread_t *pthread)
121 {
122     SLIST_REMOVE(&s_threads_list, pthread, esp_pthread_entry, list_node);
123     free(pthread);
124 }
125 
126 /* Call this function to configure pthread stacks in Pthreads */
esp_pthread_set_cfg(const esp_pthread_cfg_t * cfg)127 esp_err_t esp_pthread_set_cfg(const esp_pthread_cfg_t *cfg)
128 {
129     if (cfg->stack_size < PTHREAD_STACK_MIN) {
130         return ESP_ERR_INVALID_ARG;
131     }
132 
133     /* If a value is already set, update that value */
134     esp_pthread_cfg_t *p = pthread_getspecific(s_pthread_cfg_key);
135     if (!p) {
136         p = malloc(sizeof(esp_pthread_cfg_t));
137         if (!p) {
138             return ESP_ERR_NO_MEM;
139         }
140     }
141     *p = *cfg;
142     pthread_setspecific(s_pthread_cfg_key, p);
143     return 0;
144 }
145 
esp_pthread_get_cfg(esp_pthread_cfg_t * p)146 esp_err_t esp_pthread_get_cfg(esp_pthread_cfg_t *p)
147 {
148     esp_pthread_cfg_t *cfg = pthread_getspecific(s_pthread_cfg_key);
149     if (cfg) {
150         *p = *cfg;
151         return ESP_OK;
152     }
153     memset(p, 0, sizeof(*p));
154     return ESP_ERR_NOT_FOUND;
155 }
156 
get_default_pthread_core(void)157 static int get_default_pthread_core(void)
158 {
159     return CONFIG_PTHREAD_TASK_CORE_DEFAULT == -1 ? tskNO_AFFINITY : CONFIG_PTHREAD_TASK_CORE_DEFAULT;
160 }
161 
esp_pthread_get_default_config(void)162 esp_pthread_cfg_t esp_pthread_get_default_config(void)
163 {
164     esp_pthread_cfg_t cfg = {
165         .stack_size = CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT,
166         .prio = CONFIG_PTHREAD_TASK_PRIO_DEFAULT,
167         .inherit_cfg = false,
168         .thread_name = NULL,
169         .pin_to_core = get_default_pthread_core()
170     };
171 
172     return cfg;
173 }
174 
pthread_task_func(void * arg)175 static void pthread_task_func(void *arg)
176 {
177     void *rval = NULL;
178     esp_pthread_task_arg_t *task_arg = (esp_pthread_task_arg_t *)arg;
179 
180     ESP_LOGV(TAG, "%s ENTER %p", __FUNCTION__, task_arg->func);
181 
182     // wait for start
183     xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
184 
185     if (task_arg->cfg.inherit_cfg) {
186         /* If inherit option is set, then do a set_cfg() ourselves for future forks,
187         but first set thread_name to NULL to enable inheritance of the name too.
188         (This also to prevents dangling pointers to name of tasks that might
189         possibly have been deleted when we use the configuration).*/
190         esp_pthread_cfg_t *cfg = &task_arg->cfg;
191         cfg->thread_name = NULL;
192         esp_pthread_set_cfg(cfg);
193     }
194     ESP_LOGV(TAG, "%s START %p", __FUNCTION__, task_arg->func);
195     rval = task_arg->func(task_arg->arg);
196     ESP_LOGV(TAG, "%s END %p", __FUNCTION__, task_arg->func);
197 
198     pthread_exit(rval);
199 
200     ESP_LOGV(TAG, "%s EXIT", __FUNCTION__);
201 }
202 
pthread_create(pthread_t * thread,const pthread_attr_t * attr,void * (* start_routine)(void *),void * arg)203 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
204                    void *(*start_routine) (void *), void *arg)
205 {
206     TaskHandle_t xHandle = NULL;
207 
208     ESP_LOGV(TAG, "%s", __FUNCTION__);
209     esp_pthread_task_arg_t *task_arg = calloc(1, sizeof(esp_pthread_task_arg_t));
210     if (task_arg == NULL) {
211         ESP_LOGE(TAG, "Failed to allocate task args!");
212         return ENOMEM;
213     }
214 
215     esp_pthread_t *pthread = calloc(1, sizeof(esp_pthread_t));
216     if (pthread == NULL) {
217         ESP_LOGE(TAG, "Failed to allocate pthread data!");
218         free(task_arg);
219         return ENOMEM;
220     }
221 
222     uint32_t stack_size = CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT;
223     BaseType_t prio = CONFIG_PTHREAD_TASK_PRIO_DEFAULT;
224     BaseType_t core_id = get_default_pthread_core();
225     const char *task_name = CONFIG_PTHREAD_TASK_NAME_DEFAULT;
226 
227     esp_pthread_cfg_t *pthread_cfg = pthread_getspecific(s_pthread_cfg_key);
228     if (pthread_cfg) {
229         if (pthread_cfg->stack_size) {
230             stack_size = pthread_cfg->stack_size;
231         }
232         if (pthread_cfg->prio && pthread_cfg->prio < configMAX_PRIORITIES) {
233             prio = pthread_cfg->prio;
234         }
235 
236         if (pthread_cfg->inherit_cfg) {
237             if (pthread_cfg->thread_name == NULL) {
238                 // Inherit task name from current task.
239                 task_name = pcTaskGetTaskName(NULL);
240             } else {
241                 // Inheriting, but new task name.
242                 task_name = pthread_cfg->thread_name;
243             }
244         } else if (pthread_cfg->thread_name == NULL) {
245             task_name = CONFIG_PTHREAD_TASK_NAME_DEFAULT;
246         } else {
247             task_name = pthread_cfg->thread_name;
248         }
249 
250         if (pthread_cfg->pin_to_core >= 0 && pthread_cfg->pin_to_core < portNUM_PROCESSORS) {
251             core_id = pthread_cfg->pin_to_core;
252         }
253 
254         task_arg->cfg = *pthread_cfg;
255     }
256 
257     if (attr) {
258         /* Overwrite attributes */
259         stack_size = attr->stacksize;
260 
261         switch (attr->detachstate) {
262         case PTHREAD_CREATE_DETACHED:
263             pthread->detached = true;
264             break;
265         case PTHREAD_CREATE_JOINABLE:
266         default:
267             pthread->detached = false;
268         }
269     }
270 
271     task_arg->func = start_routine;
272     task_arg->arg = arg;
273     pthread->task_arg = task_arg;
274     BaseType_t res = xTaskCreatePinnedToCore(&pthread_task_func,
275                                              task_name,
276                                              // stack_size is in bytes. This transformation ensures that the units are
277                                              // transformed to the units used in FreeRTOS.
278                                              // Note: float division of ceil(m / n) ==
279                                              //       integer division of (m + n - 1) / n
280                                              (stack_size + sizeof(StackType_t) - 1) / sizeof(StackType_t),
281                                              task_arg,
282                                              prio,
283                                              &xHandle,
284                                              core_id);
285 
286     if (res != pdPASS) {
287         ESP_LOGE(TAG, "Failed to create task!");
288         free(pthread);
289         free(task_arg);
290         if (res == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) {
291             return ENOMEM;
292         } else {
293             return EAGAIN;
294         }
295     }
296     pthread->handle = xHandle;
297 
298     if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) {
299         assert(false && "Failed to lock threads list!");
300     }
301     SLIST_INSERT_HEAD(&s_threads_list, pthread, list_node);
302     xSemaphoreGive(s_threads_mux);
303 
304     // start task
305     xTaskNotify(xHandle, 0, eNoAction);
306 
307     *thread = (pthread_t)pthread; // pointer value fit into pthread_t (uint32_t)
308 
309     ESP_LOGV(TAG, "Created task %x", (uint32_t)xHandle);
310 
311     return 0;
312 }
313 
pthread_join(pthread_t thread,void ** retval)314 int pthread_join(pthread_t thread, void **retval)
315 {
316     esp_pthread_t *pthread = (esp_pthread_t *)thread;
317     int ret = 0;
318     bool wait = false;
319     void *child_task_retval = 0;
320 
321     ESP_LOGV(TAG, "%s %p", __FUNCTION__, pthread);
322 
323     // find task
324     if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) {
325         assert(false && "Failed to lock threads list!");
326     }
327     TaskHandle_t handle = pthread_find_handle(thread);
328     if (!handle) {
329         // not found
330         ret = ESRCH;
331     } else if (pthread->detached) {
332         // Thread is detached
333         ret = EDEADLK;
334     } else if (pthread->join_task) {
335         // already have waiting task to join
336         ret = EINVAL;
337     } else if (handle == xTaskGetCurrentTaskHandle()) {
338         // join to self not allowed
339         ret = EDEADLK;
340     } else {
341         esp_pthread_t *cur_pthread = pthread_find(xTaskGetCurrentTaskHandle());
342         if (cur_pthread && cur_pthread->join_task == handle) {
343             // join to each other not allowed
344             ret = EDEADLK;
345         } else {
346             if (pthread->state == PTHREAD_TASK_STATE_RUN) {
347                 pthread->join_task = xTaskGetCurrentTaskHandle();
348                 wait = true;
349             } else { // thread has exited and task is already suspended, or about to be suspended
350                 child_task_retval = pthread->retval;
351                 pthread_delete(pthread);
352             }
353         }
354     }
355     xSemaphoreGive(s_threads_mux);
356 
357     if (ret == 0) {
358         if (wait) {
359             xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
360             if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) {
361                 assert(false && "Failed to lock threads list!");
362             }
363             child_task_retval = pthread->retval;
364             pthread_delete(pthread);
365             xSemaphoreGive(s_threads_mux);
366         }
367         vTaskDelete(handle);
368     }
369 
370     if (retval) {
371         *retval = child_task_retval;
372     }
373 
374     ESP_LOGV(TAG, "%s %p EXIT %d", __FUNCTION__, pthread, ret);
375     return ret;
376 }
377 
pthread_detach(pthread_t thread)378 int pthread_detach(pthread_t thread)
379 {
380     esp_pthread_t *pthread = (esp_pthread_t *)thread;
381     int ret = 0;
382 
383     if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) {
384         assert(false && "Failed to lock threads list!");
385     }
386     TaskHandle_t handle = pthread_find_handle(thread);
387     if (!handle) {
388         ret = ESRCH;
389     } else if (pthread->detached) {
390         // already detached
391         ret = EINVAL;
392     } else if (pthread->join_task) {
393         // already have waiting task to join
394         ret = EINVAL;
395     } else if (pthread->state == PTHREAD_TASK_STATE_RUN) {
396         // pthread still running
397         pthread->detached = true;
398     } else {
399         // pthread already stopped
400         pthread_delete(pthread);
401         vTaskDelete(handle);
402     }
403     xSemaphoreGive(s_threads_mux);
404     ESP_LOGV(TAG, "%s %p EXIT %d", __FUNCTION__, pthread, ret);
405     return ret;
406 }
407 
pthread_exit(void * value_ptr)408 void pthread_exit(void *value_ptr)
409 {
410     bool detached = false;
411     /* preemptively clean up thread local storage, rather than
412        waiting for the idle task to clean up the thread */
413     pthread_internal_local_storage_destructor_callback();
414 
415     if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) {
416         assert(false && "Failed to lock threads list!");
417     }
418     esp_pthread_t *pthread = pthread_find(xTaskGetCurrentTaskHandle());
419     if (!pthread) {
420         assert(false && "Failed to find pthread for current task!");
421     }
422     if (pthread->task_arg) {
423         free(pthread->task_arg);
424     }
425     if (pthread->detached) {
426         // auto-free for detached threads
427         pthread_delete(pthread);
428         detached = true;
429     } else {
430         // Set return value
431         pthread->retval = value_ptr;
432         // Remove from list, it indicates that task has exited
433         if (pthread->join_task) {
434             // notify join
435             xTaskNotify(pthread->join_task, 0, eNoAction);
436         } else {
437             pthread->state = PTHREAD_TASK_STATE_EXIT;
438         }
439     }
440 
441     ESP_LOGD(TAG, "Task stk_wm = %d", uxTaskGetStackHighWaterMark(NULL));
442 
443     xSemaphoreGive(s_threads_mux);
444     // note: if this thread is joinable then after giving back s_threads_mux
445     // this task could be deleted at any time, so don't take another lock or
446     // do anything that might lock (such as printing to stdout)
447 
448     if (detached) {
449         vTaskDelete(NULL);
450     } else {
451         vTaskSuspend(NULL);
452     }
453 
454     // Should never be reached
455     abort();
456 }
457 
pthread_cancel(pthread_t thread)458 int pthread_cancel(pthread_t thread)
459 {
460     ESP_LOGE(TAG, "%s: not supported!", __FUNCTION__);
461     return ENOSYS;
462 }
463 
sched_yield(void)464 int sched_yield( void )
465 {
466     vTaskDelay(0);
467     return 0;
468 }
469 
pthread_self(void)470 pthread_t pthread_self(void)
471 {
472     if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) {
473         assert(false && "Failed to lock threads list!");
474     }
475     esp_pthread_t *pthread = pthread_find(xTaskGetCurrentTaskHandle());
476     if (!pthread) {
477         assert(false && "Failed to find current thread ID!");
478     }
479     xSemaphoreGive(s_threads_mux);
480     return (pthread_t)pthread;
481 }
482 
pthread_equal(pthread_t t1,pthread_t t2)483 int pthread_equal(pthread_t t1, pthread_t t2)
484 {
485     return t1 == t2 ? 1 : 0;
486 }
487 
488 /***************** ONCE ******************/
pthread_once(pthread_once_t * once_control,void (* init_routine)(void))489 int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
490 {
491     if (once_control == NULL || init_routine == NULL || !once_control->is_initialized) {
492         ESP_LOGE(TAG, "%s: Invalid args!", __FUNCTION__);
493         return EINVAL;
494     }
495 
496     uint32_t res = 1;
497 #if defined(CONFIG_SPIRAM)
498     if (esp_ptr_external_ram(once_control)) {
499         uxPortCompareSetExtram((uint32_t *) &once_control->init_executed, 0, &res);
500     } else {
501 #endif
502         uxPortCompareSet((uint32_t *) &once_control->init_executed, 0, &res);
503 #if defined(CONFIG_SPIRAM)
504     }
505 #endif
506     // Check if compare and set was successful
507     if (res == 0) {
508         ESP_LOGV(TAG, "%s: call init_routine %p", __FUNCTION__, once_control);
509         init_routine();
510     }
511 
512     return 0;
513 }
514 
515 /***************** MUTEX ******************/
mutexattr_check(const pthread_mutexattr_t * attr)516 static int mutexattr_check(const pthread_mutexattr_t *attr)
517 {
518     if (attr->type != PTHREAD_MUTEX_NORMAL &&
519         attr->type != PTHREAD_MUTEX_RECURSIVE &&
520         attr->type != PTHREAD_MUTEX_ERRORCHECK) {
521         return EINVAL;
522     }
523     return 0;
524 }
525 
pthread_mutex_init(pthread_mutex_t * mutex,const pthread_mutexattr_t * attr)526 int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
527 {
528     int type = PTHREAD_MUTEX_NORMAL;
529 
530     if (!mutex) {
531         return EINVAL;
532     }
533 
534     if (attr) {
535         if (!attr->is_initialized) {
536             return EINVAL;
537         }
538         int res = mutexattr_check(attr);
539         if (res) {
540             return res;
541         }
542         type = attr->type;
543     }
544 
545     esp_pthread_mutex_t *mux = (esp_pthread_mutex_t *)malloc(sizeof(esp_pthread_mutex_t));
546     if (!mux) {
547         return ENOMEM;
548     }
549     mux->type = type;
550 
551     if (mux->type == PTHREAD_MUTEX_RECURSIVE) {
552         mux->sem = xSemaphoreCreateRecursiveMutex();
553     } else {
554         mux->sem = xSemaphoreCreateMutex();
555     }
556     if (!mux->sem) {
557         free(mux);
558         return EAGAIN;
559     }
560 
561     *mutex = (pthread_mutex_t)mux; // pointer value fit into pthread_mutex_t (uint32_t)
562 
563     return 0;
564 }
565 
pthread_mutex_destroy(pthread_mutex_t * mutex)566 int pthread_mutex_destroy(pthread_mutex_t *mutex)
567 {
568     esp_pthread_mutex_t *mux;
569 
570     ESP_LOGV(TAG, "%s %p", __FUNCTION__, mutex);
571 
572     if (!mutex) {
573         return EINVAL;
574     }
575     if ((intptr_t) *mutex == PTHREAD_MUTEX_INITIALIZER) {
576         return 0; // Static mutex was never initialized
577     }
578 
579     mux = (esp_pthread_mutex_t *)*mutex;
580     if (!mux) {
581         return EINVAL;
582     }
583 
584     // check if mux is busy
585     int res = pthread_mutex_lock_internal(mux, 0);
586     if (res == EBUSY) {
587         return EBUSY;
588     }
589 
590     if (mux->type == PTHREAD_MUTEX_RECURSIVE) {
591         res = xSemaphoreGiveRecursive(mux->sem);
592     } else {
593         res = xSemaphoreGive(mux->sem);
594     }
595     if (res != pdTRUE) {
596         assert(false && "Failed to release mutex!");
597     }
598     vSemaphoreDelete(mux->sem);
599     free(mux);
600 
601     return 0;
602 }
603 
pthread_mutex_lock_internal(esp_pthread_mutex_t * mux,TickType_t tmo)604 static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickType_t tmo)
605 {
606     if (!mux) {
607         return EINVAL;
608     }
609 
610     if ((mux->type == PTHREAD_MUTEX_ERRORCHECK) &&
611         (xSemaphoreGetMutexHolder(mux->sem) == xTaskGetCurrentTaskHandle())) {
612         return EDEADLK;
613     }
614 
615     if (mux->type == PTHREAD_MUTEX_RECURSIVE) {
616         if (xSemaphoreTakeRecursive(mux->sem, tmo) != pdTRUE) {
617             return EBUSY;
618         }
619     } else {
620         if (xSemaphoreTake(mux->sem, tmo) != pdTRUE) {
621             return EBUSY;
622         }
623     }
624 
625     return 0;
626 }
627 
pthread_mutex_init_if_static(pthread_mutex_t * mutex)628 static int pthread_mutex_init_if_static(pthread_mutex_t *mutex)
629 {
630     int res = 0;
631     if ((intptr_t) *mutex == PTHREAD_MUTEX_INITIALIZER) {
632         portENTER_CRITICAL(&pthread_lazy_init_lock);
633         if ((intptr_t) *mutex == PTHREAD_MUTEX_INITIALIZER) {
634             res = pthread_mutex_init(mutex, NULL);
635         }
636         portEXIT_CRITICAL(&pthread_lazy_init_lock);
637     }
638     return res;
639 }
640 
pthread_mutex_lock(pthread_mutex_t * mutex)641 int IRAM_ATTR pthread_mutex_lock(pthread_mutex_t *mutex)
642 {
643     if (!mutex) {
644         return EINVAL;
645     }
646     int res = pthread_mutex_init_if_static(mutex);
647     if (res != 0) {
648         return res;
649     }
650     return pthread_mutex_lock_internal((esp_pthread_mutex_t *)*mutex, portMAX_DELAY);
651 }
652 
pthread_mutex_timedlock(pthread_mutex_t * mutex,const struct timespec * timeout)653 int IRAM_ATTR pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *timeout)
654 {
655     if (!mutex) {
656         return EINVAL;
657     }
658     int res = pthread_mutex_init_if_static(mutex);
659     if (res != 0) {
660         return res;
661     }
662 
663     struct timespec currtime;
664     clock_gettime(CLOCK_REALTIME, &currtime);
665     TickType_t tmo = ((timeout->tv_sec - currtime.tv_sec)*1000 +
666                      (timeout->tv_nsec - currtime.tv_nsec)/1000000)/portTICK_PERIOD_MS;
667 
668     res = pthread_mutex_lock_internal((esp_pthread_mutex_t *)*mutex, tmo);
669     if (res == EBUSY) {
670         return ETIMEDOUT;
671     }
672     return res;
673 }
674 
pthread_mutex_trylock(pthread_mutex_t * mutex)675 int IRAM_ATTR pthread_mutex_trylock(pthread_mutex_t *mutex)
676 {
677     if (!mutex) {
678         return EINVAL;
679     }
680     int res = pthread_mutex_init_if_static(mutex);
681     if (res != 0) {
682         return res;
683     }
684     return pthread_mutex_lock_internal((esp_pthread_mutex_t *)*mutex, 0);
685 }
686 
pthread_mutex_unlock(pthread_mutex_t * mutex)687 int IRAM_ATTR pthread_mutex_unlock(pthread_mutex_t *mutex)
688 {
689     esp_pthread_mutex_t *mux;
690 
691     if (!mutex) {
692         return EINVAL;
693     }
694     mux = (esp_pthread_mutex_t *)*mutex;
695     if (!mux) {
696         return EINVAL;
697     }
698 
699     if (((mux->type == PTHREAD_MUTEX_RECURSIVE) ||
700         (mux->type == PTHREAD_MUTEX_ERRORCHECK)) &&
701         (xSemaphoreGetMutexHolder(mux->sem) != xTaskGetCurrentTaskHandle())) {
702         return EPERM;
703     }
704 
705     int ret;
706     if (mux->type == PTHREAD_MUTEX_RECURSIVE) {
707         ret = xSemaphoreGiveRecursive(mux->sem);
708     } else {
709         ret = xSemaphoreGive(mux->sem);
710     }
711     if (ret != pdTRUE) {
712         assert(false && "Failed to unlock mutex!");
713     }
714     return 0;
715 }
716 
pthread_mutexattr_init(pthread_mutexattr_t * attr)717 int pthread_mutexattr_init(pthread_mutexattr_t *attr)
718 {
719     if (!attr) {
720         return EINVAL;
721     }
722     memset(attr, 0, sizeof(*attr));
723     attr->type = PTHREAD_MUTEX_NORMAL;
724     attr->is_initialized = 1;
725     return 0;
726 }
727 
pthread_mutexattr_destroy(pthread_mutexattr_t * attr)728 int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
729 {
730     if (!attr) {
731         return EINVAL;
732     }
733     attr->is_initialized = 0;
734     return 0;
735 }
736 
pthread_mutexattr_gettype(const pthread_mutexattr_t * attr,int * type)737 int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type)
738 {
739     if (!attr) {
740         return EINVAL;
741     }
742     *type = attr->type;
743     return 0;
744 }
745 
pthread_mutexattr_settype(pthread_mutexattr_t * attr,int type)746 int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
747 {
748     if (!attr) {
749         return EINVAL;
750     }
751     pthread_mutexattr_t tmp_attr = {.type = type};
752     int res = mutexattr_check(&tmp_attr);
753     if (!res) {
754         attr->type = type;
755     }
756     return res;
757 }
758 
759 /***************** ATTRIBUTES ******************/
pthread_attr_init(pthread_attr_t * attr)760 int pthread_attr_init(pthread_attr_t *attr)
761 {
762     if (attr) {
763         /* Nothing to allocate. Set everything to default */
764         memset(attr, 0, sizeof(*attr));
765         attr->stacksize   = CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT;
766         attr->detachstate = PTHREAD_CREATE_JOINABLE;
767         return 0;
768     }
769     return EINVAL;
770 }
771 
pthread_attr_destroy(pthread_attr_t * attr)772 int pthread_attr_destroy(pthread_attr_t *attr)
773 {
774     /* Nothing to deallocate. Reset everything to default */
775     return pthread_attr_init(attr);
776 }
777 
pthread_attr_getstacksize(const pthread_attr_t * attr,size_t * stacksize)778 int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize)
779 {
780     if (attr) {
781         *stacksize = attr->stacksize;
782         return 0;
783     }
784     return EINVAL;
785 }
786 
pthread_attr_setstacksize(pthread_attr_t * attr,size_t stacksize)787 int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
788 {
789     if (attr && !(stacksize < PTHREAD_STACK_MIN)) {
790         attr->stacksize = stacksize;
791         return 0;
792     }
793     return EINVAL;
794 }
795 
pthread_attr_getdetachstate(const pthread_attr_t * attr,int * detachstate)796 int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate)
797 {
798     if (attr) {
799         *detachstate = attr->detachstate;
800         return 0;
801     }
802     return EINVAL;
803 }
804 
pthread_attr_setdetachstate(pthread_attr_t * attr,int detachstate)805 int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
806 {
807     if (attr) {
808         switch (detachstate) {
809         case PTHREAD_CREATE_DETACHED:
810             attr->detachstate = PTHREAD_CREATE_DETACHED;
811             break;
812         case PTHREAD_CREATE_JOINABLE:
813             attr->detachstate = PTHREAD_CREATE_JOINABLE;
814             break;
815         default:
816             return EINVAL;
817         }
818         return 0;
819     }
820     return EINVAL;
821 }
822 
823 /* Hook function to force linking this file */
pthread_include_pthread_impl(void)824 void pthread_include_pthread_impl(void)
825 {
826 }
827