]> granicus.if.org Git - esp-idf/commitdiff
#2743 - Implemented ability to core affinity and thread name for pthreads and thus...
authorPer Malmberg <per.malmberg@gmail.com>
Wed, 28 Nov 2018 19:40:32 +0000 (20:40 +0100)
committerPer Malmberg <per.malmberg@gmail.com>
Wed, 28 Nov 2018 19:43:09 +0000 (20:43 +0100)
components/pthread/Kconfig
components/pthread/include/esp_pthread.h
components/pthread/pthread.c
docs/en/api-reference/system/esp_pthread.rst
examples/system/cpp_pthread/CMakeLists.txt [new file with mode: 0644]
examples/system/cpp_pthread/Makefile [new file with mode: 0644]
examples/system/cpp_pthread/README.md [new file with mode: 0644]
examples/system/cpp_pthread/main/CMakeLists.txt [new file with mode: 0644]
examples/system/cpp_pthread/main/component.mk [new file with mode: 0644]
examples/system/cpp_pthread/main/cpp_pthread.cpp [new file with mode: 0644]

index 349778b2cc73a86caba8edd3fc80b3cedb9e5b29..ed49dc58568140e82b8d6e9241cbf1b5ec327b34 100644 (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
index 3ce3703dccba52e6310259593e2242b8f8179225..76f45a32abd582ff1f304835c18272193d989777 100644 (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
  *
index a158294a37448715cf894e463f2446b3d314b2f0..1baa035c9b66b058364f6bce68832bec7521faaf 100644 (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);
index 7e43ce365e591de0cdc79c13617205c573113027..ab30dc2411cc38ae0f3b48944a516509ea22008a 100644 (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);
diff --git a/examples/system/cpp_pthread/CMakeLists.txt b/examples/system/cpp_pthread/CMakeLists.txt
new file mode 100644 (file)
index 0000000..06c83d8
--- /dev/null
@@ -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)
diff --git a/examples/system/cpp_pthread/Makefile b/examples/system/cpp_pthread/Makefile
new file mode 100644 (file)
index 0000000..7001b52
--- /dev/null
@@ -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
+
diff --git a/examples/system/cpp_pthread/README.md b/examples/system/cpp_pthread/README.md
new file mode 100644 (file)
index 0000000..4f925b7
--- /dev/null
@@ -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.
diff --git a/examples/system/cpp_pthread/main/CMakeLists.txt b/examples/system/cpp_pthread/main/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f95c599
--- /dev/null
@@ -0,0 +1,4 @@
+set(COMPONENT_SRCS "cpp_pthread.cpp")
+set(COMPONENT_ADD_INCLUDEDIRS ".")
+
+register_component()
diff --git a/examples/system/cpp_pthread/main/component.mk b/examples/system/cpp_pthread/main/component.mk
new file mode 100644 (file)
index 0000000..44bd2b5
--- /dev/null
@@ -0,0 +1,3 @@
+#
+# Main Makefile. This is basically the same as a component makefile.
+#
diff --git a/examples/system/cpp_pthread/main/cpp_pthread.cpp b/examples/system/cpp_pthread/main/cpp_pthread.cpp
new file mode 100644 (file)
index 0000000..59b0870
--- /dev/null
@@ -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);
+    }
+}
\ No newline at end of file