Merge branch 'feature/pthread_name_affinity_config' into 'master'

pthread: make affinity and task name configurable

See merge request idf/esp-idf!3878
This commit is contained in:
Ivan Grokhotkov 2018-12-05 13:46:05 +08:00
commit 37975c186d
10 changed files with 242 additions and 11 deletions

View File

@ -19,4 +19,31 @@ config PTHREAD_STACK_MIN
help
Minimum allowed pthread stack size set in attributes passed to pthread_create
choice ESP32_PTHREAD_TASK_CORE_DEFAULT
bool "Default pthread core affinity"
default ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY
depends on !FREERTOS_UNICORE
help
The default core to which pthreads are pinned.
config ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY
bool "No affinity"
config ESP32_DEFAULT_PTHREAD_CORE_0
bool "Core 0"
config ESP32_DEFAULT_PTHREAD_CORE_1
bool "Core 1"
endchoice
config ESP32_PTHREAD_TASK_CORE_DEFAULT
int
default -1 if ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY || FREERTOS_UNICORE
default 0 if ESP32_DEFAULT_PTHREAD_CORE_0
default 1 if ESP32_DEFAULT_PTHREAD_CORE_1
config ESP32_PTHREAD_TASK_NAME_DEFAULT
string "Default name of pthreads"
default "pthread"
help
The default name of pthreads.
endmenu

View File

