From 0b09c7006d350a58da6b1dd34687d3bd861ed1c8 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Thu, 24 Aug 2017 02:53:20 +0300 Subject: [PATCH 1/8] pthread: Initial version of thread API --- components/esp32/cpu_start.c | 9 +- components/newlib/include/pthread.h | 2 +- components/newlib/include/sys/features.h | 3 + components/pthread/Kconfig | 0 components/pthread/component.mk | 9 + components/pthread/pthread.c | 408 +++++++++++++++++++ components/pthread/test/component.mk | 5 + components/pthread/test/test_pthread.c | 11 + components/pthread/test/test_pthread_cxx.cpp | 44 ++ 9 files changed, 489 insertions(+), 2 deletions(-) create mode 100644 components/pthread/Kconfig create mode 100644 components/pthread/component.mk create mode 100644 components/pthread/pthread.c create mode 100644 components/pthread/test/component.mk create mode 100644 components/pthread/test/test_pthread.c create mode 100644 components/pthread/test/test_pthread_cxx.cpp diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 8650290cf0..0c560cce63 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -83,6 +83,7 @@ static bool app_cpu_started = false; static void do_global_ctors(void); static void main_task(void* args); extern void app_main(void); +extern int esp_pthread_init(void); extern int _bss_start; extern int _bss_end; @@ -252,6 +253,7 @@ static void intr_matrix_clear(void) void start_cpu0_default(void) { + esp_err_t err; esp_setup_syscall_table(); //Enable trace memory and immediately start trace. #if CONFIG_ESP32_TRAX @@ -290,7 +292,7 @@ void start_cpu0_default(void) esp_timer_init(); esp_set_time_from_rtc(); #if CONFIG_ESP32_APPTRACE_ENABLE - esp_err_t err = esp_apptrace_init(); + err = esp_apptrace_init(); if (err != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to init apptrace module on CPU0 (%d)!", err); } @@ -298,6 +300,11 @@ void start_cpu0_default(void) #if CONFIG_SYSVIEW_ENABLE SEGGER_SYSVIEW_Conf(); #endif + err = esp_pthread_init(); + if (err != ESP_OK) { + ESP_EARLY_LOGE(TAG, "Failed to init pthread module (%d)!", err); + } + do_global_ctors(); #if CONFIG_INT_WDT esp_int_wdt_init(); diff --git a/components/newlib/include/pthread.h b/components/newlib/include/pthread.h index db1f9c1ca3..907970fdf9 100644 --- a/components/newlib/include/pthread.h +++ b/components/newlib/include/pthread.h @@ -31,7 +31,7 @@ extern "C" { #include #include -#include +#include #include struct _pthread_cleanup_context { diff --git a/components/newlib/include/sys/features.h b/components/newlib/include/sys/features.h index 1d90921af5..792f68bbc5 100644 --- a/components/newlib/include/sys/features.h +++ b/components/newlib/include/sys/features.h @@ -210,6 +210,9 @@ extern "C" { #endif /* __CYGWIN__ */ +#define _POSIX_THREADS 1 +#define _UNIX98_THREAD_MUTEX_ATTRIBUTES 1 + /* Per the permission given in POSIX.1-2008 section 2.2.1, define * _POSIX_C_SOURCE if _XOPEN_SOURCE is defined and _POSIX_C_SOURCE is not. * (_XOPEN_SOURCE indicates that XSI extensions are desired by an application.) diff --git a/components/pthread/Kconfig b/components/pthread/Kconfig new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/pthread/component.mk b/components/pthread/component.mk new file mode 100644 index 0000000000..cd69bb3305 --- /dev/null +++ b/components/pthread/component.mk @@ -0,0 +1,9 @@ +# +# Component Makefile +# + +COMPONENT_SRCDIRS := . + +#COMPONENT_ADD_INCLUDEDIRS := include + +COMPONENT_ADD_LDFLAGS := -lpthread diff --git a/components/pthread/pthread.c b/components/pthread/pthread.c new file mode 100644 index 0000000000..ca54f75e7e --- /dev/null +++ b/components/pthread/pthread.c @@ -0,0 +1,408 @@ +#include +#include +#include +#include "esp_err.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/list.h" + +#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE//CONFIG_LOG_DEFAULT_LEVEL +#include "esp_log.h" +const static char *TAG = "esp_pthread"; + +#define ESP_PTHREAD_LOG( format, ... ) \ + do { \ + ets_printf(format, ##__VA_ARGS__); \ + } while(0) + +#define ESP_PTHREAD_LOG_LEV( _tag_, _L_, level, format, ... ) \ + do { \ + if (LOG_LOCAL_LEVEL >= level) { \ + ESP_PTHREAD_LOG(LOG_FORMAT(_L_, format), esp_log_early_timestamp(), _tag_ , ##__VA_ARGS__); \ + } \ + } while(0) + +#define ESP_PTHREAD_LOGE( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, E, ESP_LOG_ERROR, format, ##__VA_ARGS__) +#define ESP_PTHREAD_LOGW( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, W, ESP_LOG_WARN, format, ##__VA_ARGS__) +#define ESP_PTHREAD_LOGI( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, I, ESP_LOG_INFO, format, ##__VA_ARGS__) +#define ESP_PTHREAD_LOGD( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, D, ESP_LOG_DEBUG, format, ##__VA_ARGS__) +#define ESP_PTHREAD_LOGV( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, V, ESP_LOG_VERBOSE, format, ##__VA_ARGS__) +#define ESP_PTHREAD_LOGO( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, E, ESP_LOG_NONE, format, ##__VA_ARGS__) +// #define ESP_PTHREAD_LOGE( tag, format, ... ) ESP_PTHREAD_LOG(tag ": " format "\n", ##__VA_ARGS__) +// #define ESP_PTHREAD_LOGW( tag, format, ... ) ESP_PTHREAD_LOG(tag ": " format "\n", ##__VA_ARGS__) +// #define ESP_PTHREAD_LOGI( tag, format, ... ) ESP_PTHREAD_LOG(tag ": " format "\n", ##__VA_ARGS__) +// #define ESP_PTHREAD_LOGD( tag, format, ... ) ESP_PTHREAD_LOG(tag ": " format "\n", ##__VA_ARGS__) +// #define ESP_PTHREAD_LOGV( tag, format, ... ) ESP_PTHREAD_LOG(tag ": " format "\n", ##__VA_ARGS__) +// #define ESP_PTHREAD_LOGO( tag, format, ... ) ESP_PTHREAD_LOG(tag ": " format "\n", ##__VA_ARGS__) + +#define PTHREAD_TASK_PRIO_DEFAULT 5 + +#define ESP_PTHREAD_STATE_RUN 0 +#define ESP_PTHREAD_STATE_EXIT 1 +//#define ESP_PTHREAD_STATE_CANCEL 2 + +typedef struct { + ListItem_t list_item; + TaskHandle_t join_task; + int state; +} esp_pthread_t; + +typedef struct { + void *(*func)(void *); + void *arg; +} esp_pthread_task_arg_t; + +static SemaphoreHandle_t s_once_mux = NULL; +static SemaphoreHandle_t s_threads_mux = NULL; + +static List_t s_threads_list; +//static List_t s_key_list; + +int esp_pthread_init(void) +{ + ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); + vListInitialise((List_t *)&s_threads_list); +// vListInitialise((List_t *)&s_key_list); + s_once_mux = xSemaphoreCreateMutex(); + if (s_once_mux == NULL) + return ESP_FAIL; + s_threads_mux = xSemaphoreCreateMutex(); + if (s_threads_mux == NULL) { + vSemaphoreDelete(s_once_mux); + return ESP_FAIL; + } + return ESP_OK; +} + +static TaskHandle_t pthread_find_handle(pthread_t thread) +{ + ListItem_t const *list_end = listGET_END_MARKER(&s_threads_list); + ListItem_t *list_item = listGET_HEAD_ENTRY(&s_threads_list); + while (list_item != list_end) { + esp_pthread_t *pthread = listGET_LIST_ITEM_OWNER(list_item); + if ((pthread_t)pthread == thread) { + return (TaskHandle_t)listGET_LIST_ITEM_VALUE(list_item); + } + list_item = listGET_NEXT(list_item); + } + return NULL; +} + +static esp_pthread_t *pthread_find(TaskHandle_t task_handle) +{ + ListItem_t const *list_end = listGET_END_MARKER(&s_threads_list); + ListItem_t *list_item = listGET_HEAD_ENTRY(&s_threads_list); + while (list_item != list_end) { + TaskHandle_t cur_handle = (TaskHandle_t)listGET_LIST_ITEM_VALUE(list_item); + if (task_handle == cur_handle) { + return (esp_pthread_t *)listGET_LIST_ITEM_OWNER(list_item); + } + list_item = listGET_NEXT(list_item); + } + return NULL; +} + +static void pthread_task_func(void *arg) +{ + esp_pthread_task_arg_t *task_arg = (esp_pthread_task_arg_t *)arg; + + ESP_PTHREAD_LOGV(TAG, "%s ENTER %p", __FUNCTION__, task_arg->func); + + // wait for start + xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); + + ESP_PTHREAD_LOGV(TAG, "%s START %p", __FUNCTION__, task_arg->func); + + task_arg->func(task_arg->arg); + + ESP_PTHREAD_LOGV(TAG, "%s END %p", __FUNCTION__, task_arg->func); + + if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) == pdTRUE) { + esp_pthread_t *pthread = pthread_find(xTaskGetCurrentTaskHandle()); + if (pthread) { + pthread->state = ESP_PTHREAD_STATE_EXIT; + if (pthread->join_task) { + // notify join + xTaskNotify(pthread->join_task, 0, eNoAction); + } + } else { + assert(false && "Failed to find pthread for current task!"); + } + xSemaphoreGive(s_threads_mux); + } else { + assert(false && "Failed to lock threads list!"); + } + // TODO: Remove from list??? + //free(task_arg->pthread); + free(task_arg); + vTaskDelete(NULL); + + ESP_PTHREAD_LOGV(TAG, "%s EXIT %p", __FUNCTION__, task_arg->func); +} + +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg) +{ + TaskHandle_t xHandle = NULL; + // int priority; // Priority + // int stacksize; // Stack size + // int initial_state; // Initial state + // int cpu; // CPU affinity + // int res; + + ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); + if (attr) { + ESP_PTHREAD_LOGE(TAG, "Attrs not supported!"); + return EINVAL; + } + // if (attr) { + // stacksize = attr->stack_size; + // if (stacksize < PTHREAD_STACK_MIN) { + // errno = EINVAL; + // return EINVAL; + // } + // priority = attr->sched_priority; + // initial_state = attr->initial_state; + // cpu = attr->cpuset; + // } else { + // stacksize = CONFIG_LUA_RTOS_LUA_THREAD_STACK_SIZE; + // initial_state = PTHREAD_INITIAL_STATE_RUN; + // priority = CONFIG_LUA_RTOS_LUA_TASK_PRIORITY; + + // if (portNUM_PROCESSORS > 0) { + // cpu = 0; + // } else { + // cpu = tskNO_AFFINITY; + // } + // } + esp_pthread_task_arg_t *task_arg = malloc(sizeof(esp_pthread_task_arg_t)); + if (task_arg == NULL) { + ESP_PTHREAD_LOGE(TAG, "Failed to allocate task args!"); + errno = ENOMEM; + return ENOMEM; + } + memset(task_arg, 0, sizeof(esp_pthread_task_arg_t)); + esp_pthread_t *pthread = malloc(sizeof(esp_pthread_t)); + if (pthread == NULL) { + ESP_PTHREAD_LOGE(TAG, "Failed to allocate pthread data!"); + free(task_arg); + errno = ENOMEM; + return ENOMEM; + } + memset(pthread, 0, sizeof(esp_pthread_t)); + task_arg->func = start_routine; + task_arg->arg = arg; + //task_arg->pthread = pthread; + BaseType_t res = xTaskCreate(&pthread_task_func, "pthread", configMINIMAL_STACK_SIZE, task_arg, PTHREAD_TASK_PRIO_DEFAULT, &xHandle); + if(res != pdPASS) { + ESP_PTHREAD_LOGE(TAG, "Failed to create task!"); + free(pthread); + free(task_arg); + if (res == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) { + errno = ENOMEM; + return ENOMEM; + } else { + errno = EAGAIN; + return EAGAIN; + } + } + vListInitialiseItem((ListItem_t *)&pthread->list_item); + listSET_LIST_ITEM_OWNER((ListItem_t *)&pthread->list_item, pthread); + listSET_LIST_ITEM_VALUE((ListItem_t *)&pthread->list_item, (TickType_t)xHandle); + + if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) == pdTRUE) { + vListInsertEnd((List_t *)&s_threads_list, (ListItem_t *)&pthread->list_item); + xSemaphoreGive(s_threads_mux); + } else { + assert(false && "Failed to lock threads list!"); + } + // start task + xTaskNotify(xHandle, 0, eNoAction); + + *thread = (pthread_t)pthread; // pointer value fit into pthread_t (uint32_t) + + ESP_PTHREAD_LOGV(TAG, "Created task %x", (uint32_t)xHandle); + + return 0; +} + +int pthread_join(pthread_t thread, void **retval) +{ + esp_pthread_t *pthread = (esp_pthread_t *)thread; + bool wait = false; + int ret = 0; + + ets_printf("%s\n", __FUNCTION__); + // find task + if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) == pdTRUE) { + //uxBitsWaitedFor = listGET_LIST_ITEM_VALUE(list_item); + // TODO: check if task is joinable + // TODO: PTHREAD_CANCELED??? + TaskHandle_t handle = pthread_find_handle(thread); + if (!handle) { + errno = ESRCH; // not found + ret = ESRCH; + } else if (pthread->join_task) { + errno = EINVAL; // already have waiting task to join + ret = EINVAL; + } else if (handle == xTaskGetCurrentTaskHandle()) { + errno = EDEADLK; // join to self or join to each other + ret = EDEADLK; + } else { + esp_pthread_t *cur_pthread = pthread_find(xTaskGetCurrentTaskHandle()); + if (cur_pthread && cur_pthread->join_task == handle) { + errno = EDEADLK; // join to each other + ret = EDEADLK; + } else { + if (pthread->state == ESP_PTHREAD_STATE_RUN) { + pthread->join_task = xTaskGetCurrentTaskHandle(); + wait = true; + } + } + } + xSemaphoreGive(s_threads_mux); + } else { + assert(false && "Failed to lock threads list!"); + } + if (wait) { + // TODO: handle caller cancelation??? + xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); + } + if (retval) { + *retval = 0; // no exit code in FreeRTOS + } + return ret; +} + +int pthread_detach(pthread_t thread) +{ + assert(false && "pthread_detach not supported!"); + return -1; +} + +int pthread_cancel(pthread_t thread) +{ + assert(false && "pthread_cancel not supported!"); + return -1; +} + +pthread_t pthread_self(void) +{ + esp_pthread_t *pthread = pthread_find(xTaskGetCurrentTaskHandle()); + if (!pthread) { + assert(false && "Failed to find current thread ID!"); + } + return (pthread_t)pthread; +} + +int pthread_equal(pthread_t t1, pthread_t t2) +{ + ets_printf("%s %x %x\n", __FUNCTION__, t1, t2); + return t1 == t2 ? 1 : 0; +} + +/***************** KEY ******************/ +int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) +{ + static int s_created; + //struct pthread_key *key; + + ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); + + if (s_created) { + assert(false && "CREATED!"); + return ENOMEM; + } + +// key = (struct pthread_key *)malloc(sizeof(struct pthread_key)); +// if (!key) { +// errno = ENOMEM; +// return ENOMEM; +// } +// key->destructor = destructor; + +// list_init(&key->specific,1); + +// vListInsert((List_t *)s_key_list, (ListItem_t *)&); +// // Add key to key list +// res = list_add(&key_list, key, k); +// if (res) { +// free(key); +// errno = res; +// return res; +// } + *key = 1; + s_created = 1; + return 0; +} + +int pthread_key_delete(pthread_key_t key) +{ + ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); + assert(false && "NOT IMPLEMENTED!"); + return -1; +} + +void *pthread_getspecific(pthread_key_t key) +{ + ets_printf("%s\n", __FUNCTION__); + assert(false && "NOT IMPLEMENTED!"); + return NULL; +} + +int pthread_setspecific(pthread_key_t key, const void *value) +{ + ets_printf("%s\n", __FUNCTION__); + assert(false && "NOT IMPLEMENTED!"); + return -1; +} + +/***************** ONCE ******************/ +int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) +{ + ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); + + if (once_control == NULL || init_routine == NULL || !once_control->is_initialized) { + ESP_PTHREAD_LOGE(TAG, "%s: Invalid args!", __FUNCTION__); + return EINVAL; + } + + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + // do not take mutex if OS is not running yet + if (!cur_task || xSemaphoreTake(s_once_mux, portMAX_DELAY) == pdTRUE) + { + if (!once_control->init_executed) { + ESP_PTHREAD_LOGV(TAG, "%s: call init_routine %p", __FUNCTION__, once_control); + init_routine(); + once_control->init_executed = 1; + } + if (cur_task) { + xSemaphoreGive(s_once_mux); + } + } + else + { + ESP_PTHREAD_LOGE(TAG, "%s: Failed to lock!", __FUNCTION__); + return EBUSY; + } + + return 0; +} + +/***************** MUTEX ******************/ +int pthread_mutex_lock(pthread_mutex_t *mutex) +{ + ets_printf("%s\n", __FUNCTION__); + assert(false && "NOT IMPLEMENTED!"); + return -1; +} + +//int pthread_mutex_trylock(pthread_mutex_t *mutex); +int pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + ets_printf("%s\n", __FUNCTION__); + assert(false && "NOT IMPLEMENTED!"); + return -1; +} + diff --git a/components/pthread/test/component.mk b/components/pthread/test/component.mk new file mode 100644 index 0000000000..5dd172bdb7 --- /dev/null +++ b/components/pthread/test/component.mk @@ -0,0 +1,5 @@ +# +#Component Makefile +# + +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/pthread/test/test_pthread.c b/components/pthread/test/test_pthread.c new file mode 100644 index 0000000000..8625f84ebd --- /dev/null +++ b/components/pthread/test/test_pthread.c @@ -0,0 +1,11 @@ +#include "unity.h" + +TEST_CASE("pthread C test 1", "[pthread]") +{ + // int delay_ms = 50; + // const delay_test_arg_t args = { .delay_us = delay_ms * 1000, .method = 1 }; + // xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 0); + // vTaskDelay(delay_ms / portTICK_PERIOD_MS + 1); + // xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 1); + // vTaskDelay(delay_ms / portTICK_PERIOD_MS + 1); +} diff --git a/components/pthread/test/test_pthread_cxx.cpp b/components/pthread/test/test_pthread_cxx.cpp new file mode 100644 index 0000000000..800abab561 --- /dev/null +++ b/components/pthread/test/test_pthread_cxx.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include "unity.h" +#include "rom/ets_sys.h" + +std::shared_ptr global_sp; +std::mutex mtx; + +void thread_main() { + int i = 0; + //std::cout << "thread_main!" << std::endl; + ets_printf("thread_main\n"); + + while (1) { + // std::cout << "thread_main " << i << std::endl; + ets_printf("thread_main %d\n", i); + i++; + } + // auto local_sp = global_sp; // OK, copy constructor's parameter is reference-to-const + + // int i = *global_sp; // OK, operator* is const + // int j = *local_sp; // OK, does not operate on global_sp + + // *global_sp = 2; // NOT OK, modifies int visible to other threads + // *local_sp = 2; // NOT OK, modifies int visible to other threads + + // mtx.lock(); + // global_sp.reset(); // NOT OK, reset is non-const + // mtx.unlock(); + //local_sp.reset(); // OK, does not operate on global_sp +} + +//extern "C" +TEST_CASE("pthread CXX test 1", "[pthread]") +{ + std::cout << "Hello world!" << std::endl; + + global_sp.reset(new int(1)); + std::thread t1(thread_main); + // std::thread t2(thread_main); + t1.join(); + // t2.join(); +} From c631c6b3585b36f734b5ff3a3b68c6e1885718d2 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Thu, 24 Aug 2017 22:52:49 +0300 Subject: [PATCH 2/8] 1) Mux API was added 2) C++ test for threading and mux functions --- components/freertos/tasks.c | 4 +- components/pthread/pthread.c | 190 ++++++++++++++++--- components/pthread/test/component.mk | 4 + components/pthread/test/test_pthread_cxx.cpp | 55 +++--- 4 files changed, 200 insertions(+), 53 deletions(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 396ad0cd72..e4b63fdad3 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -4623,7 +4623,7 @@ TickType_t uxReturn; TickType_t xTimeToWake; BaseType_t xReturn; - UNTESTED_FUNCTION(); + // UNTESTED_FUNCTION(); taskENTER_CRITICAL(&xTaskQueueMutex); { /* Only block if a notification is not already pending. */ @@ -4747,7 +4747,7 @@ TickType_t uxReturn; eNotifyValue eOriginalNotifyState; BaseType_t xReturn = pdPASS; - UNTESTED_FUNCTION(); + // UNTESTED_FUNCTION(); configASSERT( xTaskToNotify ); pxTCB = ( TCB_t * ) xTaskToNotify; diff --git a/components/pthread/pthread.c b/components/pthread/pthread.c index ca54f75e7e..7111949ee3 100644 --- a/components/pthread/pthread.c +++ b/components/pthread/pthread.c @@ -2,6 +2,7 @@ #include #include #include "esp_err.h" +#include "esp_attr.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" @@ -29,14 +30,9 @@ const static char *TAG = "esp_pthread"; #define ESP_PTHREAD_LOGD( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, D, ESP_LOG_DEBUG, format, ##__VA_ARGS__) #define ESP_PTHREAD_LOGV( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, V, ESP_LOG_VERBOSE, format, ##__VA_ARGS__) #define ESP_PTHREAD_LOGO( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, E, ESP_LOG_NONE, format, ##__VA_ARGS__) -// #define ESP_PTHREAD_LOGE( tag, format, ... ) ESP_PTHREAD_LOG(tag ": " format "\n", ##__VA_ARGS__) -// #define ESP_PTHREAD_LOGW( tag, format, ... ) ESP_PTHREAD_LOG(tag ": " format "\n", ##__VA_ARGS__) -// #define ESP_PTHREAD_LOGI( tag, format, ... ) ESP_PTHREAD_LOG(tag ": " format "\n", ##__VA_ARGS__) -// #define ESP_PTHREAD_LOGD( tag, format, ... ) ESP_PTHREAD_LOG(tag ": " format "\n", ##__VA_ARGS__) -// #define ESP_PTHREAD_LOGV( tag, format, ... ) ESP_PTHREAD_LOG(tag ": " format "\n", ##__VA_ARGS__) -// #define ESP_PTHREAD_LOGO( tag, format, ... ) ESP_PTHREAD_LOG(tag ": " format "\n", ##__VA_ARGS__) -#define PTHREAD_TASK_PRIO_DEFAULT 5 +#define PTHREAD_TASK_PRIO_DEFAULT 5 +#define PTHREAD_TASK_STACK_SZ_DEFAULT (2048)//configMINIMAL_STACK_SIZE is not enough #define ESP_PTHREAD_STATE_RUN 0 #define ESP_PTHREAD_STATE_EXIT 1 @@ -53,17 +49,27 @@ typedef struct { void *arg; } esp_pthread_task_arg_t; +typedef struct { + ListItem_t list_item; + SemaphoreHandle_t sem; + int type; +} esp_pthread_mutex_t; + static SemaphoreHandle_t s_once_mux = NULL; static SemaphoreHandle_t s_threads_mux = NULL; +//static SemaphoreHandle_t s_mutexes_mux = NULL; static List_t s_threads_list; +//static List_t s_mutexes_list; //static List_t s_key_list; +static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickType_t tmo); + int esp_pthread_init(void) { - ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); vListInitialise((List_t *)&s_threads_list); -// vListInitialise((List_t *)&s_key_list); + // vListInitialise((List_t *)&s_mutexes_list); + // vListInitialise((List_t *)&s_key_list); s_once_mux = xSemaphoreCreateMutex(); if (s_once_mux == NULL) return ESP_FAIL; @@ -72,6 +78,12 @@ int esp_pthread_init(void) vSemaphoreDelete(s_once_mux); return ESP_FAIL; } + // s_mutexes_mux = xSemaphoreCreateMutex(); + // if (s_mutexes_mux == NULL) { + // vSemaphoreDelete(s_threads_mux); + // vSemaphoreDelete(s_once_mux); + // return ESP_FAIL; + // } return ESP_OK; } @@ -194,7 +206,7 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, task_arg->func = start_routine; task_arg->arg = arg; //task_arg->pthread = pthread; - BaseType_t res = xTaskCreate(&pthread_task_func, "pthread", configMINIMAL_STACK_SIZE, task_arg, PTHREAD_TASK_PRIO_DEFAULT, &xHandle); + BaseType_t res = xTaskCreate(&pthread_task_func, "pthread", PTHREAD_TASK_STACK_SZ_DEFAULT, task_arg, PTHREAD_TASK_PRIO_DEFAULT, &xHandle); if(res != pdPASS) { ESP_PTHREAD_LOGE(TAG, "Failed to create task!"); free(pthread); @@ -237,7 +249,7 @@ int pthread_join(pthread_t thread, void **retval) // find task if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) == pdTRUE) { //uxBitsWaitedFor = listGET_LIST_ITEM_VALUE(list_item); - // TODO: check if task is joinable + // TODO: check if task is joinable??? // TODO: PTHREAD_CANCELED??? TaskHandle_t handle = pthread_find_handle(thread); if (!handle) { @@ -287,6 +299,12 @@ int pthread_cancel(pthread_t thread) return -1; } +int sched_yield( void ) +{ + vTaskDelay(0); + return 0; +} + pthread_t pthread_self(void) { esp_pthread_t *pthread = pthread_find(xTaskGetCurrentTaskHandle()); @@ -298,7 +316,6 @@ pthread_t pthread_self(void) int pthread_equal(pthread_t t1, pthread_t t2) { - ets_printf("%s %x %x\n", __FUNCTION__, t1, t2); return t1 == t2 ? 1 : 0; } @@ -306,7 +323,6 @@ int pthread_equal(pthread_t t1, pthread_t t2) int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) { static int s_created; - //struct pthread_key *key; ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); @@ -346,14 +362,14 @@ int pthread_key_delete(pthread_key_t key) void *pthread_getspecific(pthread_key_t key) { - ets_printf("%s\n", __FUNCTION__); + ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); assert(false && "NOT IMPLEMENTED!"); return NULL; } int pthread_setspecific(pthread_key_t key, const void *value) { - ets_printf("%s\n", __FUNCTION__); + ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); assert(false && "NOT IMPLEMENTED!"); return -1; } @@ -361,8 +377,6 @@ int pthread_setspecific(pthread_key_t key, const void *value) /***************** ONCE ******************/ int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) { - ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); - if (once_control == NULL || init_routine == NULL || !once_control->is_initialized) { ESP_PTHREAD_LOGE(TAG, "%s: Invalid args!", __FUNCTION__); return EINVAL; @@ -391,18 +405,142 @@ int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) } /***************** MUTEX ******************/ -int pthread_mutex_lock(pthread_mutex_t *mutex) +int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) { - ets_printf("%s\n", __FUNCTION__); - assert(false && "NOT IMPLEMENTED!"); - return -1; + int type = PTHREAD_MUTEX_NORMAL; + + ESP_PTHREAD_LOGV(TAG, "%s %p", __FUNCTION__, mutex); + + if (!mutex) { + errno = EINVAL; + return EINVAL; + } + + if (attr && attr->is_initialized) { + type = attr->type; + } + + if (type < PTHREAD_MUTEX_NORMAL || type > PTHREAD_MUTEX_DEFAULT) { + errno = EINVAL; + return EINVAL; + } + + esp_pthread_mutex_t *mux = (esp_pthread_mutex_t *)malloc(sizeof(esp_pthread_mutex_t)); + if (!mux) { + errno = ENOMEM; + return ENOMEM; + } + mux->type = type; + + if (mux->type == PTHREAD_MUTEX_RECURSIVE) { + mux->sem = xSemaphoreCreateRecursiveMutex(); + } else { + mux->sem = xSemaphoreCreateMutex(); + } + if (!mux->sem) { + free(mux); + errno = EAGAIN; + return EAGAIN; + } + + // vListInitialiseItem((ListItem_t *)&mux->list_item); + // listSET_LIST_ITEM_OWNER((ListItem_t *)&mux->list_item, mux); + // //listSET_LIST_ITEM_VALUE((ListItem_t *)&pthread->list_item, (TickType_t)xHandle); + + // if (xSemaphoreTake(s_mutexes_mux, portMAX_DELAY) == pdTRUE) { + // vListInsertEnd((List_t *)&s_mutexes_list, (ListItem_t *)&mux->list_item); + // xSemaphoreGive(s_mutexes_mux); + // } else { + // assert(false && "Failed to lock mutexes list!"); + // } + + *mutex = (pthread_mutex_t)mux; // pointer value fit into pthread_mutex_t (uint32_t) + + return 0; } -//int pthread_mutex_trylock(pthread_mutex_t *mutex); -int pthread_mutex_unlock(pthread_mutex_t *mutex) +int pthread_mutex_destroy(pthread_mutex_t *mutex) { - ets_printf("%s\n", __FUNCTION__); - assert(false && "NOT IMPLEMENTED!"); - return -1; + esp_pthread_mutex_t *mux; + + ESP_PTHREAD_LOGV(TAG, "%s %p", __FUNCTION__, mutex); + + if (!mutex) { + errno = EINVAL; + return EINVAL; + } + mux = (esp_pthread_mutex_t *)*mutex; + + // check if mux is busy + int res = pthread_mutex_lock_internal(mux, 0); + if (res == EBUSY) { + errno = EBUSY; + return EBUSY; + } + + vSemaphoreDelete(mux->sem); + free(mux); + + return 0; } +static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickType_t tmo) +{ + if (mux->type == PTHREAD_MUTEX_RECURSIVE) { + if (xSemaphoreTakeRecursive(mux->sem, tmo) != pdTRUE) { + errno = EBUSY; + return EBUSY; + } + } else { + if (xSemaphoreTake(mux->sem, tmo) != pdTRUE) { + errno = EBUSY; + return EBUSY; + } + } + + return 0; +} + +int IRAM_ATTR pthread_mutex_lock(pthread_mutex_t *mutex) +{ + if (!mutex) { + errno = EINVAL; + return EINVAL; + } + return pthread_mutex_lock_internal((esp_pthread_mutex_t *)*mutex, portMAX_DELAY); +} + +int IRAM_ATTR pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + if (!mutex) { + errno = EINVAL; + return EINVAL; + } + return pthread_mutex_lock_internal((esp_pthread_mutex_t *)*mutex, 0); +} + +int IRAM_ATTR pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + esp_pthread_mutex_t *mux; + + if (!mutex) { + errno = EINVAL; + return EINVAL; + } + mux = (esp_pthread_mutex_t *)*mutex; + + if (mux->type == PTHREAD_MUTEX_RECURSIVE) { + xSemaphoreGiveRecursive(mux->sem); + } else { + xSemaphoreGive(mux->sem); + } + return 0; +} + +// TODO: move to newlib/time.c???? +// needed for std::this_thread::sleep_for +unsigned int sleep(unsigned int seconds) +{ + usleep(seconds*1000000UL); + return 0; +} diff --git a/components/pthread/test/component.mk b/components/pthread/test/component.mk index 5dd172bdb7..af9fbb8371 100644 --- a/components/pthread/test/component.mk +++ b/components/pthread/test/component.mk @@ -2,4 +2,8 @@ #Component Makefile # +COMPONENT_SRCDIRS := . + +#CXXFLAGS += -H + COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/pthread/test/test_pthread_cxx.cpp b/components/pthread/test/test_pthread_cxx.cpp index 800abab561..1402088a79 100644 --- a/components/pthread/test/test_pthread_cxx.cpp +++ b/components/pthread/test/test_pthread_cxx.cpp @@ -2,43 +2,48 @@ #include #include #include "unity.h" -#include "rom/ets_sys.h" std::shared_ptr global_sp; std::mutex mtx; -void thread_main() { +static void thread_main() { int i = 0; - //std::cout << "thread_main!" << std::endl; - ets_printf("thread_main\n"); + std::cout << "thread_main CXX " << std::hex << std::this_thread::get_id() << std::endl; - while (1) { - // std::cout << "thread_main " << i << std::endl; - ets_printf("thread_main %d\n", i); - i++; + while (i < 10) { + // mux test + mtx.lock(); + // yield test + std::this_thread::yield(); + (*global_sp)++; + mtx.unlock(); + std::cout << "thread " << std::hex << std::this_thread::get_id() << ": " << i++ << " val= " << *global_sp << std::endl; + // sleep_for test + std::chrono::milliseconds dur(300); + std::this_thread::sleep_for(dur); + // sleep_until test + using std::chrono::system_clock; + std::time_t tt = system_clock::to_time_t(system_clock::now()); + struct std::tm *ptm = std::localtime(&tt); + ptm->tm_sec = 1; + std::this_thread::sleep_until(system_clock::from_time_t (mktime(ptm))); } - // auto local_sp = global_sp; // OK, copy constructor's parameter is reference-to-const - - // int i = *global_sp; // OK, operator* is const - // int j = *local_sp; // OK, does not operate on global_sp - - // *global_sp = 2; // NOT OK, modifies int visible to other threads - // *local_sp = 2; // NOT OK, modifies int visible to other threads - - // mtx.lock(); - // global_sp.reset(); // NOT OK, reset is non-const - // mtx.unlock(); - //local_sp.reset(); // OK, does not operate on global_sp } -//extern "C" TEST_CASE("pthread CXX test 1", "[pthread]") { - std::cout << "Hello world!" << std::endl; + std::cout << "TEST START!" << std::endl; global_sp.reset(new int(1)); std::thread t1(thread_main); - // std::thread t2(thread_main); - t1.join(); - // t2.join(); + std::thread t2(thread_main); + if (t1.joinable()) { + std::cout << "Join thread " << std::hex << t1.get_id() << std::endl; + t1.join(); + } + if (t2.joinable()) { + std::cout << "Join thread " << std::hex << t2.get_id() << std::endl; + t2.join(); + } + std::cout << "TEST END!" << std::endl; } From bf8ff8c98b812cc7701f8d346e88a1cae1dcf6e4 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Fri, 25 Aug 2017 21:24:17 +0300 Subject: [PATCH 3/8] 1) Thread detach functionality added 2) Recursive mutexes support 3) C++ test updated --- components/pthread/pthread.c | 288 +++++++++++-------- components/pthread/test/test_pthread_cxx.cpp | 66 +++-- 2 files changed, 214 insertions(+), 140 deletions(-) diff --git a/components/pthread/pthread.c b/components/pthread/pthread.c index 7111949ee3..acd4882eb2 100644 --- a/components/pthread/pthread.c +++ b/components/pthread/pthread.c @@ -34,19 +34,20 @@ const static char *TAG = "esp_pthread"; #define PTHREAD_TASK_PRIO_DEFAULT 5 #define PTHREAD_TASK_STACK_SZ_DEFAULT (2048)//configMINIMAL_STACK_SIZE is not enough -#define ESP_PTHREAD_STATE_RUN 0 -#define ESP_PTHREAD_STATE_EXIT 1 -//#define ESP_PTHREAD_STATE_CANCEL 2 +#define PTHREAD_TASK_STATE_RUN 0 +#define PTHREAD_TASK_STATE_EXIT 1 typedef struct { ListItem_t list_item; TaskHandle_t join_task; int state; + bool detached; } esp_pthread_t; typedef struct { void *(*func)(void *); void *arg; + esp_pthread_t *pthread; } esp_pthread_task_arg_t; typedef struct { @@ -55,21 +56,18 @@ typedef struct { int type; } esp_pthread_mutex_t; + static SemaphoreHandle_t s_once_mux = NULL; static SemaphoreHandle_t s_threads_mux = NULL; -//static SemaphoreHandle_t s_mutexes_mux = NULL; static List_t s_threads_list; -//static List_t s_mutexes_list; -//static List_t s_key_list; + static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickType_t tmo); int esp_pthread_init(void) { vListInitialise((List_t *)&s_threads_list); - // vListInitialise((List_t *)&s_mutexes_list); - // vListInitialise((List_t *)&s_key_list); s_once_mux = xSemaphoreCreateMutex(); if (s_once_mux == NULL) return ESP_FAIL; @@ -78,12 +76,6 @@ int esp_pthread_init(void) vSemaphoreDelete(s_once_mux); return ESP_FAIL; } - // s_mutexes_mux = xSemaphoreCreateMutex(); - // if (s_mutexes_mux == NULL) { - // vSemaphoreDelete(s_threads_mux); - // vSemaphoreDelete(s_once_mux); - // return ESP_FAIL; - // } return ESP_OK; } @@ -115,42 +107,50 @@ static esp_pthread_t *pthread_find(TaskHandle_t task_handle) return NULL; } +static void pthread_delete(esp_pthread_t *pthread) +{ + uxListRemove(&pthread->list_item); + free(pthread); +} + static void pthread_task_func(void *arg) { esp_pthread_task_arg_t *task_arg = (esp_pthread_task_arg_t *)arg; - ESP_PTHREAD_LOGV(TAG, "%s ENTER %p", __FUNCTION__, task_arg->func); + ESP_PTHREAD_LOGV(TAG, "%s ENTER %p", __FUNCTION__, task_arg->pthread); // wait for start xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); - ESP_PTHREAD_LOGV(TAG, "%s START %p", __FUNCTION__, task_arg->func); - + ESP_PTHREAD_LOGV(TAG, "%s START %p", __FUNCTION__, task_arg->pthread); task_arg->func(task_arg->arg); + ESP_PTHREAD_LOGV(TAG, "%s END %p", __FUNCTION__, task_arg->pthread); + free(task_arg); - ESP_PTHREAD_LOGV(TAG, "%s END %p", __FUNCTION__, task_arg->func); - - if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) == pdTRUE) { - esp_pthread_t *pthread = pthread_find(xTaskGetCurrentTaskHandle()); - if (pthread) { - pthread->state = ESP_PTHREAD_STATE_EXIT; - if (pthread->join_task) { - // notify join - xTaskNotify(pthread->join_task, 0, eNoAction); - } - } else { - assert(false && "Failed to find pthread for current task!"); - } - xSemaphoreGive(s_threads_mux); - } else { + if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) { assert(false && "Failed to lock threads list!"); } - // TODO: Remove from list??? - //free(task_arg->pthread); - free(task_arg); + esp_pthread_t *pthread = pthread_find(xTaskGetCurrentTaskHandle()); + if (!pthread) { + assert(false && "Failed to find pthread for current task!"); + } + if (pthread->detached) { + // auto-free for detached threads + pthread_delete(pthread); + } else { + // Remove from list, it indicates that task has exited + if (pthread->join_task) { + // notify join + xTaskNotify(pthread->join_task, 0, eNoAction); + } else { + pthread->state = PTHREAD_TASK_STATE_EXIT; + } + } + xSemaphoreGive(s_threads_mux); + vTaskDelete(NULL); - ESP_PTHREAD_LOGV(TAG, "%s EXIT %p", __FUNCTION__, task_arg->func); + ESP_PTHREAD_LOGV(TAG, "%s EXIT", __FUNCTION__); } int pthread_create(pthread_t *thread, const pthread_attr_t *attr, @@ -205,7 +205,7 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, memset(pthread, 0, sizeof(esp_pthread_t)); task_arg->func = start_routine; task_arg->arg = arg; - //task_arg->pthread = pthread; + task_arg->pthread = pthread; BaseType_t res = xTaskCreate(&pthread_task_func, "pthread", PTHREAD_TASK_STACK_SZ_DEFAULT, task_arg, PTHREAD_TASK_PRIO_DEFAULT, &xHandle); if(res != pdPASS) { ESP_PTHREAD_LOGE(TAG, "Failed to create task!"); @@ -223,12 +223,12 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, listSET_LIST_ITEM_OWNER((ListItem_t *)&pthread->list_item, pthread); listSET_LIST_ITEM_VALUE((ListItem_t *)&pthread->list_item, (TickType_t)xHandle); - if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) == pdTRUE) { - vListInsertEnd((List_t *)&s_threads_list, (ListItem_t *)&pthread->list_item); - xSemaphoreGive(s_threads_mux); - } else { + if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) { assert(false && "Failed to lock threads list!"); } + vListInsertEnd((List_t *)&s_threads_list, (ListItem_t *)&pthread->list_item); + xSemaphoreGive(s_threads_mux); + // start task xTaskNotify(xHandle, 0, eNoAction); @@ -242,55 +242,74 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, int pthread_join(pthread_t thread, void **retval) { esp_pthread_t *pthread = (esp_pthread_t *)thread; - bool wait = false; int ret = 0; - ets_printf("%s\n", __FUNCTION__); + ESP_PTHREAD_LOGV(TAG, "%s %p", __FUNCTION__, pthread); + // find task - if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) == pdTRUE) { - //uxBitsWaitedFor = listGET_LIST_ITEM_VALUE(list_item); - // TODO: check if task is joinable??? - // TODO: PTHREAD_CANCELED??? - TaskHandle_t handle = pthread_find_handle(thread); - if (!handle) { - errno = ESRCH; // not found - ret = ESRCH; - } else if (pthread->join_task) { - errno = EINVAL; // already have waiting task to join - ret = EINVAL; - } else if (handle == xTaskGetCurrentTaskHandle()) { - errno = EDEADLK; // join to self or join to each other - ret = EDEADLK; - } else { - esp_pthread_t *cur_pthread = pthread_find(xTaskGetCurrentTaskHandle()); - if (cur_pthread && cur_pthread->join_task == handle) { - errno = EDEADLK; // join to each other - ret = EDEADLK; - } else { - if (pthread->state == ESP_PTHREAD_STATE_RUN) { - pthread->join_task = xTaskGetCurrentTaskHandle(); - wait = true; - } - } - } - xSemaphoreGive(s_threads_mux); - } else { + if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) { assert(false && "Failed to lock threads list!"); } - if (wait) { - // TODO: handle caller cancelation??? - xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); + TaskHandle_t handle = pthread_find_handle(thread); + if (!handle) { + errno = ESRCH; // not found + ret = ESRCH; + } else if (pthread->join_task) { + errno = EINVAL; // already have waiting task to join + ret = EINVAL; + } else if (handle == xTaskGetCurrentTaskHandle()) { + errno = EDEADLK; // join to self not allowed + ret = EDEADLK; + } else { + esp_pthread_t *cur_pthread = pthread_find(xTaskGetCurrentTaskHandle()); + if (cur_pthread && cur_pthread->join_task == handle) { + errno = EDEADLK; // join to each other not allowed + ret = EDEADLK; + } else { + if (pthread->state == PTHREAD_TASK_STATE_RUN) { + pthread->join_task = xTaskGetCurrentTaskHandle(); + } else { + pthread_delete(pthread); + } + } } + xSemaphoreGive(s_threads_mux); + + if (ret == 0 && pthread->join_task) { + xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); + if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) { + assert(false && "Failed to lock threads list!"); + } + pthread_delete(pthread); + xSemaphoreGive(s_threads_mux); + } + if (retval) { *retval = 0; // no exit code in FreeRTOS } + + ESP_PTHREAD_LOGV(TAG, "%s %p EXIT %d", __FUNCTION__, pthread, ret); return ret; } int pthread_detach(pthread_t thread) { - assert(false && "pthread_detach not supported!"); - return -1; + esp_pthread_t *pthread = (esp_pthread_t *)thread; + int ret = 0; + + if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) { + assert(false && "Failed to lock threads list!"); + } + TaskHandle_t handle = pthread_find_handle(thread); + if (!handle) { + errno = ESRCH; // not found + ret = ESRCH; + } else { + pthread->detached = true; + } + xSemaphoreGive(s_threads_mux); + ESP_PTHREAD_LOGV(TAG, "%s %p EXIT %d", __FUNCTION__, pthread, ret); + return ret; } int pthread_cancel(pthread_t thread) @@ -307,10 +326,14 @@ int sched_yield( void ) pthread_t pthread_self(void) { + if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) { + assert(false && "Failed to lock threads list!"); + } esp_pthread_t *pthread = pthread_find(xTaskGetCurrentTaskHandle()); if (!pthread) { assert(false && "Failed to find current thread ID!"); } + xSemaphoreGive(s_threads_mux); return (pthread_t)pthread; } @@ -324,30 +347,11 @@ int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) { static int s_created; - ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); - + //TODO: Key destructors not suppoted! if (s_created) { - assert(false && "CREATED!"); + // key API supports just one key necessary by libstdcxx threading implementation return ENOMEM; } - -// key = (struct pthread_key *)malloc(sizeof(struct pthread_key)); -// if (!key) { -// errno = ENOMEM; -// return ENOMEM; -// } -// key->destructor = destructor; - -// list_init(&key->specific,1); - -// vListInsert((List_t *)s_key_list, (ListItem_t *)&); -// // Add key to key list -// res = list_add(&key_list, key, k); -// if (res) { -// free(key); -// errno = res; -// return res; -// } *key = 1; s_created = 1; return 0; @@ -355,22 +359,19 @@ int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) int pthread_key_delete(pthread_key_t key) { - ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); - assert(false && "NOT IMPLEMENTED!"); + assert(false && "pthread_key_delete not supported!"); return -1; } void *pthread_getspecific(pthread_key_t key) { - ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); - assert(false && "NOT IMPLEMENTED!"); + assert(false && "pthread_getspecific not supported!"); return NULL; } int pthread_setspecific(pthread_key_t key, const void *value) { - ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); - assert(false && "NOT IMPLEMENTED!"); + assert(false && "pthread_setspecific not supported!"); return -1; } @@ -405,26 +406,36 @@ int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) } /***************** MUTEX ******************/ +static int mutexattr_check(const pthread_mutexattr_t *attr) +{ + if (attr->type < PTHREAD_MUTEX_NORMAL || attr->type > PTHREAD_MUTEX_RECURSIVE) { + return EINVAL; + } + return 0; +} + int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) { int type = PTHREAD_MUTEX_NORMAL; - ESP_PTHREAD_LOGV(TAG, "%s %p", __FUNCTION__, mutex); - if (!mutex) { errno = EINVAL; return EINVAL; } - if (attr && attr->is_initialized) { + if (attr) { + if (!attr->is_initialized) { + errno = EINVAL; + return EINVAL; + } + int res = mutexattr_check(attr); + if (res) { + errno = res; + return res; + } type = attr->type; } - if (type < PTHREAD_MUTEX_NORMAL || type > PTHREAD_MUTEX_DEFAULT) { - errno = EINVAL; - return EINVAL; - } - esp_pthread_mutex_t *mux = (esp_pthread_mutex_t *)malloc(sizeof(esp_pthread_mutex_t)); if (!mux) { errno = ENOMEM; @@ -443,17 +454,6 @@ int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) return EAGAIN; } - // vListInitialiseItem((ListItem_t *)&mux->list_item); - // listSET_LIST_ITEM_OWNER((ListItem_t *)&mux->list_item, mux); - // //listSET_LIST_ITEM_VALUE((ListItem_t *)&pthread->list_item, (TickType_t)xHandle); - - // if (xSemaphoreTake(s_mutexes_mux, portMAX_DELAY) == pdTRUE) { - // vListInsertEnd((List_t *)&s_mutexes_list, (ListItem_t *)&mux->list_item); - // xSemaphoreGive(s_mutexes_mux); - // } else { - // assert(false && "Failed to lock mutexes list!"); - // } - *mutex = (pthread_mutex_t)mux; // pointer value fit into pthread_mutex_t (uint32_t) return 0; @@ -537,6 +537,50 @@ int IRAM_ATTR pthread_mutex_unlock(pthread_mutex_t *mutex) return 0; } +int pthread_mutexattr_init(pthread_mutexattr_t *attr) +{ + if (!attr) { + errno = EINVAL; + return EINVAL; + } + attr->type = PTHREAD_MUTEX_NORMAL; + attr->is_initialized = 1; + return 0; +} + +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) +{ + if (!attr) { + errno = EINVAL; + return EINVAL; + } + attr->is_initialized = 0; + return 0; +} + +int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) +{ + assert(false && "pthread_mutexattr_gettype not supported!"); + return -1; +} + +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) +{ + if (!attr) { + errno = EINVAL; + return EINVAL; + } + pthread_mutexattr_t tmp_attr = {.type = type}; + int res = mutexattr_check(&tmp_attr); + if (res) { + errno = res; + } else { + attr->type = type; + } + return res; +} + +/***************** AUX ******************/ // TODO: move to newlib/time.c???? // needed for std::this_thread::sleep_for unsigned int sleep(unsigned int seconds) diff --git a/components/pthread/test/test_pthread_cxx.cpp b/components/pthread/test/test_pthread_cxx.cpp index 1402088a79..ab2ba07ccd 100644 --- a/components/pthread/test/test_pthread_cxx.cpp +++ b/components/pthread/test/test_pthread_cxx.cpp @@ -4,46 +4,76 @@ #include "unity.h" std::shared_ptr global_sp; -std::mutex mtx; +std::mutex mtx; +std::recursive_mutex recur_mtx; -static void thread_main() { +static void thread_do_nothing() {} + +static void thread_main() +{ int i = 0; std::cout << "thread_main CXX " << std::hex << std::this_thread::get_id() << std::endl; - while (i < 10) { + while (i < 3) { + int old_val, new_val; + // mux test mtx.lock(); - // yield test + old_val = *global_sp; std::this_thread::yield(); (*global_sp)++; + std::this_thread::yield(); + new_val = *global_sp; mtx.unlock(); std::cout << "thread " << std::hex << std::this_thread::get_id() << ": " << i++ << " val= " << *global_sp << std::endl; - // sleep_for test + TEST_ASSERT_TRUE(new_val == old_val + 1); + + // sleep_for test std::chrono::milliseconds dur(300); std::this_thread::sleep_for(dur); - // sleep_until test + + // recursive mux test + recur_mtx.lock(); + recur_mtx.lock(); + old_val = *global_sp; + std::this_thread::yield(); + (*global_sp)++; + std::this_thread::yield(); + new_val = *global_sp; + recur_mtx.unlock(); + recur_mtx.unlock(); + std::cout << "thread " << std::hex << std::this_thread::get_id() << ": " << i++ << " val= " << *global_sp << std::endl; + TEST_ASSERT_TRUE(new_val == old_val + 1); + + // sleep_until test using std::chrono::system_clock; - std::time_t tt = system_clock::to_time_t(system_clock::now()); + std::time_t tt = system_clock::to_time_t(system_clock::now()); struct std::tm *ptm = std::localtime(&tt); - ptm->tm_sec = 1; + ptm->tm_sec++; std::this_thread::sleep_until(system_clock::from_time_t (mktime(ptm))); } } TEST_CASE("pthread CXX test 1", "[pthread]") { - std::cout << "TEST START!" << std::endl; - global_sp.reset(new int(1)); - std::thread t1(thread_main); + + std::thread t1(thread_do_nothing); + t1.join(); + std::thread t2(thread_main); - if (t1.joinable()) { - std::cout << "Join thread " << std::hex << t1.get_id() << std::endl; - t1.join(); + std::cout << "Detach thread " << std::hex << t2.get_id() << std::endl; + t2.detach(); + TEST_ASSERT_FALSE(t2.joinable()); + + std::thread t3(thread_main); + std::thread t4(thread_main); + if (t3.joinable()) { + std::cout << "Join thread " << std::hex << t3.get_id() << std::endl; + t3.join(); } - if (t2.joinable()) { - std::cout << "Join thread " << std::hex << t2.get_id() << std::endl; - t2.join(); + if (t4.joinable()) { + std::cout << "Join thread " << std::hex << t4.get_id() << std::endl; + t4.join(); } - std::cout << "TEST END!" << std::endl; } From 8859dab10c99554c78d1e9d57089563effe961d3 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Mon, 28 Aug 2017 18:41:32 +0300 Subject: [PATCH 4/8] Cleanup --- components/pthread/Kconfig | 16 +++++ components/pthread/pthread.c | 66 ++++---------------- components/pthread/test/test_pthread.c | 11 ---- components/pthread/test/test_pthread_cxx.cpp | 2 +- 4 files changed, 29 insertions(+), 66 deletions(-) delete mode 100644 components/pthread/test/test_pthread.c diff --git a/components/pthread/Kconfig b/components/pthread/Kconfig index e69de29bb2..aa4c35b789 100644 --- a/components/pthread/Kconfig +++ b/components/pthread/Kconfig @@ -0,0 +1,16 @@ +menu "PThreads" + +config ESP32_PTHREAD_TASK_PRIO_DEFAULT + int "Default task priority" + range 0 255 + default 5 + help + Priority used to create new tasks with default pthread parameters. + +config ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT + int "Default task stack size" + default 2048 + help + Stack size used to create new tasks with default pthread parameters. + +endmenu diff --git a/components/pthread/pthread.c b/components/pthread/pthread.c index acd4882eb2..44d76ec8ce 100644 --- a/components/pthread/pthread.c +++ b/components/pthread/pthread.c @@ -8,31 +8,15 @@ #include "freertos/semphr.h" #include "freertos/list.h" -#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE//CONFIG_LOG_DEFAULT_LEVEL +#define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL//ESP_LOG_VERBOSE #include "esp_log.h" const static char *TAG = "esp_pthread"; -#define ESP_PTHREAD_LOG( format, ... ) \ - do { \ - ets_printf(format, ##__VA_ARGS__); \ - } while(0) - -#define ESP_PTHREAD_LOG_LEV( _tag_, _L_, level, format, ... ) \ - do { \ - if (LOG_LOCAL_LEVEL >= level) { \ - ESP_PTHREAD_LOG(LOG_FORMAT(_L_, format), esp_log_early_timestamp(), _tag_ , ##__VA_ARGS__); \ - } \ - } while(0) - -#define ESP_PTHREAD_LOGE( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, E, ESP_LOG_ERROR, format, ##__VA_ARGS__) -#define ESP_PTHREAD_LOGW( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, W, ESP_LOG_WARN, format, ##__VA_ARGS__) -#define ESP_PTHREAD_LOGI( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, I, ESP_LOG_INFO, format, ##__VA_ARGS__) -#define ESP_PTHREAD_LOGD( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, D, ESP_LOG_DEBUG, format, ##__VA_ARGS__) -#define ESP_PTHREAD_LOGV( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, V, ESP_LOG_VERBOSE, format, ##__VA_ARGS__) -#define ESP_PTHREAD_LOGO( _tag_, format, ... ) ESP_PTHREAD_LOG_LEV(_tag_, E, ESP_LOG_NONE, format, ##__VA_ARGS__) - -#define PTHREAD_TASK_PRIO_DEFAULT 5 -#define PTHREAD_TASK_STACK_SZ_DEFAULT (2048)//configMINIMAL_STACK_SIZE is not enough +#define ESP_PTHREAD_LOGE( _tag_, format, ... ) ESP_LOGE(_tag_, format, ##__VA_ARGS__) +#define ESP_PTHREAD_LOGW( _tag_, format, ... ) ESP_LOGW(_tag_, format, ##__VA_ARGS__) +#define ESP_PTHREAD_LOGI( _tag_, format, ... ) ESP_LOGI(_tag_, format, ##__VA_ARGS__) +#define ESP_PTHREAD_LOGD( _tag_, format, ... ) ESP_LOGD(_tag_, format, ##__VA_ARGS__) +#define ESP_PTHREAD_LOGV( _tag_, format, ... ) ESP_LOGV(_tag_, format, ##__VA_ARGS__) #define PTHREAD_TASK_STATE_RUN 0 #define PTHREAD_TASK_STATE_EXIT 1 @@ -47,7 +31,6 @@ typedef struct { typedef struct { void *(*func)(void *); void *arg; - esp_pthread_t *pthread; } esp_pthread_task_arg_t; typedef struct { @@ -117,14 +100,14 @@ static void pthread_task_func(void *arg) { esp_pthread_task_arg_t *task_arg = (esp_pthread_task_arg_t *)arg; - ESP_PTHREAD_LOGV(TAG, "%s ENTER %p", __FUNCTION__, task_arg->pthread); + ESP_PTHREAD_LOGV(TAG, "%s ENTER %p", __FUNCTION__, task_arg->func); // wait for start xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); - ESP_PTHREAD_LOGV(TAG, "%s START %p", __FUNCTION__, task_arg->pthread); + ESP_PTHREAD_LOGV(TAG, "%s START %p", __FUNCTION__, task_arg->func); task_arg->func(task_arg->arg); - ESP_PTHREAD_LOGV(TAG, "%s END %p", __FUNCTION__, task_arg->pthread); + ESP_PTHREAD_LOGV(TAG, "%s END %p", __FUNCTION__, task_arg->func); free(task_arg); if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) { @@ -143,7 +126,7 @@ static void pthread_task_func(void *arg) // notify join xTaskNotify(pthread->join_task, 0, eNoAction); } else { - pthread->state = PTHREAD_TASK_STATE_EXIT; + pthread->state = PTHREAD_TASK_STATE_EXIT; } } xSemaphoreGive(s_threads_mux); @@ -157,37 +140,12 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) { TaskHandle_t xHandle = NULL; - // int priority; // Priority - // int stacksize; // Stack size - // int initial_state; // Initial state - // int cpu; // CPU affinity - // int res; ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); if (attr) { ESP_PTHREAD_LOGE(TAG, "Attrs not supported!"); return EINVAL; } - // if (attr) { - // stacksize = attr->stack_size; - // if (stacksize < PTHREAD_STACK_MIN) { - // errno = EINVAL; - // return EINVAL; - // } - // priority = attr->sched_priority; - // initial_state = attr->initial_state; - // cpu = attr->cpuset; - // } else { - // stacksize = CONFIG_LUA_RTOS_LUA_THREAD_STACK_SIZE; - // initial_state = PTHREAD_INITIAL_STATE_RUN; - // priority = CONFIG_LUA_RTOS_LUA_TASK_PRIORITY; - - // if (portNUM_PROCESSORS > 0) { - // cpu = 0; - // } else { - // cpu = tskNO_AFFINITY; - // } - // } esp_pthread_task_arg_t *task_arg = malloc(sizeof(esp_pthread_task_arg_t)); if (task_arg == NULL) { ESP_PTHREAD_LOGE(TAG, "Failed to allocate task args!"); @@ -205,8 +163,8 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, memset(pthread, 0, sizeof(esp_pthread_t)); task_arg->func = start_routine; task_arg->arg = arg; - task_arg->pthread = pthread; - BaseType_t res = xTaskCreate(&pthread_task_func, "pthread", PTHREAD_TASK_STACK_SZ_DEFAULT, task_arg, PTHREAD_TASK_PRIO_DEFAULT, &xHandle); + BaseType_t res = xTaskCreate(&pthread_task_func, "pthread", CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT, + task_arg, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT, &xHandle); if(res != pdPASS) { ESP_PTHREAD_LOGE(TAG, "Failed to create task!"); free(pthread); diff --git a/components/pthread/test/test_pthread.c b/components/pthread/test/test_pthread.c deleted file mode 100644 index 8625f84ebd..0000000000 --- a/components/pthread/test/test_pthread.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "unity.h" - -TEST_CASE("pthread C test 1", "[pthread]") -{ - // int delay_ms = 50; - // const delay_test_arg_t args = { .delay_us = delay_ms * 1000, .method = 1 }; - // xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 0); - // vTaskDelay(delay_ms / portTICK_PERIOD_MS + 1); - // xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 1); - // vTaskDelay(delay_ms / portTICK_PERIOD_MS + 1); -} diff --git a/components/pthread/test/test_pthread_cxx.cpp b/components/pthread/test/test_pthread_cxx.cpp index ab2ba07ccd..2cdaf18cf9 100644 --- a/components/pthread/test/test_pthread_cxx.cpp +++ b/components/pthread/test/test_pthread_cxx.cpp @@ -54,7 +54,7 @@ static void thread_main() } } -TEST_CASE("pthread CXX test 1", "[pthread]") +TEST_CASE("pthread CXX", "[pthread]") { global_sp.reset(new int(1)); From 20942421c3599cac568f4eb0e8602f848274c2be Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Fri, 1 Sep 2017 02:18:14 +0300 Subject: [PATCH 5/8] According to review comments --- components/freertos/tasks.c | 2 - components/newlib/include/pthread.h | 2 +- components/newlib/include/sys/features.h | 7 +- components/newlib/include/sys/sched.h | 2 + components/newlib/time.c | 6 + components/pthread/pthread.c | 598 ++++++++++--------- components/pthread/test/test_pthread_cxx.cpp | 40 +- 7 files changed, 337 insertions(+), 320 deletions(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index e4b63fdad3..0433deb2af 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -4623,7 +4623,6 @@ TickType_t uxReturn; TickType_t xTimeToWake; BaseType_t xReturn; - // UNTESTED_FUNCTION(); taskENTER_CRITICAL(&xTaskQueueMutex); { /* Only block if a notification is not already pending. */ @@ -4747,7 +4746,6 @@ TickType_t uxReturn; eNotifyValue eOriginalNotifyState; BaseType_t xReturn = pdPASS; - // UNTESTED_FUNCTION(); configASSERT( xTaskToNotify ); pxTCB = ( TCB_t * ) xTaskToNotify; diff --git a/components/newlib/include/pthread.h b/components/newlib/include/pthread.h index 907970fdf9..db1f9c1ca3 100644 --- a/components/newlib/include/pthread.h +++ b/components/newlib/include/pthread.h @@ -31,7 +31,7 @@ extern "C" { #include #include -#include +#include #include struct _pthread_cleanup_context { diff --git a/components/newlib/include/sys/features.h b/components/newlib/include/sys/features.h index 792f68bbc5..87f3314fd9 100644 --- a/components/newlib/include/sys/features.h +++ b/components/newlib/include/sys/features.h @@ -210,8 +210,11 @@ extern "C" { #endif /* __CYGWIN__ */ -#define _POSIX_THREADS 1 -#define _UNIX98_THREAD_MUTEX_ATTRIBUTES 1 +/* ESP-IDF-specific: enable pthreads support */ +#ifdef __XTENSA__ +#define _POSIX_THREADS 1 +#define _UNIX98_THREAD_MUTEX_ATTRIBUTES 1 +#endif /* Per the permission given in POSIX.1-2008 section 2.2.1, define * _POSIX_C_SOURCE if _XOPEN_SOURCE is defined and _POSIX_C_SOURCE is not. diff --git a/components/newlib/include/sys/sched.h b/components/newlib/include/sys/sched.h index 58f99d6823..8554fc2b92 100644 --- a/components/newlib/include/sys/sched.h +++ b/components/newlib/include/sys/sched.h @@ -58,6 +58,8 @@ struct sched_param { #endif }; +int sched_yield( void ); + #ifdef __cplusplus } #endif diff --git a/components/newlib/time.c b/components/newlib/time.c index 1655c0b8e6..6fde8b9344 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -216,6 +216,12 @@ int usleep(useconds_t us) return 0; } +unsigned int sleep(unsigned int seconds) +{ + usleep(seconds*1000000UL); + return 0; +} + uint32_t system_get_time(void) { #if defined( WITH_FRC1 ) || defined( WITH_RTC ) diff --git a/components/pthread/pthread.c b/components/pthread/pthread.c index 44d76ec8ce..f2409671ad 100644 --- a/components/pthread/pthread.c +++ b/components/pthread/pthread.c @@ -8,35 +8,35 @@ #include "freertos/semphr.h" #include "freertos/list.h" -#define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL//ESP_LOG_VERBOSE +#define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL #include "esp_log.h" const static char *TAG = "esp_pthread"; -#define ESP_PTHREAD_LOGE( _tag_, format, ... ) ESP_LOGE(_tag_, format, ##__VA_ARGS__) -#define ESP_PTHREAD_LOGW( _tag_, format, ... ) ESP_LOGW(_tag_, format, ##__VA_ARGS__) -#define ESP_PTHREAD_LOGI( _tag_, format, ... ) ESP_LOGI(_tag_, format, ##__VA_ARGS__) -#define ESP_PTHREAD_LOGD( _tag_, format, ... ) ESP_LOGD(_tag_, format, ##__VA_ARGS__) -#define ESP_PTHREAD_LOGV( _tag_, format, ... ) ESP_LOGV(_tag_, format, ##__VA_ARGS__) - -#define PTHREAD_TASK_STATE_RUN 0 -#define PTHREAD_TASK_STATE_EXIT 1 +/** task state */ +enum esp_pthread_task_state { + PTHREAD_TASK_STATE_RUN, + PTHREAD_TASK_STATE_EXIT +}; +/** pthread thread FreeRTOS wrapper */ typedef struct { - ListItem_t list_item; - TaskHandle_t join_task; - int state; - bool detached; + ListItem_t list_item; ///< Tasks list node struct. FreeRTOS task handle is kept as list_item.xItemValue + TaskHandle_t join_task; ///< Handle of the task waiting to join + enum esp_pthread_task_state state; ///< pthread task state + bool detached; ///< True if pthread is detached } esp_pthread_t; +/** pthread wrapper task arg */ typedef struct { - void *(*func)(void *); - void *arg; + void *(*func)(void *); ///< user task entry + void *arg; ///< user task argument } esp_pthread_task_arg_t; +/** pthread mutex FreeRTOS wrapper */ typedef struct { - ListItem_t list_item; - SemaphoreHandle_t sem; - int type; + ListItem_t list_item; ///< mutexes list node struct + SemaphoreHandle_t sem; ///< Handle of the task waiting to join + int type; ///< Handle of the task waiting to join } esp_pthread_mutex_t; @@ -50,358 +50,371 @@ static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickT int esp_pthread_init(void) { - vListInitialise((List_t *)&s_threads_list); - s_once_mux = xSemaphoreCreateMutex(); - if (s_once_mux == NULL) - return ESP_FAIL; - s_threads_mux = xSemaphoreCreateMutex(); - if (s_threads_mux == NULL) { - vSemaphoreDelete(s_once_mux); - return ESP_FAIL; - } - return ESP_OK; + vListInitialise((List_t *)&s_threads_list); + s_once_mux = xSemaphoreCreateMutex(); + if (s_once_mux == NULL) { + return ESP_ERR_NO_MEM; + } + s_threads_mux = xSemaphoreCreateMutex(); + if (s_threads_mux == NULL) { + vSemaphoreDelete(s_once_mux); + return ESP_ERR_NO_MEM; + } + return ESP_OK; } -static TaskHandle_t pthread_find_handle(pthread_t thread) +static void *pthread_find_list_item(void *(*item_check)(ListItem_t *, void *arg), void *check_arg) { - ListItem_t const *list_end = listGET_END_MARKER(&s_threads_list); - ListItem_t *list_item = listGET_HEAD_ENTRY(&s_threads_list); - while (list_item != list_end) { - esp_pthread_t *pthread = listGET_LIST_ITEM_OWNER(list_item); - if ((pthread_t)pthread == thread) { - return (TaskHandle_t)listGET_LIST_ITEM_VALUE(list_item); - } - list_item = listGET_NEXT(list_item); - } - return NULL; + ListItem_t const *list_end = listGET_END_MARKER(&s_threads_list); + ListItem_t *list_item = listGET_HEAD_ENTRY(&s_threads_list); + while (list_item != list_end) { + void *val = item_check(list_item, check_arg); + if (val) { + return val; + } + list_item = listGET_NEXT(list_item); + } + return NULL; } +static void *pthread_get_handle_by_desc(ListItem_t *item, void *arg) +{ + esp_pthread_t *pthread = listGET_LIST_ITEM_OWNER(item); + if (pthread == arg) { + return (void *)listGET_LIST_ITEM_VALUE(item); + } + return NULL; +} +static inline TaskHandle_t pthread_find_handle(pthread_t thread) +{ + return pthread_find_list_item(pthread_get_handle_by_desc, (void *)thread); +} + +static void *pthread_get_desc_by_handle(ListItem_t *item, void *arg) +{ + TaskHandle_t task_handle = arg; + TaskHandle_t cur_handle = (TaskHandle_t)listGET_LIST_ITEM_VALUE(item); + if (task_handle == cur_handle) { + return (esp_pthread_t *)listGET_LIST_ITEM_OWNER(item); + } + return NULL; +} static esp_pthread_t *pthread_find(TaskHandle_t task_handle) { - ListItem_t const *list_end = listGET_END_MARKER(&s_threads_list); - ListItem_t *list_item = listGET_HEAD_ENTRY(&s_threads_list); - while (list_item != list_end) { - TaskHandle_t cur_handle = (TaskHandle_t)listGET_LIST_ITEM_VALUE(list_item); - if (task_handle == cur_handle) { - return (esp_pthread_t *)listGET_LIST_ITEM_OWNER(list_item); - } - list_item = listGET_NEXT(list_item); - } - return NULL; + return pthread_find_list_item(pthread_get_desc_by_handle, task_handle); } static void pthread_delete(esp_pthread_t *pthread) { - uxListRemove(&pthread->list_item); - free(pthread); + uxListRemove(&pthread->list_item); + free(pthread); } static void pthread_task_func(void *arg) { - esp_pthread_task_arg_t *task_arg = (esp_pthread_task_arg_t *)arg; + esp_pthread_task_arg_t *task_arg = (esp_pthread_task_arg_t *)arg; - ESP_PTHREAD_LOGV(TAG, "%s ENTER %p", __FUNCTION__, task_arg->func); + ESP_LOGV(TAG, "%s ENTER %p", __FUNCTION__, task_arg->func); - // wait for start - xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); + // wait for start + xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); - ESP_PTHREAD_LOGV(TAG, "%s START %p", __FUNCTION__, task_arg->func); - task_arg->func(task_arg->arg); - ESP_PTHREAD_LOGV(TAG, "%s END %p", __FUNCTION__, task_arg->func); - free(task_arg); + ESP_LOGV(TAG, "%s START %p", __FUNCTION__, task_arg->func); + task_arg->func(task_arg->arg); + ESP_LOGV(TAG, "%s END %p", __FUNCTION__, task_arg->func); + free(task_arg); if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) { - assert(false && "Failed to lock threads list!"); + assert(false && "Failed to lock threads list!"); } - esp_pthread_t *pthread = pthread_find(xTaskGetCurrentTaskHandle()); - if (!pthread) { - assert(false && "Failed to find pthread for current task!"); + esp_pthread_t *pthread = pthread_find(xTaskGetCurrentTaskHandle()); + if (!pthread) { + assert(false && "Failed to find pthread for current task!"); + } + if (pthread->detached) { + // auto-free for detached threads + pthread_delete(pthread); + } else { + // Remove from list, it indicates that task has exited + if (pthread->join_task) { + // notify join + xTaskNotify(pthread->join_task, 0, eNoAction); + } else { + pthread->state = PTHREAD_TASK_STATE_EXIT; + } } - if (pthread->detached) { - // auto-free for detached threads - pthread_delete(pthread); - } else { - // Remove from list, it indicates that task has exited - if (pthread->join_task) { - // notify join - xTaskNotify(pthread->join_task, 0, eNoAction); - } else { - pthread->state = PTHREAD_TASK_STATE_EXIT; - } - } xSemaphoreGive(s_threads_mux); - vTaskDelete(NULL); + vTaskDelete(NULL); - ESP_PTHREAD_LOGV(TAG, "%s EXIT", __FUNCTION__); + ESP_LOGV(TAG, "%s EXIT", __FUNCTION__); } int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) { - TaskHandle_t xHandle = NULL; + TaskHandle_t xHandle = NULL; - ESP_PTHREAD_LOGV(TAG, "%s", __FUNCTION__); - if (attr) { - ESP_PTHREAD_LOGE(TAG, "Attrs not supported!"); - return EINVAL; - } - esp_pthread_task_arg_t *task_arg = malloc(sizeof(esp_pthread_task_arg_t)); - if (task_arg == NULL) { - ESP_PTHREAD_LOGE(TAG, "Failed to allocate task args!"); - errno = ENOMEM; - return ENOMEM; - } - memset(task_arg, 0, sizeof(esp_pthread_task_arg_t)); - esp_pthread_t *pthread = malloc(sizeof(esp_pthread_t)); - if (pthread == NULL) { - ESP_PTHREAD_LOGE(TAG, "Failed to allocate pthread data!"); - free(task_arg); - errno = ENOMEM; - return ENOMEM; - } - memset(pthread, 0, sizeof(esp_pthread_t)); - task_arg->func = start_routine; - task_arg->arg = arg; + ESP_LOGV(TAG, "%s", __FUNCTION__); + if (attr) { + assert(false && "pthread_create: attrs not supported!"); + } + esp_pthread_task_arg_t *task_arg = malloc(sizeof(esp_pthread_task_arg_t)); + if (task_arg == NULL) { + ESP_LOGE(TAG, "Failed to allocate task args!"); + errno = ENOMEM; + return ENOMEM; + } + memset(task_arg, 0, sizeof(esp_pthread_task_arg_t)); + esp_pthread_t *pthread = malloc(sizeof(esp_pthread_t)); + if (pthread == NULL) { + ESP_LOGE(TAG, "Failed to allocate pthread data!"); + free(task_arg); + errno = ENOMEM; + return ENOMEM; + } + memset(pthread, 0, sizeof(esp_pthread_t)); + task_arg->func = start_routine; + task_arg->arg = arg; BaseType_t res = xTaskCreate(&pthread_task_func, "pthread", CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT, - task_arg, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT, &xHandle); - if(res != pdPASS) { - ESP_PTHREAD_LOGE(TAG, "Failed to create task!"); - free(pthread); - free(task_arg); - if (res == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) { - errno = ENOMEM; - return ENOMEM; - } else { - errno = EAGAIN; - return EAGAIN; - } - } - vListInitialiseItem((ListItem_t *)&pthread->list_item); - listSET_LIST_ITEM_OWNER((ListItem_t *)&pthread->list_item, pthread); - listSET_LIST_ITEM_VALUE((ListItem_t *)&pthread->list_item, (TickType_t)xHandle); + task_arg, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT, &xHandle); + if(res != pdPASS) { + ESP_LOGE(TAG, "Failed to create task!"); + free(pthread); + free(task_arg); + if (res == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) { + errno = ENOMEM; + return ENOMEM; + } else { + errno = EAGAIN; + return EAGAIN; + } + } + vListInitialiseItem((ListItem_t *)&pthread->list_item); + listSET_LIST_ITEM_OWNER((ListItem_t *)&pthread->list_item, pthread); + listSET_LIST_ITEM_VALUE((ListItem_t *)&pthread->list_item, (TickType_t)xHandle); if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) { - assert(false && "Failed to lock threads list!"); + assert(false && "Failed to lock threads list!"); } - vListInsertEnd((List_t *)&s_threads_list, (ListItem_t *)&pthread->list_item); + vListInsertEnd((List_t *)&s_threads_list, (ListItem_t *)&pthread->list_item); xSemaphoreGive(s_threads_mux); - // start task - xTaskNotify(xHandle, 0, eNoAction); + // start task + xTaskNotify(xHandle, 0, eNoAction); - *thread = (pthread_t)pthread; // pointer value fit into pthread_t (uint32_t) + *thread = (pthread_t)pthread; // pointer value fit into pthread_t (uint32_t) - ESP_PTHREAD_LOGV(TAG, "Created task %x", (uint32_t)xHandle); + ESP_LOGV(TAG, "Created task %x", (uint32_t)xHandle); - return 0; + return 0; } int pthread_join(pthread_t thread, void **retval) { - esp_pthread_t *pthread = (esp_pthread_t *)thread; - int ret = 0; + esp_pthread_t *pthread = (esp_pthread_t *)thread; + int ret = 0; - ESP_PTHREAD_LOGV(TAG, "%s %p", __FUNCTION__, pthread); + ESP_LOGV(TAG, "%s %p", __FUNCTION__, pthread); - // find task + // find task if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) { - assert(false && "Failed to lock threads list!"); + assert(false && "Failed to lock threads list!"); } TaskHandle_t handle = pthread_find_handle(thread); if (!handle) { - errno = ESRCH; // not found - ret = ESRCH; + errno = ESRCH; // not found + ret = ESRCH; } else if (pthread->join_task) { - errno = EINVAL; // already have waiting task to join - ret = EINVAL; + errno = EINVAL; // already have waiting task to join + ret = EINVAL; } else if (handle == xTaskGetCurrentTaskHandle()) { - errno = EDEADLK; // join to self not allowed - ret = EDEADLK; + errno = EDEADLK; // join to self not allowed + ret = EDEADLK; } else { - esp_pthread_t *cur_pthread = pthread_find(xTaskGetCurrentTaskHandle()); - if (cur_pthread && cur_pthread->join_task == handle) { - errno = EDEADLK; // join to each other not allowed - ret = EDEADLK; - } else { - if (pthread->state == PTHREAD_TASK_STATE_RUN) { - pthread->join_task = xTaskGetCurrentTaskHandle(); - } else { - pthread_delete(pthread); - } - } + esp_pthread_t *cur_pthread = pthread_find(xTaskGetCurrentTaskHandle()); + if (cur_pthread && cur_pthread->join_task == handle) { + errno = EDEADLK; // join to each other not allowed + ret = EDEADLK; + } else { + if (pthread->state == PTHREAD_TASK_STATE_RUN) { + pthread->join_task = xTaskGetCurrentTaskHandle(); + } else { + pthread_delete(pthread); + } + } } xSemaphoreGive(s_threads_mux); if (ret == 0 && pthread->join_task) { - xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); - if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) { - assert(false && "Failed to lock threads list!"); - } - pthread_delete(pthread); + xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); + if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) { + assert(false && "Failed to lock threads list!"); + } + pthread_delete(pthread); xSemaphoreGive(s_threads_mux); } if (retval) { - *retval = 0; // no exit code in FreeRTOS + *retval = 0; // no exit code in FreeRTOS } - ESP_PTHREAD_LOGV(TAG, "%s %p EXIT %d", __FUNCTION__, pthread, ret); - return ret; + ESP_LOGV(TAG, "%s %p EXIT %d", __FUNCTION__, pthread, ret); + return ret; } int pthread_detach(pthread_t thread) { - esp_pthread_t *pthread = (esp_pthread_t *)thread; - int ret = 0; + esp_pthread_t *pthread = (esp_pthread_t *)thread; + int ret = 0; if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) { - assert(false && "Failed to lock threads list!"); + assert(false && "Failed to lock threads list!"); } TaskHandle_t handle = pthread_find_handle(thread); if (!handle) { - errno = ESRCH; // not found - ret = ESRCH; - } else { - pthread->detached = true; - } + errno = ESRCH; // not found + ret = ESRCH; + } else { + pthread->detached = true; + } xSemaphoreGive(s_threads_mux); - ESP_PTHREAD_LOGV(TAG, "%s %p EXIT %d", __FUNCTION__, pthread, ret); - return ret; + ESP_LOGV(TAG, "%s %p EXIT %d", __FUNCTION__, pthread, ret); + return ret; } int pthread_cancel(pthread_t thread) { - assert(false && "pthread_cancel not supported!"); - return -1; + assert(false && "pthread_cancel not supported!"); + return -1; } int sched_yield( void ) { - vTaskDelay(0); - return 0; + vTaskDelay(0); + return 0; } pthread_t pthread_self(void) { if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) { - assert(false && "Failed to lock threads list!"); + assert(false && "Failed to lock threads list!"); + } + esp_pthread_t *pthread = pthread_find(xTaskGetCurrentTaskHandle()); + if (!pthread) { + assert(false && "Failed to find current thread ID!"); } - esp_pthread_t *pthread = pthread_find(xTaskGetCurrentTaskHandle()); - if (!pthread) { - assert(false && "Failed to find current thread ID!"); - } xSemaphoreGive(s_threads_mux); - return (pthread_t)pthread; + return (pthread_t)pthread; } int pthread_equal(pthread_t t1, pthread_t t2) { - return t1 == t2 ? 1 : 0; + return t1 == t2 ? 1 : 0; } /***************** KEY ******************/ int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) { - static int s_created; + static int s_created; - //TODO: Key destructors not suppoted! - if (s_created) { - // key API supports just one key necessary by libstdcxx threading implementation - return ENOMEM; - } - *key = 1; - s_created = 1; - return 0; + //TODO: Key destructors not suppoted! + if (s_created) { + // key API supports just one key necessary by libstdcxx threading implementation + assert(false && "pthread_key_create: multiple keys not supported!"); + } + *key = 1; + s_created = 1; + return 0; } int pthread_key_delete(pthread_key_t key) { - assert(false && "pthread_key_delete not supported!"); - return -1; + assert(false && "pthread_key_delete not supported!"); + return -1; } void *pthread_getspecific(pthread_key_t key) { - assert(false && "pthread_getspecific not supported!"); - return NULL; + assert(false && "pthread_getspecific not supported!"); + return NULL; } int pthread_setspecific(pthread_key_t key, const void *value) { - assert(false && "pthread_setspecific not supported!"); - return -1; + assert(false && "pthread_setspecific not supported!"); + return -1; } /***************** ONCE ******************/ int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) { - if (once_control == NULL || init_routine == NULL || !once_control->is_initialized) { - ESP_PTHREAD_LOGE(TAG, "%s: Invalid args!", __FUNCTION__); - return EINVAL; - } + if (once_control == NULL || init_routine == NULL || !once_control->is_initialized) { + ESP_LOGE(TAG, "%s: Invalid args!", __FUNCTION__); + return EINVAL; + } - TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); - // do not take mutex if OS is not running yet + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + // do not take mutex if OS is not running yet if (!cur_task || xSemaphoreTake(s_once_mux, portMAX_DELAY) == pdTRUE) { - if (!once_control->init_executed) { - ESP_PTHREAD_LOGV(TAG, "%s: call init_routine %p", __FUNCTION__, once_control); - init_routine(); - once_control->init_executed = 1; - } - if (cur_task) { - xSemaphoreGive(s_once_mux); - } + if (!once_control->init_executed) { + ESP_LOGV(TAG, "%s: call init_routine %p", __FUNCTION__, once_control); + init_routine(); + once_control->init_executed = 1; + } + if (cur_task) { + xSemaphoreGive(s_once_mux); + } } else { - ESP_PTHREAD_LOGE(TAG, "%s: Failed to lock!", __FUNCTION__); - return EBUSY; + ESP_LOGE(TAG, "%s: Failed to lock!", __FUNCTION__); + return EBUSY; } - return 0; + return 0; } /***************** MUTEX ******************/ static int mutexattr_check(const pthread_mutexattr_t *attr) { - if (attr->type < PTHREAD_MUTEX_NORMAL || attr->type > PTHREAD_MUTEX_RECURSIVE) { - return EINVAL; - } - return 0; + if (attr->type < PTHREAD_MUTEX_NORMAL || attr->type > PTHREAD_MUTEX_RECURSIVE) { + return EINVAL; + } + return 0; } int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) { - int type = PTHREAD_MUTEX_NORMAL; + int type = PTHREAD_MUTEX_NORMAL; - if (!mutex) { - errno = EINVAL; - return EINVAL; - } + if (!mutex) { + errno = EINVAL; + return EINVAL; + } - if (attr) { - if (!attr->is_initialized) { - errno = EINVAL; - return EINVAL; - } - int res = mutexattr_check(attr); - if (res) { - errno = res; - return res; - } - type = attr->type; - } + if (attr) { + if (!attr->is_initialized) { + errno = EINVAL; + return EINVAL; + } + int res = mutexattr_check(attr); + if (res) { + errno = res; + return res; + } + type = attr->type; + } - esp_pthread_mutex_t *mux = (esp_pthread_mutex_t *)malloc(sizeof(esp_pthread_mutex_t)); + esp_pthread_mutex_t *mux = (esp_pthread_mutex_t *)malloc(sizeof(esp_pthread_mutex_t)); if (!mux) { errno = ENOMEM; return ENOMEM; - } - mux->type = type; + } + mux->type = type; - if (mux->type == PTHREAD_MUTEX_RECURSIVE) { + if (mux->type == PTHREAD_MUTEX_RECURSIVE) { mux->sem = xSemaphoreCreateRecursiveMutex(); } else { mux->sem = xSemaphoreCreateMutex(); @@ -410,24 +423,24 @@ int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) free(mux); errno = EAGAIN; return EAGAIN; - } + } - *mutex = (pthread_mutex_t)mux; // pointer value fit into pthread_mutex_t (uint32_t) + *mutex = (pthread_mutex_t)mux; // pointer value fit into pthread_mutex_t (uint32_t) - return 0; + return 0; } int pthread_mutex_destroy(pthread_mutex_t *mutex) { - esp_pthread_mutex_t *mux; + esp_pthread_mutex_t *mux; - ESP_PTHREAD_LOGV(TAG, "%s %p", __FUNCTION__, mutex); + ESP_LOGV(TAG, "%s %p", __FUNCTION__, mutex); - if (!mutex) { - errno = EINVAL; - return EINVAL; - } - mux = (esp_pthread_mutex_t *)*mutex; + if (!mutex) { + errno = EINVAL; + return EINVAL; + } + mux = (esp_pthread_mutex_t *)*mutex; // check if mux is busy int res = pthread_mutex_lock_internal(mux, 0); @@ -436,10 +449,10 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex) return EBUSY; } - vSemaphoreDelete(mux->sem); - free(mux); + vSemaphoreDelete(mux->sem); + free(mux); - return 0; + return 0; } static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickType_t tmo) @@ -461,88 +474,79 @@ static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickT int IRAM_ATTR pthread_mutex_lock(pthread_mutex_t *mutex) { - if (!mutex) { - errno = EINVAL; - return EINVAL; - } + if (!mutex) { + errno = EINVAL; + return EINVAL; + } return pthread_mutex_lock_internal((esp_pthread_mutex_t *)*mutex, portMAX_DELAY); } int IRAM_ATTR pthread_mutex_trylock(pthread_mutex_t *mutex) { - if (!mutex) { - errno = EINVAL; - return EINVAL; - } + if (!mutex) { + errno = EINVAL; + return EINVAL; + } return pthread_mutex_lock_internal((esp_pthread_mutex_t *)*mutex, 0); } int IRAM_ATTR pthread_mutex_unlock(pthread_mutex_t *mutex) { - esp_pthread_mutex_t *mux; + esp_pthread_mutex_t *mux; - if (!mutex) { - errno = EINVAL; - return EINVAL; - } - mux = (esp_pthread_mutex_t *)*mutex; + if (!mutex) { + errno = EINVAL; + return EINVAL; + } + mux = (esp_pthread_mutex_t *)*mutex; - if (mux->type == PTHREAD_MUTEX_RECURSIVE) { + if (mux->type == PTHREAD_MUTEX_RECURSIVE) { xSemaphoreGiveRecursive(mux->sem); } else { xSemaphoreGive(mux->sem); - } - return 0; + } + return 0; } int pthread_mutexattr_init(pthread_mutexattr_t *attr) { - if (!attr) { - errno = EINVAL; - return EINVAL; - } - attr->type = PTHREAD_MUTEX_NORMAL; - attr->is_initialized = 1; - return 0; + if (!attr) { + errno = EINVAL; + return EINVAL; + } + attr->type = PTHREAD_MUTEX_NORMAL; + attr->is_initialized = 1; + return 0; } int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) { - if (!attr) { - errno = EINVAL; - return EINVAL; - } - attr->is_initialized = 0; - return 0; + if (!attr) { + errno = EINVAL; + return EINVAL; + } + attr->is_initialized = 0; + return 0; } int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) { - assert(false && "pthread_mutexattr_gettype not supported!"); - return -1; + assert(false && "pthread_mutexattr_gettype not supported!"); + return -1; } int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) { - if (!attr) { - errno = EINVAL; - return EINVAL; - } - pthread_mutexattr_t tmp_attr = {.type = type}; - int res = mutexattr_check(&tmp_attr); - if (res) { - errno = res; - } else { - attr->type = type; - } - return res; -} - -/***************** AUX ******************/ -// TODO: move to newlib/time.c???? -// needed for std::this_thread::sleep_for -unsigned int sleep(unsigned int seconds) -{ - usleep(seconds*1000000UL); - return 0; + if (!attr) { + errno = EINVAL; + return EINVAL; + } + pthread_mutexattr_t tmp_attr = {.type = type}; + int res = mutexattr_check(&tmp_attr); + if (res) { + errno = res; + } else { + attr->type = type; + } + return res; } diff --git a/components/pthread/test/test_pthread_cxx.cpp b/components/pthread/test/test_pthread_cxx.cpp index 2cdaf18cf9..287a1da1b1 100644 --- a/components/pthread/test/test_pthread_cxx.cpp +++ b/components/pthread/test/test_pthread_cxx.cpp @@ -3,9 +3,11 @@ #include #include "unity.h" -std::shared_ptr global_sp; -std::mutex mtx; -std::recursive_mutex recur_mtx; +#if __GTHREADS && __GTHREADS_CXX0X + +static std::shared_ptr global_sp; +static std::mutex mtx; +static std::recursive_mutex recur_mtx; static void thread_do_nothing() {} @@ -17,20 +19,20 @@ static void thread_main() while (i < 3) { int old_val, new_val; - // mux test - mtx.lock(); + // mux test + mtx.lock(); old_val = *global_sp; - std::this_thread::yield(); - (*global_sp)++; + std::this_thread::yield(); + (*global_sp)++; std::this_thread::yield(); new_val = *global_sp; - mtx.unlock(); + mtx.unlock(); std::cout << "thread " << std::hex << std::this_thread::get_id() << ": " << i++ << " val= " << *global_sp << std::endl; TEST_ASSERT_TRUE(new_val == old_val + 1); // sleep_for test std::chrono::milliseconds dur(300); - std::this_thread::sleep_for(dur); + std::this_thread::sleep_for(dur); // recursive mux test recur_mtx.lock(); @@ -46,11 +48,11 @@ static void thread_main() TEST_ASSERT_TRUE(new_val == old_val + 1); // sleep_until test - using std::chrono::system_clock; - std::time_t tt = system_clock::to_time_t(system_clock::now()); - struct std::tm *ptm = std::localtime(&tt); - ptm->tm_sec++; - std::this_thread::sleep_until(system_clock::from_time_t (mktime(ptm))); + using std::chrono::system_clock; + std::time_t tt = system_clock::to_time_t(system_clock::now()); + struct std::tm *ptm = std::localtime(&tt); + ptm->tm_sec++; + std::this_thread::sleep_until(system_clock::from_time_t (mktime(ptm))); } } @@ -69,11 +71,13 @@ TEST_CASE("pthread CXX", "[pthread]") std::thread t3(thread_main); std::thread t4(thread_main); if (t3.joinable()) { - std::cout << "Join thread " << std::hex << t3.get_id() << std::endl; - t3.join(); + std::cout << "Join thread " << std::hex << t3.get_id() << std::endl; + t3.join(); } if (t4.joinable()) { - std::cout << "Join thread " << std::hex << t4.get_id() << std::endl; - t4.join(); + std::cout << "Join thread " << std::hex << t4.get_id() << std::endl; + t4.join(); } } + +#endif From e3b86e7bdbc231638242e9c01486de7f948ce1c7 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 1 Sep 2017 12:31:04 +0800 Subject: [PATCH 6/8] pthread: handle statically initialized mutexes --- components/pthread/pthread.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/components/pthread/pthread.c b/components/pthread/pthread.c index f2409671ad..2cf6694258 100644 --- a/components/pthread/pthread.c +++ b/components/pthread/pthread.c @@ -42,6 +42,7 @@ typedef struct { static SemaphoreHandle_t s_once_mux = NULL; static SemaphoreHandle_t s_threads_mux = NULL; +static portMUX_TYPE s_mutex_init_lock = portMUX_INITIALIZER_UNLOCKED; static List_t s_threads_list; @@ -356,7 +357,8 @@ int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); // do not take mutex if OS is not running yet - if (!cur_task || xSemaphoreTake(s_once_mux, portMAX_DELAY) == pdTRUE) + if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED || + !cur_task || xSemaphoreTake(s_once_mux, portMAX_DELAY) == pdTRUE) { if (!once_control->init_executed) { ESP_LOGV(TAG, "%s: call init_routine %p", __FUNCTION__, once_control); @@ -472,12 +474,28 @@ static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickT return 0; } +static int pthread_mutex_init_if_static(pthread_mutex_t *mutex) { + int res = 0; + if ((intptr_t) *mutex == PTHREAD_MUTEX_INITIALIZER) { + portENTER_CRITICAL(&s_mutex_init_lock); + if ((intptr_t) *mutex == PTHREAD_MUTEX_INITIALIZER) { + res = pthread_mutex_init(mutex, NULL); + } + portEXIT_CRITICAL(&s_mutex_init_lock); + } + return res; +} + int IRAM_ATTR pthread_mutex_lock(pthread_mutex_t *mutex) { if (!mutex) { errno = EINVAL; return EINVAL; } + int res = pthread_mutex_init_if_static(mutex); + if (res != 0) { + return res; + } return pthread_mutex_lock_internal((esp_pthread_mutex_t *)*mutex, portMAX_DELAY); } @@ -487,6 +505,10 @@ int IRAM_ATTR pthread_mutex_trylock(pthread_mutex_t *mutex) errno = EINVAL; return EINVAL; } + int res = pthread_mutex_init_if_static(mutex); + if (res != 0) { + return res; + } return pthread_mutex_lock_internal((esp_pthread_mutex_t *)*mutex, 0); } From 5094965e98c505db1e8142e9286d1991498f76a1 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Mon, 4 Sep 2017 21:46:16 +0300 Subject: [PATCH 7/8] According to comments --- components/esp32/cpu_start.c | 2 +- components/pthread/pthread.c | 80 ++++++++++++++++++------------------ 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 0c560cce63..3f4333f337 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -83,7 +83,7 @@ static bool app_cpu_started = false; static void do_global_ctors(void); static void main_task(void* args); extern void app_main(void); -extern int esp_pthread_init(void); +extern esp_err_t esp_pthread_init(void); extern int _bss_start; extern int _bss_end; diff --git a/components/pthread/pthread.c b/components/pthread/pthread.c index 2cf6694258..25dae54d79 100644 --- a/components/pthread/pthread.c +++ b/components/pthread/pthread.c @@ -1,3 +1,23 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This module implements pthread API on top of FreeRTOS. API is implemented to the level allowing +// libstdcxx threading framework to operate correctly. So not all original pthread routines are supported. +// Moreover some implemened functions do not provide full functionality, e.g. pthread_create does not support +// thread's attributes customization (prio, stack size and so on). So if you are not satisfied with default +// behavior use native FreeRTOS API. +// #include #include #include @@ -36,7 +56,7 @@ typedef struct { typedef struct { ListItem_t list_item; ///< mutexes list node struct SemaphoreHandle_t sem; ///< Handle of the task waiting to join - int type; ///< Handle of the task waiting to join + int type; ///< Mutex type. Currently supported PTHREAD_MUTEX_NORMAL and PTHREAD_MUTEX_RECURSIVE } esp_pthread_mutex_t; @@ -49,7 +69,7 @@ static List_t s_threads_list; static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickType_t tmo); -int esp_pthread_init(void) +esp_err_t __attribute__((constructor)) esp_pthread_init(void) { vListInitialise((List_t *)&s_threads_list); s_once_mux = xSemaphoreCreateMutex(); @@ -158,12 +178,12 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, ESP_LOGV(TAG, "%s", __FUNCTION__); if (attr) { - assert(false && "pthread_create: attrs not supported!"); + ESP_LOGE(TAG, "%s: attrs not supported!", __FUNCTION__); + return ENOSYS; } esp_pthread_task_arg_t *task_arg = malloc(sizeof(esp_pthread_task_arg_t)); if (task_arg == NULL) { ESP_LOGE(TAG, "Failed to allocate task args!"); - errno = ENOMEM; return ENOMEM; } memset(task_arg, 0, sizeof(esp_pthread_task_arg_t)); @@ -171,7 +191,6 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, if (pthread == NULL) { ESP_LOGE(TAG, "Failed to allocate pthread data!"); free(task_arg); - errno = ENOMEM; return ENOMEM; } memset(pthread, 0, sizeof(esp_pthread_t)); @@ -184,10 +203,8 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, free(pthread); free(task_arg); if (res == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) { - errno = ENOMEM; return ENOMEM; } else { - errno = EAGAIN; return EAGAIN; } } @@ -224,18 +241,18 @@ int pthread_join(pthread_t thread, void **retval) } TaskHandle_t handle = pthread_find_handle(thread); if (!handle) { - errno = ESRCH; // not found + // not found ret = ESRCH; } else if (pthread->join_task) { - errno = EINVAL; // already have waiting task to join + // already have waiting task to join ret = EINVAL; } else if (handle == xTaskGetCurrentTaskHandle()) { - errno = EDEADLK; // join to self not allowed + // join to self not allowed ret = EDEADLK; } else { esp_pthread_t *cur_pthread = pthread_find(xTaskGetCurrentTaskHandle()); if (cur_pthread && cur_pthread->join_task == handle) { - errno = EDEADLK; // join to each other not allowed + // join to each other not allowed ret = EDEADLK; } else { if (pthread->state == PTHREAD_TASK_STATE_RUN) { @@ -274,7 +291,6 @@ int pthread_detach(pthread_t thread) } TaskHandle_t handle = pthread_find_handle(thread); if (!handle) { - errno = ESRCH; // not found ret = ESRCH; } else { pthread->detached = true; @@ -286,8 +302,8 @@ int pthread_detach(pthread_t thread) int pthread_cancel(pthread_t thread) { - assert(false && "pthread_cancel not supported!"); - return -1; + ESP_LOGE(TAG, "%s: not supported!", __FUNCTION__); + return ENOSYS; } int sched_yield( void ) @@ -322,7 +338,8 @@ int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) //TODO: Key destructors not suppoted! if (s_created) { // key API supports just one key necessary by libstdcxx threading implementation - assert(false && "pthread_key_create: multiple keys not supported!"); + ESP_LOGE(TAG, "%s: multiple keys not supported!", __FUNCTION__); + return ENOSYS; } *key = 1; s_created = 1; @@ -331,20 +348,20 @@ int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) int pthread_key_delete(pthread_key_t key) { - assert(false && "pthread_key_delete not supported!"); - return -1; + ESP_LOGE(TAG, "%s: not supported!", __FUNCTION__); + return ENOSYS; } void *pthread_getspecific(pthread_key_t key) { - assert(false && "pthread_getspecific not supported!"); + ESP_LOGE(TAG, "%s: not supported!", __FUNCTION__); return NULL; } int pthread_setspecific(pthread_key_t key, const void *value) { - assert(false && "pthread_setspecific not supported!"); - return -1; + ESP_LOGE(TAG, "%s: not supported!", __FUNCTION__); + return ENOSYS; } /***************** ONCE ******************/ @@ -392,18 +409,15 @@ int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) int type = PTHREAD_MUTEX_NORMAL; if (!mutex) { - errno = EINVAL; return EINVAL; } if (attr) { if (!attr->is_initialized) { - errno = EINVAL; return EINVAL; } int res = mutexattr_check(attr); if (res) { - errno = res; return res; } type = attr->type; @@ -411,7 +425,6 @@ int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) esp_pthread_mutex_t *mux = (esp_pthread_mutex_t *)malloc(sizeof(esp_pthread_mutex_t)); if (!mux) { - errno = ENOMEM; return ENOMEM; } mux->type = type; @@ -423,7 +436,6 @@ int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) } if (!mux->sem) { free(mux); - errno = EAGAIN; return EAGAIN; } @@ -439,7 +451,6 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex) ESP_LOGV(TAG, "%s %p", __FUNCTION__, mutex); if (!mutex) { - errno = EINVAL; return EINVAL; } mux = (esp_pthread_mutex_t *)*mutex; @@ -447,7 +458,6 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex) // check if mux is busy int res = pthread_mutex_lock_internal(mux, 0); if (res == EBUSY) { - errno = EBUSY; return EBUSY; } @@ -461,12 +471,10 @@ static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickT { if (mux->type == PTHREAD_MUTEX_RECURSIVE) { if (xSemaphoreTakeRecursive(mux->sem, tmo) != pdTRUE) { - errno = EBUSY; return EBUSY; } } else { if (xSemaphoreTake(mux->sem, tmo) != pdTRUE) { - errno = EBUSY; return EBUSY; } } @@ -489,7 +497,6 @@ static int pthread_mutex_init_if_static(pthread_mutex_t *mutex) { int IRAM_ATTR pthread_mutex_lock(pthread_mutex_t *mutex) { if (!mutex) { - errno = EINVAL; return EINVAL; } int res = pthread_mutex_init_if_static(mutex); @@ -502,7 +509,6 @@ int IRAM_ATTR pthread_mutex_lock(pthread_mutex_t *mutex) int IRAM_ATTR pthread_mutex_trylock(pthread_mutex_t *mutex) { if (!mutex) { - errno = EINVAL; return EINVAL; } int res = pthread_mutex_init_if_static(mutex); @@ -517,7 +523,6 @@ int IRAM_ATTR pthread_mutex_unlock(pthread_mutex_t *mutex) esp_pthread_mutex_t *mux; if (!mutex) { - errno = EINVAL; return EINVAL; } mux = (esp_pthread_mutex_t *)*mutex; @@ -533,7 +538,6 @@ int IRAM_ATTR pthread_mutex_unlock(pthread_mutex_t *mutex) int pthread_mutexattr_init(pthread_mutexattr_t *attr) { if (!attr) { - errno = EINVAL; return EINVAL; } attr->type = PTHREAD_MUTEX_NORMAL; @@ -544,7 +548,6 @@ int pthread_mutexattr_init(pthread_mutexattr_t *attr) int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) { if (!attr) { - errno = EINVAL; return EINVAL; } attr->is_initialized = 0; @@ -553,21 +556,18 @@ int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) { - assert(false && "pthread_mutexattr_gettype not supported!"); - return -1; + ESP_LOGE(TAG, "%s: not supported!", __FUNCTION__); + return ENOSYS; } int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) { if (!attr) { - errno = EINVAL; return EINVAL; } pthread_mutexattr_t tmp_attr = {.type = type}; int res = mutexattr_check(&tmp_attr); - if (res) { - errno = res; - } else { + if (!res) { attr->type = type; } return res; From 5dea0fca15d86a7e0217a2b7f3b38f8ccbfc7887 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Tue, 5 Sep 2017 20:37:31 +0300 Subject: [PATCH 8/8] esp_pthread_init constructor attr was removed --- components/pthread/pthread.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/pthread/pthread.c b/components/pthread/pthread.c index 25dae54d79..73f23a00d7 100644 --- a/components/pthread/pthread.c +++ b/components/pthread/pthread.c @@ -69,7 +69,7 @@ static List_t s_threads_list; static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickType_t tmo); -esp_err_t __attribute__((constructor)) esp_pthread_init(void) +esp_err_t esp_pthread_init(void) { vListInitialise((List_t *)&s_threads_list); s_once_mux = xSemaphoreCreateMutex();