diff --git a/components/esp8266/source/startup.c b/components/esp8266/source/startup.c index 36ceddb6..cf0c7d9d 100644 --- a/components/esp8266/source/startup.c +++ b/components/esp8266/source/startup.c @@ -39,6 +39,7 @@ extern int base_gpio_init(void); extern int watchdog_init(void); extern int wifi_timer_init(void); extern int wifi_nvs_init(void); +extern esp_err_t esp_pthread_init(void); static void user_init_entry(void *param) { @@ -67,6 +68,10 @@ static void user_init_entry(void *param) esp_task_wdt_init(); #endif +#ifdef CONFIG_ENABLE_PTHREAD + assert(esp_pthread_init() == 0); +#endif + app_main(); wifi_task_delete(NULL); diff --git a/components/freertos/port/esp8266/include/freertos/FreeRTOSConfig.h b/components/freertos/port/esp8266/include/freertos/FreeRTOSConfig.h index 2d728385..78d22eeb 100644 --- a/components/freertos/port/esp8266/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/port/esp8266/include/freertos/FreeRTOSConfig.h @@ -152,7 +152,11 @@ NVIC value of 255. */ #define configUSE_NEWLIB_REENTRANT 1 #endif +#ifdef CONFIG_ENABLE_PTHREAD +#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 2 +#else #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 1 +#endif #define configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS 1 /* add this to dump task stack information */ diff --git a/components/newlib/Makefile.projbuild b/components/newlib/Makefile.projbuild index 733676d0..90b99fcb 100644 --- a/components/newlib/Makefile.projbuild +++ b/components/newlib/Makefile.projbuild @@ -1 +1,4 @@ -CFLAGS += -D_CLOCKS_PER_SEC_=CONFIG_FREERTOS_HZ +CFLAGS += -D_CLOCKS_PER_SEC_=CONFIG_FREERTOS_HZ -D_POSIX_THREADS=1 -D_UNIX98_THREAD_MUTEX_ATTRIBUTES=1 +ifdef CONFIG_ENABLE_PTHREAD +CFLAGS += -D_POSIX_THREADS=1 -D_UNIX98_THREAD_MUTEX_AT +endif diff --git a/components/newlib/newlib/include/sys/time.h b/components/newlib/newlib/include/sys/time.h index be16497f..8e3ef808 100644 --- a/components/newlib/newlib/include/sys/time.h +++ b/components/newlib/newlib/include/sys/time.h @@ -20,7 +20,7 @@ struct timeval { }; /* BSD time macros used by RTEMS code */ -#if defined (__rtems__) || defined (__CYGWIN__) +#if defined (__rtems__) || defined (__CYGWIN__) || defined(__XTENSA__) /* Convenience macros for operations on timevals. NOTE: `timercmp' does not work for >= or <=. */ diff --git a/components/newlib/newlib/port/syscall.c b/components/newlib/newlib/port/syscall.c index f289b1df..a7fe205d 100644 --- a/components/newlib/newlib/port/syscall.c +++ b/components/newlib/newlib/port/syscall.c @@ -32,7 +32,7 @@ _ssize_t _read_r(struct _reent *r, int fd, void *buf, size_t len) return 0; } -_ssize_t _write_r(struct _reent *r, int fd, void *buf, size_t len) +_ssize_t _write_r(struct _reent *r, int fd, const void *buf, size_t len) { int i; const char *cbuf = buf; diff --git a/components/pthread/Kconfig b/components/pthread/Kconfig index 3f16c6ff..6c6d8fa1 100644 --- a/components/pthread/Kconfig +++ b/components/pthread/Kconfig @@ -1,5 +1,11 @@ menu "PThreads" +config ENABLE_PTHREAD + bool "Enable pthread" + default n + help + Enable this option and then pthread is to be used. + config ESP32_PTHREAD_TASK_PRIO_DEFAULT int "Default task priority" range 0 255 diff --git a/components/pthread/component.mk b/components/pthread/component.mk index 0dd23948..7528b1da 100644 --- a/components/pthread/component.mk +++ b/components/pthread/component.mk @@ -2,7 +2,9 @@ # Component Makefile # -COMPONENT_SRCDIRS := . +ifdef CONFIG_ENABLE_PTHREAD +COMPONENT_SRCDIRS := src +endif COMPONENT_ADD_INCLUDEDIRS := include diff --git a/components/pthread/pthread.c b/components/pthread/src/pthread.c similarity index 96% rename from components/pthread/pthread.c rename to components/pthread/src/pthread.c index 9d7822b8..c9fec073 100644 --- a/components/pthread/pthread.c +++ b/components/pthread/src/pthread.c @@ -21,9 +21,11 @@ #include #include #include +#include #include "esp_err.h" #include "esp_attr.h" -#include "rom/queue.h" +#include "sys/queue.h" +#include "sys/types.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" @@ -31,6 +33,16 @@ #include "pthread_internal.h" #include "esp_pthread.h" +#ifdef CONFIG_ENABLE_PTHREAD + +#if portNUM_PROCESSORS == 1 +#undef portENTER_CRITICAL +#undef portEXIT_CRITICAL + +#define portENTER_CRITICAL(l) vPortEnterCritical() +#define portEXIT_CRITICAL(l) vPortExitCritical() +#endif + #define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL #include "esp_log.h" const static char *TAG = "pthread"; @@ -65,7 +77,9 @@ typedef struct { static SemaphoreHandle_t s_threads_mux = NULL; +#if portNUM_PROCESSORS > 1 static portMUX_TYPE s_mutex_init_lock = portMUX_INITIALIZER_UNLOCKED; +#endif static SLIST_HEAD(esp_thread_list_head, esp_pthread_entry) s_threads_list = SLIST_HEAD_INITIALIZER(s_threads_list); static pthread_key_t s_pthread_cfg_key; @@ -73,6 +87,14 @@ static pthread_key_t s_pthread_cfg_key; static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickType_t tmo); +static inline void uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set) +{ + portENTER_CRITICAL(&s_mutex_init_lock); + *addr = compare; + *set = 0; + portEXIT_CRITICAL(&s_mutex_init_lock); +} + static void esp_pthread_cfg_key_destructor(void *value) { free(value); @@ -205,7 +227,7 @@ static void pthread_task_func(void *arg) } xSemaphoreGive(s_threads_mux); - ESP_LOGD(TAG, "Task stk_wm = %d", uxTaskGetStackHighWaterMark(NULL)); + ESP_LOGD(TAG, "Task stk_wm = %lu", uxTaskGetStackHighWaterMark(NULL)); vTaskDelete(NULL); ESP_LOGV(TAG, "%s EXIT", __FUNCTION__); @@ -586,3 +608,5 @@ int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) } return res; } + +#endif diff --git a/components/pthread/pthread_cond_var.c b/components/pthread/src/pthread_cond_var.c similarity index 97% rename from components/pthread/pthread_cond_var.c rename to components/pthread/src/pthread_cond_var.c index 39e23821..98f53fda 100644 --- a/components/pthread/pthread_cond_var.c +++ b/components/pthread/src/pthread_cond_var.c @@ -20,16 +20,19 @@ #include #include #include +#include #include "esp_err.h" #include "esp_attr.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" -#include "freertos/list.h" +#include "freertos/private/list.h" -#include +#include #include +#ifdef CONFIG_ENABLE_PTHREAD + #define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL #include "esp_log.h" const static char *TAG = "esp_pthread"; @@ -198,3 +201,5 @@ int pthread_cond_destroy(pthread_cond_t *cv) return ret; } + +#endif diff --git a/components/pthread/pthread_internal.h b/components/pthread/src/pthread_internal.h similarity index 100% rename from components/pthread/pthread_internal.h rename to components/pthread/src/pthread_internal.h diff --git a/components/pthread/pthread_local_storage.c b/components/pthread/src/pthread_local_storage.c similarity index 96% rename from components/pthread/pthread_local_storage.c rename to components/pthread/src/pthread_local_storage.c index f87f698d..dd10d50e 100644 --- a/components/pthread/pthread_local_storage.c +++ b/components/pthread/src/pthread_local_storage.c @@ -14,16 +14,27 @@ #include #include #include +#include #include "esp_err.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "sys/lock.h" -#include "rom/queue.h" +#include "sys/queue.h" #include "pthread_internal.h" -#define PTHREAD_TLS_INDEX 0 +#ifdef CONFIG_ENABLE_PTHREAD + +#define PTHREAD_TLS_INDEX 1 + +#if portNUM_PROCESSORS == 1 +#undef portENTER_CRITICAL +#undef portEXIT_CRITICAL + +#define portENTER_CRITICAL(l) vPortEnterCritical() +#define portEXIT_CRITICAL(l) vPortExitCritical() +#endif typedef void (*pthread_destructor_t)(void*); @@ -42,7 +53,9 @@ typedef struct key_entry_t_ { // List of all keys created with pthread_key_create() SLIST_HEAD(key_list_t, key_entry_t_) s_keys = SLIST_HEAD_INITIALIZER(s_keys); +#if portNUM_PROCESSORS > 1 static portMUX_TYPE s_keys_lock = portMUX_INITIALIZER_UNLOCKED; +#endif // List of all value entries associated with a thread via pthread_setspecific() typedef struct value_entry_t_ { @@ -255,3 +268,5 @@ int pthread_setspecific(pthread_key_t key, const void *value) return 0; } + +#endif diff --git a/components/pthread/test/test_cxx_cond_var.cpp b/components/pthread/test/test_cxx_cond_var.cpp deleted file mode 100644 index ccb411ae..00000000 --- a/components/pthread/test/test_cxx_cond_var.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "unity.h" - -#if __GTHREADS && __GTHREADS_CXX0X - -std::condition_variable cv; -std::mutex cv_m; -std::atomic i{0}; - -static void waits(int idx, int timeout_ms) -{ - std::unique_lock lk(cv_m); - auto now = std::chrono::system_clock::now(); - - if(cv.wait_until(lk, now + std::chrono::milliseconds(timeout_ms), [](){return i == 1;})) - std::cout << "Thread " << idx << " finished waiting. i == " << i << '\n'; - else - std::cout << "Thread " << idx << " timed out. i == " << i << '\n'; -} - -static void signals(int signal_ms) -{ - std::this_thread::sleep_for(std::chrono::milliseconds(signal_ms)); - std::cout << "Notifying...\n"; - cv.notify_all(); - std::this_thread::sleep_for(std::chrono::milliseconds(signal_ms)); - i = 1; - std::cout << "Notifying again...\n"; - cv.notify_all(); -} - -TEST_CASE("C++ condition_variable", "[std::condition_variable]") -{ - i = 0; - std::thread t1(waits, 1, 100), t2(waits, 2, 800), t3(signals, 200); - - t1.join(); - t2.join(); - t3.join(); - - std::cout << "All threads joined\n"; -} -#endif diff --git a/components/pthread/test/test_cxx_std_future.cpp b/components/pthread/test/test_cxx_std_future.cpp deleted file mode 100644 index bbf8d624..00000000 --- a/components/pthread/test/test_cxx_std_future.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include -#include -#include "unity.h" - -#if __GTHREADS && __GTHREADS_CXX0X -TEST_CASE("C++ future", "[std::future]") -{ - // future from a packaged_task - std::packaged_task task([]{ return 7; }); // wrap the function - std::future f1 = task.get_future(); // get a future - std::thread t(std::move(task)); // launch on a thread - - // future from an async() - std::future f2 = std::async(std::launch::async, []{ return 8; }); - - // future from a promise - std::promise p; - std::future f3 = p.get_future(); - std::thread( [&p]{ p.set_value_at_thread_exit(9); }).detach(); - - std::cout << "Waiting..." << std::flush; - f1.wait(); - f2.wait(); - f3.wait(); - std::cout << "Done!\nResults are: " - << f1.get() << ' ' << f2.get() << ' ' << f3.get() << '\n'; - t.join(); -} -#endif - diff --git a/components/pthread/test/test_pthread_cxx.cpp b/components/pthread/test/test_pthread_cxx.cpp deleted file mode 100644 index 074b9ba9..00000000 --- a/components/pthread/test/test_pthread_cxx.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "unity.h" - -#if __GTHREADS && __GTHREADS_CXX0X - -#define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL -#include "esp_log.h" -const static char *TAG = "pthread_test"; - -static std::shared_ptr global_sp; -static std::mutex mtx; -static std::recursive_mutex recur_mtx; - -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; - std::chrono::milliseconds dur = std::chrono::milliseconds(300); - - while (i < 3) { - int old_val, new_val; - - // mux test - mtx.lock(); - 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; - TEST_ASSERT_TRUE(new_val == old_val + 1); - - // sleep_for test - std::this_thread::sleep_for(dur); - - // 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()); - struct std::tm *ptm = std::localtime(&tt); - ptm->tm_sec++; - std::this_thread::sleep_until(system_clock::from_time_t(mktime(ptm))); - } -} - -TEST_CASE("pthread C++", "[pthread]") -{ - global_sp.reset(new int(1)); - - std::thread t1(thread_do_nothing); - t1.join(); - - std::thread t2(thread_main); - 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 (t4.joinable()) { - std::cout << "Join thread " << std::hex << t4.get_id() << std::endl; - t4.join(); - } - - global_sp.reset(); // avoid reported leak -} - -static void task_test_sandbox() -{ - std::stringstream ss; - - ESP_LOGI(TAG, "About to create a string stream"); - ESP_LOGI(TAG, "About to write to string stream"); - ss << "Hello World!"; - ESP_LOGI(TAG, "About to extract from stringstream"); - ESP_LOGI(TAG, "Text: %s", ss.str().c_str()); -} - -static void task_test_sandbox_c(void *arg) -{ - bool *running = (bool *)arg; - - // wrap thread func to ensure that all C++ stack objects are cleaned up by their destructors - task_test_sandbox(); - - ESP_LOGI(TAG, "Task stk_wm = %d", uxTaskGetStackHighWaterMark(NULL)); - if (running) { - *running = false; - vTaskDelete(NULL); - } -} - -TEST_CASE("pthread mix C/C++", "[pthread]") -{ - bool c_running = true; - - std::thread t1(task_test_sandbox); - xTaskCreatePinnedToCore((TaskFunction_t)&task_test_sandbox_c, "task_test_sandbox", 3072, &c_running, 5, NULL, 0); - while (c_running) { - vTaskDelay(1); - } - if (t1.joinable()) { - std::cout << "Join thread " << std::hex << t1.get_id() << std::endl; - t1.join(); - } -} - -#endif diff --git a/components/pthread/test/test_pthread_local_storage.c b/components/pthread/test/test_pthread_local_storage.c index 8be22ce5..0b7fe96a 100644 --- a/components/pthread/test/test_pthread_local_storage.c +++ b/components/pthread/test/test_pthread_local_storage.c @@ -1,5 +1,6 @@ // Test pthread_create_key, pthread_delete_key, pthread_setspecific, pthread_getspecific #include +#include #include "unity.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h"