@ -14,6 +14,9 @@
#pragma once
#include "esp_err.h"
#include <freertos/FreeRTOSConfig.h>
#ifdef __cplusplus
extern "C" {
#endif
@ -24,11 +27,22 @@ extern "C" {
/** 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();
/**
* @brief Configure parameters for creating pthread
*

View File

@ -136,7 +136,6 @@ 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)
{
@ -168,6 +167,24 @@ esp_err_t esp_pthread_get_cfg(esp_pthread_cfg_t *p)
return ESP_ERR_NOT_FOUND;
}
static int get_default_pthread_core()
{
return CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT == -1 ? tskNO_AFFINITY : CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT;
}
esp_pthread_cfg_t esp_pthread_get_default_config()
{
esp_pthread_cfg_t cfg = {
.stack_size = CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT,
.prio = CONFIG_ESP32_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;
@ -179,8 +196,13 @@ static void pthread_task_func(void *arg)
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);
rval = task_arg->func(task_arg->arg);
@ -212,6 +234,8 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
uint32_t stack_size = CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT;
BaseType_t prio = CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT;
BaseType_t core_id = get_default_pthread_core();
const char *task_name = CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT;
esp_pthread_cfg_t *pthread_cfg = pthread_getspecific(s_pthread_cfg_key);
if (pthread_cfg) {
@ -221,6 +245,25 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
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_ESP32_PTHREAD_TASK_NAME_DEFAULT;
} else {
task_name = pthread_cfg->thread_name;
}
if (pthread_cfg->pin_to_core >= 0 && pthread_cfg->pin_to_core < portNUM_PROCESSORS) {
core_id = pthread_cfg->pin_to_core;
}
task_arg->cfg = *pthread_cfg;
}
@ -241,9 +284,15 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
task_arg->func = start_routine;
task_arg->arg = arg;
pthread->task_arg = task_arg;
BaseType_t res = xTaskCreate(&pthread_task_func, "pthread", stack_size,
task_arg, prio, &xHandle);
if(res != pdPASS) {
BaseType_t res = xTaskCreatePinnedToCore(&pthread_task_func,
task_name,
stack_size,
task_arg,
prio,
&xHandle,
core_id);
if (res != pdPASS) {
ESP_LOGE(TAG, "Failed to create task!");
free(pthread);
free(task_arg);

View File

@ -8,6 +8,8 @@ This module offers Espressif specific extensions to the pthread library that can
* Stack size of the pthreads
* Priority of the created pthreads
* Inheriting this configuration across threads
* Thread name
* Core affinity / core pinning.
Example to tune the stack size of the pthread:
@ -19,7 +21,7 @@ Example to tune the stack size of the pthread:
{
pthread_t t1;
esp_pthread_cfg_t cfg;
esp_pthread_cfg_t cfg = esp_create_default_pthread_config();
cfg.stack_size = (4 * 1024);
esp_pthread_set_cfg(&cfg);
@ -50,7 +52,7 @@ The API can also be used for inheriting the settings across threads. For example
pthread_t t1;
esp_pthread_cfg_t cfg;
esp_pthread_cfg_t cfg = esp_create_default_pthread_config();
cfg.stack_size = (4 * 1024);
cfg.inherit_cfg = true;
esp_pthread_set_cfg(&cfg);

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(cpp_pthread)

View File

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := cpp_pthread
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,5 @@
# pthread examples
This example shows how to use the pthread API to create std::threads with different stack sizes, names, priorities and pinned to certain cores.
This example is in C++, contrary to the the normal standard of pure C.

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "cpp_pthread.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@ -0,0 +1,112 @@
/* pthread/std::thread example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <iostream>
#include <thread>
#include <chrono>
#include <memory>
#include <string>
#include <sstream>
#include <esp_pthread.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>
using namespace std::chrono;
const auto sleep_time = seconds
{
5
};
void print_thread_info(const char *extra = nullptr)
{
std::stringstream ss;
if (extra) {
ss << extra;
}
ss << "Core id: " << xPortGetCoreID()
<< ", prio: " << uxTaskPriorityGet(nullptr)
<< ", minimum free stack: " << uxTaskGetStackHighWaterMark(nullptr) << " bytes.";
ESP_LOGI(pcTaskGetTaskName(nullptr), "%s", ss.str().c_str());
}
void thread_func_inherited()
{
while (true) {
print_thread_info("This is the INHERITING thread with the same parameters as our parent, including name. ");
std::this_thread::sleep_for(sleep_time);
}
}
void spawn_another_thread()
{
// Create a new thread, it will inherit our configuration
std::thread inherits(thread_func_inherited);
while (true) {
print_thread_info();
std::this_thread::sleep_for(sleep_time);
}
}
void thread_func_any_core()
{
while (true) {
print_thread_info("This thread (with the default name) may run on any core.");
std::this_thread::sleep_for(sleep_time);
}
}
void thread_func()
{
while (true) {
print_thread_info();
std::this_thread::sleep_for(sleep_time);
}
}
esp_pthread_cfg_t create_config(const char *name, int core_id, int stack, int prio)
{
auto cfg = esp_pthread_get_default_config();
cfg.thread_name = name;
cfg.pin_to_core = core_id;
cfg.stack_size = stack;
cfg.prio = prio;
return cfg;
}
extern "C" void app_main()
{
// Create a thread using deafult values that can run on any core
auto cfg = esp_pthread_get_default_config();
esp_pthread_set_cfg(&cfg);
std::thread any_core(thread_func_any_core);
// Create a thread on core 0 that spawns another thread, they will both have the same name etc.
cfg = create_config("Thread 1", 0, 3 * 1024, 5);
cfg.inherit_cfg = true;
esp_pthread_set_cfg(&cfg);
std::thread thread_1(spawn_another_thread);
// Create a thread on core 1.
cfg = create_config("Thread 2", 1, 3 * 1024, 5);
esp_pthread_set_cfg(&cfg);
std::thread thread_2(thread_func);
// Let the main task do something too
while (true) {
std::stringstream ss;
ss << "core id: " << xPortGetCoreID()
<< ", prio: " << uxTaskPriorityGet(nullptr)
<< ", minimum free stack: " << uxTaskGetStackHighWaterMark(nullptr) << " bytes.";
ESP_LOGI(pcTaskGetTaskName(nullptr), "%s", ss.str().c_str());
std::this_thread::sleep_for(sleep_time);
}
}