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