mirror of
https://github.com/espressif/ESP8266_RTOS_SDK.git
synced 2025-05-29 14:06:08 +08:00
Merge branch 'feature/update_pthread_from_esp_idf' into 'master'
pthread: update pthread from esp-idf See merge request sdk/ESP8266_RTOS_SDK!1072
This commit is contained in:
@ -103,6 +103,8 @@ to exclude the API function. */
|
||||
#define INCLUDE_xTaskGetCurrentTaskHandle 1
|
||||
#define INCLUDE_uxTaskGetStackHighWaterMark 1
|
||||
|
||||
#define INCLUDE_xSemaphoreGetMutexHolder 1
|
||||
|
||||
/* This is the raw value as per the Cortex-M3 NVIC. Values can be 255
|
||||
(lowest) to 0 (1?) (highest). */
|
||||
#define configKERNEL_INTERRUPT_PRIORITY 255
|
||||
|
40
components/newlib/newlib/port/include/pthread.h
Normal file
40
components/newlib/newlib/port/include/pthread.h
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2018 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.
|
||||
#ifndef __ESP_PLATFORM_PTHREAD_H__
|
||||
#define __ESP_PLATFORM_PTHREAD_H__
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/features.h>
|
||||
|
||||
// Remove this when GCC 5.2.0 is no longer supported
|
||||
#ifndef _POSIX_TIMEOUTS
|
||||
#define _POSIX_TIMEOUTS // For pthread_mutex_timedlock
|
||||
#endif
|
||||
|
||||
#include_next <pthread.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int pthread_condattr_getclock(const pthread_condattr_t * attr, clockid_t * clock_id);
|
||||
|
||||
int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __ESP_PLATFORM_PTHREAD_H__
|
35
components/newlib/newlib/port/include/time.h
Normal file
35
components/newlib/newlib/port/include/time.h
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
|
||||
#ifndef _ESP_TIME_H
|
||||
#define _ESP_TIME_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include_next <time.h>
|
||||
|
||||
#define _POSIX_TIMERS 1
|
||||
#define CLOCK_MONOTONIC (clockid_t)4
|
||||
#define CLOCK_BOOTTIME (clockid_t)4
|
||||
|
||||
int clock_settime(clockid_t clock_id, const struct timespec *tp);
|
||||
int clock_gettime(clockid_t clock_id, struct timespec *tp);
|
||||
int clock_getres(clockid_t clock_id, struct timespec *res);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _ESP_TIME_H */
|
@ -16,6 +16,7 @@
|
||||
#include <reent.h>
|
||||
#include <sys/times.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
#include "esp_system.h"
|
||||
#include "esp_timer.h"
|
||||
@ -106,3 +107,38 @@ unsigned int sleep(unsigned int seconds)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clock_gettime (clockid_t clock_id, struct timespec *tp)
|
||||
{
|
||||
#if defined( WITH_FRC ) || defined( WITH_RTC )
|
||||
if (tp == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
struct timeval tv;
|
||||
uint64_t monotonic_time_us = 0;
|
||||
switch (clock_id) {
|
||||
case CLOCK_REALTIME:
|
||||
_gettimeofday_r(NULL, &tv, NULL);
|
||||
tp->tv_sec = tv.tv_sec;
|
||||
tp->tv_nsec = tv.tv_usec * 1000L;
|
||||
break;
|
||||
case CLOCK_MONOTONIC:
|
||||
#if defined( WITH_FRC )
|
||||
monotonic_time_us = (uint64_t) esp_timer_get_time();
|
||||
#elif defined( WITH_RTC )
|
||||
monotonic_time_us = get_rtc_time_us();
|
||||
#endif // WITH_FRC
|
||||
tp->tv_sec = monotonic_time_us / 1000000LL;
|
||||
tp->tv_nsec = (monotonic_time_us % 1000000LL) * 1000L;
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
@ -1,11 +1,23 @@
|
||||
if(CONFIG_ENABLE_PTHREAD)
|
||||
set(COMPONENT_SRCDIRS "src")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS "include")
|
||||
set(COMPONENT_REQUIRES)
|
||||
idf_component_register(SRCS "pthread.c"
|
||||
"pthread_cond_var.c"
|
||||
"pthread_local_storage.c"
|
||||
INCLUDE_DIRS include
|
||||
PRIV_REQUIRES newlib)
|
||||
|
||||
if(GCC_NOT_5_2_0)
|
||||
set(extra_link_flags "-u pthread_include_pthread_impl")
|
||||
list(APPEND extra_link_flags "-u pthread_include_pthread_cond_impl")
|
||||
list(APPEND extra_link_flags "-u pthread_include_pthread_local_storage_impl")
|
||||
endif()
|
||||
|
||||
if(CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP)
|
||||
target_link_libraries(${COMPONENT_LIB} "-Wl,--wrap=vPortCleanUpTCB")
|
||||
endif()
|
||||
|
||||
if(extra_link_flags)
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "${extra_link_flags}")
|
||||
endif()
|
||||
else()
|
||||
register_component()
|
||||
|
||||
if(CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK)
|
||||
target_link_libraries(pthread "-Wl,--wrap=vPortCleanUpTCB")
|
||||
endif()
|
||||
endif()
|
@ -1,22 +1,37 @@
|
||||
menu "PThreads"
|
||||
|
||||
config ENABLE_PTHREAD
|
||||
bool "Enable pthread"
|
||||
default n
|
||||
help
|
||||
Enable this option and then pthread is to be used.
|
||||
config ENABLE_PTHREAD
|
||||
bool "Enable Pthread"
|
||||
help
|
||||
Enable this option and then pthread is to be used.
|
||||
|
||||
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 PTHREAD_TASK_PRIO_DEFAULT
|
||||
int "Default task priority"
|
||||
range 0 255
|
||||
default 5
|
||||
depends on ENABLE_PTHREAD
|
||||
help
|
||||
Priority used to create new tasks with default pthread parameters.
|
||||
|
||||
config ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT
|
||||
int "Default task stack size"
|
||||
default 3072
|
||||
help
|
||||
Stack size used to create new tasks with default pthread parameters.
|
||||
config PTHREAD_TASK_STACK_SIZE_DEFAULT
|
||||
int "Default task stack size"
|
||||
default 3072
|
||||
depends on ENABLE_PTHREAD
|
||||
help
|
||||
Stack size used to create new tasks with default pthread parameters.
|
||||
|
||||
config PTHREAD_STACK_MIN
|
||||
int "Minimum allowed pthread stack size"
|
||||
default 768
|
||||
depends on ENABLE_PTHREAD
|
||||
help
|
||||
Minimum allowed pthread stack size set in attributes passed to pthread_create
|
||||
|
||||
config PTHREAD_TASK_NAME_DEFAULT
|
||||
string "Default name of pthreads"
|
||||
default "pthread"
|
||||
depends on ENABLE_PTHREAD
|
||||
help
|
||||
The default name of pthreads.
|
||||
|
||||
endmenu
|
||||
|
@ -3,13 +3,23 @@
|
||||
#
|
||||
|
||||
ifdef CONFIG_ENABLE_PTHREAD
|
||||
COMPONENT_SRCDIRS := src
|
||||
endif
|
||||
COMPONENT_SRCDIRS := .
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
|
||||
COMPONENT_ADD_LDFLAGS := -lpthread
|
||||
|
||||
ifdef CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK
|
||||
ifdef CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP
|
||||
COMPONENT_ADD_LDFLAGS += -Wl,--wrap=vPortCleanUpTCB
|
||||
endif
|
||||
|
||||
ifeq ($(GCC_NOT_5_2_0), 1)
|
||||
# Forces the linker to include pthread implementation from this component,
|
||||
# instead of the weak implementations provided by libgcc.
|
||||
COMPONENT_ADD_LDFLAGS += -u pthread_include_pthread_impl
|
||||
COMPONENT_ADD_LDFLAGS += -u pthread_include_pthread_cond_impl
|
||||
COMPONENT_ADD_LDFLAGS += -u pthread_include_pthread_local_storage_impl
|
||||
endif # GCC_NOT_5_2_0
|
||||
else
|
||||
COMPONENT_SRCDIRS :=
|
||||
endif
|
||||
|
@ -14,19 +14,35 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include <freertos/FreeRTOSConfig.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#ifndef PTHREAD_STACK_MIN
|
||||
#define PTHREAD_STACK_MIN CONFIG_PTHREAD_STACK_MIN
|
||||
#endif
|
||||
|
||||
/** pthread configuration structure that influences pthread creation */
|
||||
typedef struct {
|
||||
size_t stack_size; ///< the stack size of the pthread
|
||||
size_t prio; ///< the thread's priority
|
||||
bool inherit_cfg; ///< inherit this configuration further
|
||||
size_t stack_size; ///< The stack size of the pthread
|
||||
size_t prio; ///< The thread's priority
|
||||
bool inherit_cfg; ///< Inherit this configuration further
|
||||
const char* thread_name; ///< The thread name.
|
||||
int pin_to_core; ///< The core id to pin the thread to. Has the same value range as xCoreId argument of xTaskCreatePinnedToCore.
|
||||
} esp_pthread_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Creates a default pthread configuration based
|
||||
* on the values set via menuconfig.
|
||||
*
|
||||
* @return
|
||||
* A default configuration structure.
|
||||
*/
|
||||
esp_pthread_cfg_t esp_pthread_get_default_config(void);
|
||||
|
||||
/**
|
||||
* @brief Configure parameters for creating pthread
|
||||
*
|
||||
@ -39,11 +55,15 @@ typedef struct {
|
||||
* then the same configuration is also inherited in the thread
|
||||
* subtree.
|
||||
*
|
||||
* @note Passing non-NULL attributes to pthread_create() will override
|
||||
* the stack_size parameter set using this API
|
||||
*
|
||||
* @param cfg The pthread config parameters
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK if configuration was successfully set
|
||||
* - ESP_ERR_NO_MEM if out of memory
|
||||
* - ESP_ERR_INVALID_ARG if stack_size is less than PTHREAD_STACK_MIN
|
||||
*/
|
||||
esp_err_t esp_pthread_set_cfg(const esp_pthread_cfg_t *cfg);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
|
||||
// Copyright 2018 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.
|
||||
@ -14,18 +14,15 @@
|
||||
//
|
||||
// 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 <time.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_attr.h"
|
||||
#include "sys/queue.h"
|
||||
#include "sys/types.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
@ -33,16 +30,6 @@
|
||||
#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";
|
||||
@ -60,6 +47,8 @@ typedef struct esp_pthread_entry {
|
||||
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
|
||||
void *retval; ///< Value supplied to calling thread during join
|
||||
void *task_arg; ///< Task arguments
|
||||
} esp_pthread_t;
|
||||
|
||||
/** pthread wrapper task arg */
|
||||
@ -77,9 +66,6 @@ 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;
|
||||
@ -87,14 +73,6 @@ static pthread_key_t s_pthread_cfg_key;
|
||||
|
||||
static int 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);
|
||||
@ -107,7 +85,7 @@ esp_err_t esp_pthread_init(void)
|
||||
}
|
||||
s_threads_mux = xSemaphoreCreateMutex();
|
||||
if (s_threads_mux == NULL) {
|
||||
pthread_key_delete(s_pthread_cfg_key);
|
||||
pthread_key_delete(s_pthread_cfg_key);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
@ -157,17 +135,20 @@ static void pthread_delete(esp_pthread_t *pthread)
|
||||
free(pthread);
|
||||
}
|
||||
|
||||
|
||||
/* Call this function to configure pthread stacks in Pthreads */
|
||||
esp_err_t esp_pthread_set_cfg(const esp_pthread_cfg_t *cfg)
|
||||
{
|
||||
if (cfg->stack_size < PTHREAD_STACK_MIN) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* If a value is already set, update that value */
|
||||
esp_pthread_cfg_t *p = pthread_getspecific(s_pthread_cfg_key);
|
||||
if (!p) {
|
||||
p = malloc(sizeof(esp_pthread_cfg_t));
|
||||
if (!p) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
p = malloc(sizeof(esp_pthread_cfg_t));
|
||||
if (!p) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
*p = *cfg;
|
||||
pthread_setspecific(s_pthread_cfg_key, p);
|
||||
@ -178,57 +159,55 @@ esp_err_t esp_pthread_get_cfg(esp_pthread_cfg_t *p)
|
||||
{
|
||||
esp_pthread_cfg_t *cfg = pthread_getspecific(s_pthread_cfg_key);
|
||||
if (cfg) {
|
||||
*p = *cfg;
|
||||
return ESP_OK;
|
||||
*p = *cfg;
|
||||
return ESP_OK;
|
||||
}
|
||||
memset(p, 0, sizeof(*p));
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
static int get_default_pthread_core(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
esp_pthread_cfg_t esp_pthread_get_default_config(void)
|
||||
{
|
||||
esp_pthread_cfg_t cfg = {
|
||||
.stack_size = CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT,
|
||||
.prio = CONFIG_PTHREAD_TASK_PRIO_DEFAULT,
|
||||
.inherit_cfg = false,
|
||||
.thread_name = NULL,
|
||||
.pin_to_core = get_default_pthread_core()
|
||||
};
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
static void pthread_task_func(void *arg)
|
||||
{
|
||||
void *rval = NULL;
|
||||
esp_pthread_task_arg_t *task_arg = (esp_pthread_task_arg_t *)arg;
|
||||
|
||||
ESP_LOGV(TAG, "%s ENTER %p", __FUNCTION__, task_arg->func);
|
||||
|
||||
// wait for start
|
||||
xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
|
||||
|
||||
if (task_arg->cfg.inherit_cfg) {
|
||||
/* If inherit option is set, then do a set_cfg() ourselves for future forks */
|
||||
esp_pthread_set_cfg(&task_arg->cfg);
|
||||
/* If inherit option is set, then do a set_cfg() ourselves for future forks,
|
||||
but first set thread_name to NULL to enable inheritance of the name too.
|
||||
(This also to prevents dangling pointers to name of tasks that might
|
||||
possibly have been deleted when we use the configuration).*/
|
||||
esp_pthread_cfg_t *cfg = &task_arg->cfg;
|
||||
cfg->thread_name = NULL;
|
||||
esp_pthread_set_cfg(cfg);
|
||||
}
|
||||
ESP_LOGV(TAG, "%s START %p", __FUNCTION__, task_arg->func);
|
||||
task_arg->func(task_arg->arg);
|
||||
rval = task_arg->func(task_arg->arg);
|
||||
ESP_LOGV(TAG, "%s END %p", __FUNCTION__, task_arg->func);
|
||||
free(task_arg);
|
||||
|
||||
/* preemptively clean up thread local storage, rather than
|
||||
waiting for the idle task to clean up the thread */
|
||||
pthread_internal_local_storage_destructor_callback();
|
||||
|
||||
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 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);
|
||||
|
||||
ESP_LOGD(TAG, "Task stk_wm = %lu", uxTaskGetStackHighWaterMark(NULL));
|
||||
vTaskDelete(NULL);
|
||||
pthread_exit(rval);
|
||||
|
||||
ESP_LOGV(TAG, "%s EXIT", __FUNCTION__);
|
||||
}
|
||||
@ -239,40 +218,79 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||
TaskHandle_t xHandle = NULL;
|
||||
|
||||
ESP_LOGV(TAG, "%s", __FUNCTION__);
|
||||
if (attr) {
|
||||
ESP_LOGE(TAG, "%s: attrs not supported!", __FUNCTION__);
|
||||
return ENOSYS;
|
||||
}
|
||||
esp_pthread_task_arg_t *task_arg = malloc(sizeof(esp_pthread_task_arg_t));
|
||||
esp_pthread_task_arg_t *task_arg = calloc(1, sizeof(esp_pthread_task_arg_t));
|
||||
if (task_arg == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate task args!");
|
||||
return ENOMEM;
|
||||
}
|
||||
memset(task_arg, 0, sizeof(esp_pthread_task_arg_t));
|
||||
esp_pthread_t *pthread = malloc(sizeof(esp_pthread_t));
|
||||
|
||||
esp_pthread_t *pthread = calloc(1, sizeof(esp_pthread_t));
|
||||
if (pthread == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate pthread data!");
|
||||
free(task_arg);
|
||||
return ENOMEM;
|
||||
}
|
||||
uint32_t stack_size = CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT;
|
||||
BaseType_t prio = CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT;
|
||||
|
||||
uint32_t stack_size = CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT;
|
||||
BaseType_t prio = CONFIG_PTHREAD_TASK_PRIO_DEFAULT;
|
||||
const char *task_name = CONFIG_PTHREAD_TASK_NAME_DEFAULT;
|
||||
|
||||
esp_pthread_cfg_t *pthread_cfg = pthread_getspecific(s_pthread_cfg_key);
|
||||
if (pthread_cfg) {
|
||||
if (pthread_cfg->stack_size) {
|
||||
stack_size = pthread_cfg->stack_size;
|
||||
}
|
||||
if (pthread_cfg->prio && pthread_cfg->prio < configMAX_PRIORITIES) {
|
||||
prio = pthread_cfg->prio;
|
||||
}
|
||||
task_arg->cfg = *pthread_cfg;
|
||||
if (pthread_cfg->stack_size) {
|
||||
stack_size = pthread_cfg->stack_size;
|
||||
}
|
||||
if (pthread_cfg->prio && pthread_cfg->prio < configMAX_PRIORITIES) {
|
||||
prio = pthread_cfg->prio;
|
||||
}
|
||||
|
||||
if (pthread_cfg->inherit_cfg) {
|
||||
if (pthread_cfg->thread_name == NULL) {
|
||||
// Inherit task name from current task.
|
||||
task_name = pcTaskGetTaskName(NULL);
|
||||
} else {
|
||||
// Inheriting, but new task name.
|
||||
task_name = pthread_cfg->thread_name;
|
||||
}
|
||||
} else if (pthread_cfg->thread_name == NULL) {
|
||||
task_name = CONFIG_PTHREAD_TASK_NAME_DEFAULT;
|
||||
} else {
|
||||
task_name = pthread_cfg->thread_name;
|
||||
}
|
||||
|
||||
task_arg->cfg = *pthread_cfg;
|
||||
}
|
||||
memset(pthread, 0, sizeof(esp_pthread_t));
|
||||
|
||||
if (attr) {
|
||||
/* Overwrite attributes */
|
||||
stack_size = attr->stacksize;
|
||||
|
||||
switch (attr->detachstate) {
|
||||
case PTHREAD_CREATE_DETACHED:
|
||||
pthread->detached = true;
|
||||
break;
|
||||
case PTHREAD_CREATE_JOINABLE:
|
||||
default:
|
||||
pthread->detached = false;
|
||||
}
|
||||
}
|
||||
|
||||
task_arg->func = start_routine;
|
||||
task_arg->arg = arg;
|
||||
BaseType_t res = xTaskCreate(&pthread_task_func, "pthread", stack_size,
|
||||
task_arg, prio, &xHandle);
|
||||
if(res != pdPASS) {
|
||||
pthread->task_arg = task_arg;
|
||||
BaseType_t res = xTaskCreatePinnedToCore(&pthread_task_func,
|
||||
task_name,
|
||||
// stack_size is in bytes. This transformation ensures that the units are
|
||||
// transformed to the units used in FreeRTOS.
|
||||
// Note: float division of ceil(m / n) ==
|
||||
// integer division of (m + n - 1) / n
|
||||
(stack_size + sizeof(StackType_t) - 1) / sizeof(StackType_t),
|
||||
task_arg,
|
||||
prio,
|
||||
&xHandle,
|
||||
0);
|
||||
|
||||
if (res != pdPASS) {
|
||||
ESP_LOGE(TAG, "Failed to create task!");
|
||||
free(pthread);
|
||||
free(task_arg);
|
||||
@ -305,6 +323,7 @@ int pthread_join(pthread_t thread, void **retval)
|
||||
esp_pthread_t *pthread = (esp_pthread_t *)thread;
|
||||
int ret = 0;
|
||||
bool wait = false;
|
||||
void *child_task_retval = 0;
|
||||
|
||||
ESP_LOGV(TAG, "%s %p", __FUNCTION__, pthread);
|
||||
|
||||
@ -316,6 +335,9 @@ int pthread_join(pthread_t thread, void **retval)
|
||||
if (!handle) {
|
||||
// not found
|
||||
ret = ESRCH;
|
||||
} else if (pthread->detached) {
|
||||
// Thread is detached
|
||||
ret = EDEADLK;
|
||||
} else if (pthread->join_task) {
|
||||
// already have waiting task to join
|
||||
ret = EINVAL;
|
||||
@ -332,23 +354,28 @@ int pthread_join(pthread_t thread, void **retval)
|
||||
pthread->join_task = xTaskGetCurrentTaskHandle();
|
||||
wait = true;
|
||||
} else {
|
||||
child_task_retval = pthread->retval;
|
||||
pthread_delete(pthread);
|
||||
}
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(s_threads_mux);
|
||||
|
||||
if (ret == 0 && wait) {
|
||||
xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
|
||||
if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) {
|
||||
assert(false && "Failed to lock threads list!");
|
||||
if (ret == 0) {
|
||||
if (wait) {
|
||||
xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
|
||||
if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) {
|
||||
assert(false && "Failed to lock threads list!");
|
||||
}
|
||||
child_task_retval = pthread->retval;
|
||||
pthread_delete(pthread);
|
||||
xSemaphoreGive(s_threads_mux);
|
||||
}
|
||||
pthread_delete(pthread);
|
||||
xSemaphoreGive(s_threads_mux);
|
||||
vTaskDelete(handle);
|
||||
}
|
||||
|
||||
if (retval) {
|
||||
*retval = 0; // no exit code in FreeRTOS
|
||||
*retval = child_task_retval;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "%s %p EXIT %d", __FUNCTION__, pthread, ret);
|
||||
@ -366,14 +393,71 @@ int pthread_detach(pthread_t thread)
|
||||
TaskHandle_t handle = pthread_find_handle(thread);
|
||||
if (!handle) {
|
||||
ret = ESRCH;
|
||||
} else {
|
||||
} else if (pthread->detached) {
|
||||
// already detached
|
||||
ret = EINVAL;
|
||||
} else if (pthread->join_task) {
|
||||
// already have waiting task to join
|
||||
ret = EINVAL;
|
||||
} else if (pthread->state == PTHREAD_TASK_STATE_RUN) {
|
||||
// pthread still running
|
||||
pthread->detached = true;
|
||||
} else {
|
||||
// pthread already stopped
|
||||
pthread_delete(pthread);
|
||||
vTaskDelete(handle);
|
||||
}
|
||||
xSemaphoreGive(s_threads_mux);
|
||||
ESP_LOGV(TAG, "%s %p EXIT %d", __FUNCTION__, pthread, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void pthread_exit(void *value_ptr)
|
||||
{
|
||||
bool detached = false;
|
||||
/* preemptively clean up thread local storage, rather than
|
||||
waiting for the idle task to clean up the thread */
|
||||
pthread_internal_local_storage_destructor_callback();
|
||||
|
||||
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 pthread for current task!");
|
||||
}
|
||||
if (pthread->task_arg) {
|
||||
free(pthread->task_arg);
|
||||
}
|
||||
if (pthread->detached) {
|
||||
// auto-free for detached threads
|
||||
pthread_delete(pthread);
|
||||
detached = true;
|
||||
} else {
|
||||
// Set return value
|
||||
pthread->retval = value_ptr;
|
||||
// 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);
|
||||
|
||||
ESP_LOGD(TAG, "Task stk_wm = %ld", uxTaskGetStackHighWaterMark(NULL));
|
||||
|
||||
if (detached) {
|
||||
vTaskDelete(NULL);
|
||||
} else {
|
||||
vTaskSuspend(NULL);
|
||||
}
|
||||
|
||||
// Should never be reached
|
||||
abort();
|
||||
}
|
||||
|
||||
int pthread_cancel(pthread_t thread)
|
||||
{
|
||||
ESP_LOGE(TAG, "%s: not supported!", __FUNCTION__);
|
||||
@ -412,21 +496,7 @@ int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
uint32_t res = 1;
|
||||
#if defined(CONFIG_SPIRAM_SUPPORT)
|
||||
if (esp_ptr_external_ram(once_control)) {
|
||||
uxPortCompareSetExtram((uint32_t *) &once_control->init_executed, 0, &res);
|
||||
} else {
|
||||
#endif
|
||||
uxPortCompareSet((uint32_t *) &once_control->init_executed, 0, &res);
|
||||
#if defined(CONFIG_SPIRAM_SUPPORT)
|
||||
}
|
||||
#endif
|
||||
// Check if compare and set was successful
|
||||
if (res == 0) {
|
||||
ESP_LOGV(TAG, "%s: call init_routine %p", __FUNCTION__, once_control);
|
||||
init_routine();
|
||||
}
|
||||
once_control->init_executed = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -434,7 +504,9 @@ 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) {
|
||||
if (attr->type != PTHREAD_MUTEX_NORMAL &&
|
||||
attr->type != PTHREAD_MUTEX_RECURSIVE &&
|
||||
attr->type != PTHREAD_MUTEX_ERRORCHECK) {
|
||||
return EINVAL;
|
||||
}
|
||||
return 0;
|
||||
@ -490,6 +562,9 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex)
|
||||
return EINVAL;
|
||||
}
|
||||
mux = (esp_pthread_mutex_t *)*mutex;
|
||||
if (!mux) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
// check if mux is busy
|
||||
int res = pthread_mutex_lock_internal(mux, 0);
|
||||
@ -505,6 +580,15 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex)
|
||||
|
||||
static int pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickType_t tmo)
|
||||
{
|
||||
if (!mux) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((mux->type == PTHREAD_MUTEX_ERRORCHECK) &&
|
||||
(xSemaphoreGetMutexHolder(mux->sem) == xTaskGetCurrentTaskHandle())) {
|
||||
return EDEADLK;
|
||||
}
|
||||
|
||||
if (mux->type == PTHREAD_MUTEX_RECURSIVE) {
|
||||
if (xSemaphoreTakeRecursive(mux->sem, tmo) != pdTRUE) {
|
||||
return EBUSY;
|
||||
@ -518,14 +602,15 @@ static int pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickType_t tmo)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pthread_mutex_init_if_static(pthread_mutex_t *mutex) {
|
||||
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);
|
||||
portENTER_CRITICAL();
|
||||
if ((intptr_t) *mutex == PTHREAD_MUTEX_INITIALIZER) {
|
||||
res = pthread_mutex_init(mutex, NULL);
|
||||
}
|
||||
portEXIT_CRITICAL(&s_mutex_init_lock);
|
||||
portEXIT_CRITICAL();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -542,6 +627,28 @@ int pthread_mutex_lock(pthread_mutex_t *mutex)
|
||||
return pthread_mutex_lock_internal((esp_pthread_mutex_t *)*mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *timeout)
|
||||
{
|
||||
if (!mutex) {
|
||||
return EINVAL;
|
||||
}
|
||||
int res = pthread_mutex_init_if_static(mutex);
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
struct timespec currtime;
|
||||
clock_gettime(CLOCK_REALTIME, &currtime);
|
||||
TickType_t tmo = ((timeout->tv_sec - currtime.tv_sec)*1000 +
|
||||
(timeout->tv_nsec - currtime.tv_nsec)/1000000)/portTICK_PERIOD_MS;
|
||||
|
||||
res = pthread_mutex_lock_internal((esp_pthread_mutex_t *)*mutex, tmo);
|
||||
if (res == EBUSY) {
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int pthread_mutex_trylock(pthread_mutex_t *mutex)
|
||||
{
|
||||
if (!mutex) {
|
||||
@ -562,11 +669,24 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex)
|
||||
return EINVAL;
|
||||
}
|
||||
mux = (esp_pthread_mutex_t *)*mutex;
|
||||
if (!mux) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (((mux->type == PTHREAD_MUTEX_RECURSIVE) ||
|
||||
(mux->type == PTHREAD_MUTEX_ERRORCHECK)) &&
|
||||
(xSemaphoreGetMutexHolder(mux->sem) != xTaskGetCurrentTaskHandle())) {
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
int ret;
|
||||
if (mux->type == PTHREAD_MUTEX_RECURSIVE) {
|
||||
xSemaphoreGiveRecursive(mux->sem);
|
||||
ret = xSemaphoreGiveRecursive(mux->sem);
|
||||
} else {
|
||||
xSemaphoreGive(mux->sem);
|
||||
ret = xSemaphoreGive(mux->sem);
|
||||
}
|
||||
if (ret != pdTRUE) {
|
||||
assert(false && "Failed to unlock mutex!");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -592,8 +712,11 @@ int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
|
||||
|
||||
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type)
|
||||
{
|
||||
ESP_LOGE(TAG, "%s: not supported!", __FUNCTION__);
|
||||
return ENOSYS;
|
||||
if (!attr) {
|
||||
return EINVAL;
|
||||
}
|
||||
*type = attr->type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
|
||||
@ -609,4 +732,75 @@ int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif
|
||||
/***************** ATTRIBUTES ******************/
|
||||
int pthread_attr_init(pthread_attr_t *attr)
|
||||
{
|
||||
if (attr) {
|
||||
/* Nothing to allocate. Set everything to default */
|
||||
attr->stacksize = CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT;
|
||||
attr->detachstate = PTHREAD_CREATE_JOINABLE;
|
||||
return 0;
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int pthread_attr_destroy(pthread_attr_t *attr)
|
||||
{
|
||||
if (attr) {
|
||||
/* Nothing to deallocate. Reset everything to default */
|
||||
attr->stacksize = CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT;
|
||||
attr->detachstate = PTHREAD_CREATE_JOINABLE;
|
||||
return 0;
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize)
|
||||
{
|
||||
if (attr) {
|
||||
*stacksize = attr->stacksize;
|
||||
return 0;
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
|
||||
{
|
||||
if (attr && !(stacksize < PTHREAD_STACK_MIN)) {
|
||||
attr->stacksize = stacksize;
|
||||
return 0;
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate)
|
||||
{
|
||||
if (attr) {
|
||||
*detachstate = attr->detachstate;
|
||||
return 0;
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
|
||||
{
|
||||
if (attr) {
|
||||
switch (detachstate) {
|
||||
case PTHREAD_CREATE_DETACHED:
|
||||
attr->detachstate = PTHREAD_CREATE_DETACHED;
|
||||
break;
|
||||
case PTHREAD_CREATE_JOINABLE:
|
||||
attr->detachstate = PTHREAD_CREATE_JOINABLE;
|
||||
break;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Hook function to force linking this file */
|
||||
void pthread_include_pthread_impl(void)
|
||||
{
|
||||
}
|
@ -20,19 +20,15 @@
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_attr.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/private/list.h"
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifdef CONFIG_ENABLE_PTHREAD
|
||||
|
||||
#define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL
|
||||
#include "esp_log.h"
|
||||
const static char *TAG = "esp_pthread";
|
||||
@ -202,4 +198,7 @@ int pthread_cond_destroy(pthread_cond_t *cv)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
/* Hook function to force linking this file */
|
||||
void pthread_include_pthread_cond_var_impl(void)
|
||||
{
|
||||
}
|
@ -13,4 +13,4 @@
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
|
||||
void pthread_internal_local_storage_destructor_callback();
|
||||
void pthread_internal_local_storage_destructor_callback(void);
|
@ -14,7 +14,6 @@
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@ -24,18 +23,8 @@
|
||||
|
||||
#include "pthread_internal.h"
|
||||
|
||||
#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*);
|
||||
|
||||
/* This is a very naive implementation of key-indexed thread local storage, using two linked lists
|
||||
@ -53,10 +42,6 @@ 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_ {
|
||||
pthread_key_t key;
|
||||
@ -75,7 +60,7 @@ int pthread_key_create(pthread_key_t *key, pthread_destructor_t destructor)
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
portENTER_CRITICAL(&s_keys_lock);
|
||||
portENTER_CRITICAL();
|
||||
|
||||
const key_entry_t *head = SLIST_FIRST(&s_keys);
|
||||
new_key->key = (head == NULL) ? 1 : (head->key + 1);
|
||||
@ -84,27 +69,27 @@ int pthread_key_create(pthread_key_t *key, pthread_destructor_t destructor)
|
||||
|
||||
SLIST_INSERT_HEAD(&s_keys, new_key, next);
|
||||
|
||||
portEXIT_CRITICAL(&s_keys_lock);
|
||||
portEXIT_CRITICAL();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static key_entry_t *find_key(pthread_key_t key)
|
||||
{
|
||||
portENTER_CRITICAL(&s_keys_lock);
|
||||
portENTER_CRITICAL();
|
||||
key_entry_t *result = NULL;;
|
||||
SLIST_FOREACH(result, &s_keys, next) {
|
||||
if(result->key == key) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&s_keys_lock);
|
||||
portEXIT_CRITICAL();
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_key_delete(pthread_key_t key)
|
||||
{
|
||||
|
||||
portENTER_CRITICAL(&s_keys_lock);
|
||||
portENTER_CRITICAL();
|
||||
|
||||
/* Ideally, we would also walk all tasks' thread local storage value_list here
|
||||
and delete any values associated with this key. We do not do this...
|
||||
@ -116,7 +101,7 @@ int pthread_key_delete(pthread_key_t key)
|
||||
free(entry);
|
||||
}
|
||||
|
||||
portEXIT_CRITICAL(&s_keys_lock);
|
||||
portEXIT_CRITICAL();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -155,7 +140,7 @@ static void pthread_local_storage_thread_deleted_callback(int index, void *v_tls
|
||||
free(tls);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK)
|
||||
#if defined(CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP)
|
||||
/* Called from FreeRTOS task delete hook */
|
||||
void pthread_local_storage_cleanup(TaskHandle_t task)
|
||||
{
|
||||
@ -179,7 +164,7 @@ void __wrap_vPortCleanUpTCB(void *tcb)
|
||||
#endif
|
||||
|
||||
/* this function called from pthread_task_func for "early" cleanup of TLS in a pthread */
|
||||
void pthread_internal_local_storage_destructor_callback()
|
||||
void pthread_internal_local_storage_destructor_callback(void)
|
||||
{
|
||||
void *tls = pvTaskGetThreadLocalStoragePointer(NULL, PTHREAD_TLS_INDEX);
|
||||
if (tls != NULL) {
|
||||
@ -187,7 +172,7 @@ void pthread_internal_local_storage_destructor_callback()
|
||||
/* remove the thread-local-storage pointer to avoid the idle task cleanup
|
||||
calling it again...
|
||||
*/
|
||||
#if defined(CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK)
|
||||
#if defined(CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP)
|
||||
vTaskSetThreadLocalStoragePointer(NULL, PTHREAD_TLS_INDEX, NULL);
|
||||
#else
|
||||
vTaskSetThreadLocalStoragePointerAndDelCallback(NULL,
|
||||
@ -236,7 +221,7 @@ int pthread_setspecific(pthread_key_t key, const void *value)
|
||||
if (tls == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
#if defined(CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK)
|
||||
#if defined(CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP)
|
||||
vTaskSetThreadLocalStoragePointer(NULL, PTHREAD_TLS_INDEX, tls);
|
||||
#else
|
||||
vTaskSetThreadLocalStoragePointerAndDelCallback(NULL,
|
||||
@ -269,4 +254,7 @@ int pthread_setspecific(pthread_key_t key, const void *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
/* Hook function to force linking this file */
|
||||
void pthread_include_pthread_local_storage_impl(void)
|
||||
{
|
||||
}
|
11
components/pthread/sdkconfig.rename
Normal file
11
components/pthread/sdkconfig.rename
Normal file
@ -0,0 +1,11 @@
|
||||
# sdkconfig replacement configurations for deprecated options formatted as
|
||||
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
|
||||
|
||||
CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT CONFIG_PTHREAD_TASK_PRIO_DEFAULT
|
||||
CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT
|
||||
CONFIG_ESP32_PTHREAD_STACK_MIN CONFIG_PTHREAD_STACK_MIN
|
||||
CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT CONFIG_PTHREAD_TASK_CORE_DEFAULT
|
||||
CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY
|
||||
CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 CONFIG_PTHREAD_DEFAULT_CORE_0
|
||||
CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 CONFIG_PTHREAD_DEFAULT_CORE_1
|
||||
CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT CONFIG_PTHREAD_TASK_NAME_DEFAULT
|
3
components/pthread/test/CMakeLists.txt
Normal file
3
components/pthread/test/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES unity test_utils pthread)
|
48
components/pthread/test/test_cxx_cond_var.cpp
Normal file
48
components/pthread/test/test_cxx_cond_var.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include "unity.h"
|
||||
|
||||
#if __GTHREADS && __GTHREADS_CXX0X
|
||||
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
std::atomic<int> i{0};
|
||||
|
||||
static void waits(int idx, int timeout_ms)
|
||||
{
|
||||
std::unique_lock<std::mutex> 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
|
31
components/pthread/test/test_cxx_std_future.cpp
Normal file
31
components/pthread/test/test_cxx_std_future.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include <iostream>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include "unity.h"
|
||||
|
||||
#if __GTHREADS && __GTHREADS_CXX0X
|
||||
TEST_CASE("C++ future", "[std::future]")
|
||||
{
|
||||
// future from a packaged_task
|
||||
std::packaged_task<int()> task([]{ return 7; }); // wrap the function
|
||||
std::future<int> f1 = task.get_future(); // get a future
|
||||
std::thread t(std::move(task)); // launch on a thread
|
||||
|
||||
// future from an async()
|
||||
std::future<int> f2 = std::async(std::launch::async, []{ return 8; });
|
||||
|
||||
// future from a promise
|
||||
std::promise<int> p;
|
||||
std::future<int> 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
|
||||
|
294
components/pthread/test/test_pthread.c
Normal file
294
components/pthread/test/test_pthread.c
Normal file
@ -0,0 +1,294 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "esp_pthread.h"
|
||||
#include <pthread.h>
|
||||
|
||||
#include "unity.h"
|
||||
|
||||
static void *compute_square(void *arg)
|
||||
{
|
||||
int *num = (int *) arg;
|
||||
*num = (*num) * (*num);
|
||||
pthread_exit((void *) num);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TEST_CASE("pthread create join", "[pthread]")
|
||||
{
|
||||
int res = 0;
|
||||
volatile int num = 7;
|
||||
volatile bool attr_init = false;
|
||||
void *thread_rval = NULL;
|
||||
pthread_t new_thread = (pthread_t)NULL;
|
||||
pthread_attr_t attr;
|
||||
|
||||
if (TEST_PROTECT()) {
|
||||
res = pthread_attr_init(&attr);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
attr_init = true;
|
||||
|
||||
res = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
res = pthread_create(&new_thread, &attr, compute_square, (void *) &num);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
res = pthread_join(new_thread, &thread_rval);
|
||||
TEST_ASSERT_EQUAL_INT(EDEADLK, res);
|
||||
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
TEST_ASSERT_EQUAL_INT(49, num);
|
||||
|
||||
res = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
res = pthread_create(&new_thread, &attr, compute_square, (void *) &num);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
res = pthread_join(new_thread, &thread_rval);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(2401, num);
|
||||
TEST_ASSERT_EQUAL_PTR(&num, thread_rval);
|
||||
}
|
||||
|
||||
if (attr_init) {
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
}
|
||||
|
||||
static void *waiting_thread(void *arg)
|
||||
{
|
||||
TaskHandle_t *task_handle = (TaskHandle_t *)arg;
|
||||
TaskHandle_t parent_task = *task_handle;
|
||||
|
||||
*task_handle = xTaskGetCurrentTaskHandle();
|
||||
|
||||
xTaskNotify(parent_task, 0, eNoAction);
|
||||
xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TEST_CASE("pthread detach", "[pthread]")
|
||||
{
|
||||
int res = 0;
|
||||
pthread_t new_thread = (pthread_t)NULL;
|
||||
TaskHandle_t task_handle = NULL;
|
||||
const int task_count = uxTaskGetNumberOfTasks();
|
||||
bool detach_works = false;
|
||||
|
||||
if (TEST_PROTECT()) {
|
||||
task_handle = xTaskGetCurrentTaskHandle();
|
||||
res = pthread_create(&new_thread, NULL, waiting_thread, (void *)&task_handle);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
res = xTaskNotifyWait(0, 0, NULL, 100 / portTICK_PERIOD_MS);
|
||||
TEST_ASSERT_EQUAL_INT(pdTRUE, res);
|
||||
|
||||
xTaskNotify(task_handle, 0, eNoAction);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
|
||||
res = pthread_detach(new_thread);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
res = uxTaskGetNumberOfTasks();
|
||||
TEST_ASSERT_EQUAL_INT(task_count, res);
|
||||
detach_works = true;
|
||||
}
|
||||
|
||||
if (!detach_works) {
|
||||
vTaskDelete(task_handle);
|
||||
} else {
|
||||
detach_works = false;
|
||||
}
|
||||
|
||||
if (TEST_PROTECT()) {
|
||||
task_handle = xTaskGetCurrentTaskHandle();
|
||||
res = pthread_create(&new_thread, NULL, waiting_thread, (void *)&task_handle);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
res = xTaskNotifyWait(0, 0, NULL, 100 / portTICK_PERIOD_MS);
|
||||
TEST_ASSERT_EQUAL_INT(pdTRUE, res);
|
||||
|
||||
res = pthread_detach(new_thread);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
xTaskNotify(task_handle, 0, eNoAction);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
|
||||
res = uxTaskGetNumberOfTasks();
|
||||
TEST_ASSERT_EQUAL_INT(task_count, res);
|
||||
detach_works = true;
|
||||
}
|
||||
|
||||
if (!detach_works) {
|
||||
vTaskDelete(task_handle);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("pthread attr init destroy", "[pthread]")
|
||||
{
|
||||
int res = 0;
|
||||
size_t stack_size_1 = 0, stack_size_2 = 0;
|
||||
volatile bool attr_init = pdFALSE;
|
||||
pthread_attr_t attr;
|
||||
|
||||
if (TEST_PROTECT()) {
|
||||
res = pthread_attr_init(&attr);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
attr_init = true;
|
||||
|
||||
res = pthread_attr_getstacksize(&attr, &stack_size_1);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
res = pthread_attr_setstacksize(&attr, stack_size_1);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
res = pthread_attr_getstacksize(&attr, &stack_size_2);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
TEST_ASSERT_EQUAL_INT(stack_size_2, stack_size_1);
|
||||
|
||||
stack_size_1 = PTHREAD_STACK_MIN - 1;
|
||||
res = pthread_attr_setstacksize(&attr, stack_size_1);
|
||||
TEST_ASSERT_EQUAL_INT(EINVAL, res);
|
||||
}
|
||||
|
||||
if (attr_init) {
|
||||
TEST_ASSERT_EQUAL_INT(0, pthread_attr_destroy(&attr));
|
||||
}
|
||||
}
|
||||
|
||||
static void *unlock_mutex(void *arg)
|
||||
{
|
||||
pthread_mutex_t *mutex = (pthread_mutex_t *) arg;
|
||||
intptr_t res = (intptr_t) pthread_mutex_unlock(mutex);
|
||||
pthread_exit((void *) res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void test_mutex_lock_unlock(int mutex_type)
|
||||
{
|
||||
int res = 0;
|
||||
int set_type = -1;
|
||||
volatile bool attr_created = false;
|
||||
volatile bool mutex_created = false;
|
||||
volatile intptr_t thread_rval = 0;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_t new_thread;
|
||||
|
||||
if (TEST_PROTECT()) {
|
||||
res = pthread_mutexattr_init(&attr);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
attr_created = true;
|
||||
|
||||
res = pthread_mutexattr_settype(&attr, mutex_type);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
res = pthread_mutexattr_gettype(&attr, &set_type);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
TEST_ASSERT_EQUAL_INT(mutex_type, set_type);
|
||||
|
||||
res = pthread_mutex_init(&mutex, &attr);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
mutex_created = true;
|
||||
|
||||
res = pthread_mutex_lock(&mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
res = pthread_mutex_lock(&mutex);
|
||||
|
||||
if(mutex_type == PTHREAD_MUTEX_ERRORCHECK) {
|
||||
TEST_ASSERT_EQUAL_INT(EDEADLK, res);
|
||||
} else {
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
res = pthread_mutex_unlock(&mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
}
|
||||
|
||||
pthread_create(&new_thread, NULL, unlock_mutex, &mutex);
|
||||
|
||||
pthread_join(new_thread, (void **) &thread_rval);
|
||||
TEST_ASSERT_EQUAL_INT(EPERM, (int) thread_rval);
|
||||
|
||||
res = pthread_mutex_unlock(&mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
}
|
||||
|
||||
if (attr_created) {
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
}
|
||||
|
||||
if (mutex_created) {
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("pthread mutex lock unlock", "[pthread]")
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
/* Present behavior of mutex initializer is unlike what is
|
||||
* defined in Posix standard, ie. calling pthread_mutex_lock
|
||||
* on such a mutex would internally cause dynamic allocation.
|
||||
* Therefore pthread_mutex_destroy needs to be called in
|
||||
* order to avoid memory leak. */
|
||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
res = pthread_mutex_lock(&mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
res = pthread_mutex_unlock(&mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
/* This deviates from the Posix standard static mutex behavior.
|
||||
* This needs to be removed in the future when standard mutex
|
||||
* initializer is supported */
|
||||
pthread_mutex_destroy(&mutex);
|
||||
|
||||
test_mutex_lock_unlock(PTHREAD_MUTEX_ERRORCHECK);
|
||||
test_mutex_lock_unlock(PTHREAD_MUTEX_RECURSIVE);
|
||||
}
|
||||
|
||||
static void timespec_add_nano(struct timespec * out, struct timespec * in, long val)
|
||||
{
|
||||
out->tv_nsec = val + in->tv_nsec;
|
||||
if (out->tv_nsec < (in->tv_nsec)) {
|
||||
out->tv_sec += 1;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("pthread mutex trylock timedlock", "[pthread]")
|
||||
{
|
||||
int res = 0;
|
||||
volatile bool mutex_created = false;
|
||||
pthread_mutex_t mutex;
|
||||
struct timespec abs_timeout;
|
||||
|
||||
if (TEST_PROTECT()) {
|
||||
res = pthread_mutex_init(&mutex, NULL);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
mutex_created = true;
|
||||
|
||||
res = pthread_mutex_trylock(&mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
|
||||
res = pthread_mutex_trylock(&mutex);
|
||||
TEST_ASSERT_EQUAL_INT(EBUSY, res);
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &abs_timeout);
|
||||
timespec_add_nano(&abs_timeout, &abs_timeout, 100000000LL);
|
||||
|
||||
res = pthread_mutex_timedlock(&mutex, &abs_timeout);
|
||||
TEST_ASSERT_EQUAL_INT(ETIMEDOUT, res);
|
||||
|
||||
res = pthread_mutex_unlock(&mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
}
|
||||
|
||||
if (mutex_created) {
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
}
|
132
components/pthread/test/test_pthread_cxx.cpp
Normal file
132
components/pthread/test/test_pthread_cxx.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#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<int> 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
|
@ -1,9 +1,9 @@
|
||||
// Test pthread_create_key, pthread_delete_key, pthread_setspecific, pthread_getspecific
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include "unity.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "test_utils.h"
|
||||
|
||||
TEST_CASE("pthread local storage basics", "[pthread]")
|
||||
{
|
||||
|
Reference in New Issue
Block a user