From e6d15a097558c279237cf8e97851dc34a3c081c3 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 23 Apr 2021 10:00:17 +1000 Subject: [PATCH] docs: Rename esp_pthread docs to pthread, expand details of supported APIs - Move the code that was in the document out to an example --- docs/en/api-guides/thread-local-storage.rst | 12 +- docs/en/api-reference/system/esp_pthread.rst | 73 --------- docs/en/api-reference/system/index.rst | 2 +- docs/en/api-reference/system/pthread.rst | 146 ++++++++++++++++++ docs/page_redirects.txt | 1 + .../api-reference/system/esp_pthread.rst | 1 - docs/zh_CN/api-reference/system/pthread.rst | 1 + examples/system/pthread/CMakeLists.txt | 6 + examples/system/pthread/Makefile | 8 + examples/system/pthread/README.md | 56 +++++++ examples/system/pthread/example_test.py | 27 ++++ examples/system/pthread/main/CMakeLists.txt | 2 + examples/system/pthread/main/component.mk | 4 + .../system/pthread/main/pthread_example.c | 67 ++++++++ 14 files changed, 326 insertions(+), 80 deletions(-) delete mode 100644 docs/en/api-reference/system/esp_pthread.rst create mode 100644 docs/en/api-reference/system/pthread.rst delete mode 100644 docs/zh_CN/api-reference/system/esp_pthread.rst create mode 100644 docs/zh_CN/api-reference/system/pthread.rst create mode 100644 examples/system/pthread/CMakeLists.txt create mode 100644 examples/system/pthread/Makefile create mode 100644 examples/system/pthread/README.md create mode 100644 examples/system/pthread/example_test.py create mode 100644 examples/system/pthread/main/CMakeLists.txt create mode 100644 examples/system/pthread/main/component.mk create mode 100644 examples/system/pthread/main/pthread_example.c diff --git a/docs/en/api-guides/thread-local-storage.rst b/docs/en/api-guides/thread-local-storage.rst index 3c380db4b4..f77bddc1b7 100644 --- a/docs/en/api-guides/thread-local-storage.rst +++ b/docs/en/api-guides/thread-local-storage.rst @@ -24,12 +24,14 @@ The ESP-IDF FreeRTOS provides the following API to manage thread local variables - :cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback` In this case maximum number of variables that can be allocated is limited by -``configNUM_THREAD_LOCAL_STORAGE_POINTERS`` macro. Variables are kept in the task control block (TCB) +:ref:`CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS` configuration value. Variables are kept in the task control block (TCB) and accessed by their index. Note that index 0 is reserved for ESP-IDF internal uses. + Using that API user can allocate thread local variables of an arbitrary size and assign them to any number of tasks. Different tasks can have different sets of TLS variables. -If size of the variable is more then 4 bytes then user is responsible for allocating/deallocating memory for it. -Variable's deallocation is initiated by FreeRTOS when task is deleted, but user must provide function (callback) + +If size of the variable is more then 4 bytes then user is responsible for allocating/deallocating memory for it. +Variable's deallocation is initiated by FreeRTOS when task is deleted, but user must provide function (callback) to do proper cleanup. .. _pthread-api: @@ -37,7 +39,7 @@ to do proper cleanup. Pthread API ---------------- -The ESP-IDF provides the following pthread API to manage thtread local variables: +The ESP-IDF provides the following :doc:`pthread API ` to manage thread local variables: - :cpp:func:`pthread_key_create` - :cpp:func:`pthread_key_delete` @@ -60,6 +62,6 @@ Note that area for all such variables in the program will be allocated on the st every task in the system even if that task does not use such variables at all. For example ESP-IDF system tasks (like ``ipc``, ``timer`` tasks etc.) will also have that extra stack space allocated. So this feature should be used with care. There is a tradeoff: C11 thread local variables are quite handy -to use in programming and can be accessed using just a few Xtensa instructions, but this benefit goes +to use in programming and can be accessed using minimal CPU instructions, but this benefit goes with the cost of additional stack usage for all tasks in the system. Due to static nature of variables allocation all tasks in the system have the same sets of C11 thread local variables. diff --git a/docs/en/api-reference/system/esp_pthread.rst b/docs/en/api-reference/system/esp_pthread.rst deleted file mode 100644 index f02055ff36..0000000000 --- a/docs/en/api-reference/system/esp_pthread.rst +++ /dev/null @@ -1,73 +0,0 @@ -ESP-pthread -=========== - -Overview --------- - -This module offers Espressif specific extensions to the pthread library that can be used to influence the behaviour of pthreads. Currently the following configuration can be tuned: - - * 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: - -.. code-block:: c - - void * thread_func(void * p) - { - printf("In thread_func\n"); - return NULL; - } - - void app_main(void) - { - pthread_t t1; - - esp_pthread_cfg_t cfg = esp_pthread_get_default_config(); - cfg.stack_size = (4 * 1024); - esp_pthread_set_cfg(&cfg); - - pthread_create(&t1, NULL, thread_func, NULL); - } - -The API can also be used for inheriting the settings across threads. For example: - -.. code-block:: c - - void * my_thread2(void * p) - { - /* This thread will inherit the stack size of 4K */ - printf("In my_thread2\n"); - - return NULL; - } - - void * my_thread1(void * p) - { - printf("In my_thread1\n"); - pthread_t t2; - pthread_create(&t2, NULL, my_thread2, NULL); - - return NULL; - } - - void app_main(void) - { - pthread_t t1; - - esp_pthread_cfg_t cfg = esp_pthread_get_default_config(); - cfg.stack_size = (4 * 1024); - cfg.inherit_cfg = true; - esp_pthread_set_cfg(&cfg); - - pthread_create(&t1, NULL, my_thread1, NULL); - } - -API Reference -------------- - -.. include-build-file:: inc/esp_pthread.inc - diff --git a/docs/en/api-reference/system/index.rst b/docs/en/api-reference/system/index.rst index 020ddbdd01..3fa360854b 100644 --- a/docs/en/api-reference/system/index.rst +++ b/docs/en/api-reference/system/index.rst @@ -11,7 +11,7 @@ System API eFuse Manager Error Codes and Helper Functions ESP HTTPS OTA - ESP pthread + POSIX Threads Support Event Loop Library FreeRTOS FreeRTOS Additions diff --git a/docs/en/api-reference/system/pthread.rst b/docs/en/api-reference/system/pthread.rst new file mode 100644 index 0000000000..94aba961c7 --- /dev/null +++ b/docs/en/api-reference/system/pthread.rst @@ -0,0 +1,146 @@ +POSIX Threads Support +===================== + +Overview +-------- + +ESP-IDF is based on FreeRTOS but offers a range of POSIX-compatible APIs that allow easy porting of third party code. This includes support for common parts of the POSIX Threads "pthreads" API. + +POSIX Threads are implemented in ESP-IDF as wrappers around equivalent FreeRTOS features. The runtime memory or performance overhead of using the pthreads API is quite low, but not every feature available in either pthreads or FreeRTOS is available via the ESP-IDF pthreads support. + +Pthreads can be used in ESP-IDF by including standard ``pthread.h`` header, which is included in the toolchain libc. An additional ESP-IDF specific header, ``esp_pthread.h``, provides additional non-POSIX APIs for using some ESP-IDF features with pthreads. + +C++ Standard Library implementations for ``std::thread``, ``std::mutex``, ``std::condition_variable``, etc. are implemented using pthreads (via GCC libstdc++). Therefore, restrictions mentioned here also apply to the equivalent C++ standard library functionality. + +RTOS Integration +---------------- + +Unlike many operating systems using POSIX Threads, ESP-IDF is a real-time operating system with a real-time scheduler. This means that a thread will only stop running if a higher priority task is ready to run, the thread blocks on an OS synchronization structure like a mutex, or the thread calls any of the functions ``sleep``, :cpp:func:`vTaskDelay`, or ``usleep``. + +.. note:: + + If calling a standard libc or C++ sleep function, such as ``usleep`` defined in ``unistd.h``, then the task will only block and yield the CPU if the sleep time is longer than :ref:`one FreeRTOS tick period `. If the time is shorter, the thread will busy-wait instead of yielding to another RTOS task. + +By default all POSIX Threads have the same RTOS priority, but it is possible to change this by calling a :ref:`custom API `. + +Standard features +----------------- + +The following standard APIs are implemented in ESP-IDF. + +Refer to standard POSIX Threads documentation, or pthread.h, for details about the standard arguments and behaviour of each function. Differences or limitations compared to the standard APIs are noted below. + +Thread APIs +^^^^^^^^^^^ + +* ``pthread_create()`` + - The ``attr`` argument is supported for setting stack size and detach state only. Other attribute fields are ignored. + - Unlike FreeRTOS task functions, the ``start_routine`` function is allowed to return. A "detached" type thread is automatically deleted if the function returns. The default "joinable" type thread will be suspended until pthread_join() is called on it. +* ``pthread_join()`` +* ``pthread_detach()`` +* ``pthread_exit()`` +* ``sched_yield()`` +* ``pthread_self()`` + - An assert will fail if this function is called from a FreeRTOS task which is not a pthread. +* ``pthread_equal()`` + +Thread Attributes +^^^^^^^^^^^^^^^^^ + +* ``pthread_attr_init()`` +* ``pthread_attr_destroy()`` + - This function doesn't need to free any resources and instead resets the ``attr`` structure to defaults (implementation is same as ``pthread_attr_init()``). +* ``pthread_attr_getstacksize()`` / ``pthread_attr_setstacksize()`` +* ``pthread_attr_getdetachstate()`` / ``pthread_attr_setdetachstate()`` + +Once +^^^^ + +* ``pthread_once()`` + +Static initializer constant ``PTHREAD_ONCE_INIT`` is supported. + +.. note:: This function can be called from tasks created using either pthread or FreeRTOS APIs + +Mutexes +^^^^^^^ + +POSIX Mutexes are implemented as FreeRTOS Mutex Semaphores (normal type for "fast" or "error check" mutexes, and Recursive type for "recursive" mutexes). This means that they have the same priority inheritance behaviour as mutexes created with :cpp:func:`xSemaphoreCreateMutex`. + +* ``pthread_mutex_init()`` +* ``pthread_mutex_destroy()`` +* ``pthread_mutex_lock()`` +* ``pthread_mutex_timedlock()`` +* ``pthread_mutex_trylock()`` +* ``pthread_mutex_unlock()`` +* ``pthread_mutexattr_init()`` +* ``pthread_mutexattr_destroy()`` +* ``pthread_mutexattr_gettype()`` / ``pthread_mutexattr_settype()`` + +Static initializer constant ``PTHREAD_MUTEX_INITIALIZER`` is supported, but the non-standard static initializer constants for other mutex types are not supported. + +.. note:: These functions can be called from tasks created using either pthread or FreeRTOS APIs + +Condition Variables +^^^^^^^^^^^^^^^^^^^ + +* ``pthread_cond_init()`` + - The ``attr`` argument is not implemented and is ignored. +* ``pthread_cond_destroy()`` +* ``pthread_cond_signal()`` +* ``pthread_cond_broadcast()`` +* ``pthread_cond_wait()`` +* ``pthread_cond_timedwait()`` + +.. note:: These functions can be called from tasks created using either pthread or FreeRTOS APIs + +Thread-Specific Data +^^^^^^^^^^^^^^^^^^^^ + +* ``pthread_key_create()`` + - The ``destr_function`` argument is supported and will be called if a thread function exits normally, calls ``pthread_exit()``, or if the underlying task is deleted directly using the FreeRTOS function :cpp:func:`vTaskDelete`. +* ``pthread_key_delete()`` +* ``pthread_setspecific()`` / ``pthread_getspecific()`` + +.. note:: These functions can be called from tasks created using either pthread or FreeRTOS APIs + +.. note:: There are other options for thread local storage in ESP-IDF, including options with higher performance. See :doc:`/api-guides/thread-local-storage`. + +Not Implemented +--------------- + +The ``pthread.h`` header is a standard header and includes additional APIs and features which are not implemented in ESP-IDF. These include: + +* ``pthread_cancel()`` returns ``ENOSYS`` if called. +* ``pthread_condattr_init()`` returns ``ENOSYS`` if called. +* ``PTHREAD_COND_INITIALIZER`` static initializer constant is not implemented and will crash if passed to a function. + +Other POSIX Threads functions (not listed here) are not implemented and will produce either a compiler or a linker error if referenced from an ESP-IDF application. If you identify a useful API that you would like to see implemented in ESP-IDF, please open a `feature request on GitHub ` with the details. + +.. _esp-pthread: + +ESP-IDF Extensions +------------------ + +The API :cpp:func:`esp_pthread_set_cfg` defined in the ``esp_pthreads.h`` header offers custom extensions to control how subsequent calls to ``pthread_create()`` will behave. Currently the following configuration can be set: + +.. list:: + - Default stack size of new threads, if not specified when calling ``pthread_create()`` (overrides :ref:`CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT`). + - RTOS priority of new threads (overrides :ref:`CONFIG_PTHREAD_TASK_PRIO_DEFAULT`). + :not CONFIG_FREERTOS_UNICORE: - Core affinity / core pinning of new threads (overrides :ref:`CONFIG_PTHREAD_TASK_CORE_DEFAULT`). + - FreeRTOS task name for new threads (overrides :ref:`CONFIG_PTHREAD_TASK_NAME_DEFAULT`) + +This configuration is scoped to the calling thread (or FreeRTOS task), meaning that :cpp:func:`esp_pthread_set_cfg` can be called independently in different threads or tasks. If the ``inherit_cfg`` flag is set in the current configuration then any new thread created will inherit the creator's configuration (if that thread calls ``pthread_create()`` recursively), otherwise the new thread will have the default configuration. + +Examples +-------- + +- :example:`system/pthread` demonstrates using the pthreads API to create threads +- :example:`cxx/pthread` demonstrates using C++ Standard Library functions with threads + + +API Reference +------------- + +.. include-build-file:: inc/esp_pthread.inc + diff --git a/docs/page_redirects.txt b/docs/page_redirects.txt index 920fc41622..336510cb39 100644 --- a/docs/page_redirects.txt +++ b/docs/page_redirects.txt @@ -21,6 +21,7 @@ api-reference/wifi/esp_now api-reference/network/esp_now api-reference/wifi/esp_smartconfig api-reference/network/esp_smartconfig api-reference/wifi/esp_wifi api-reference/network/esp_wifi api-reference/system/tcpip_adapter api-reference/network/esp_netif +api-reference/system/esp_pthread api-reference/system/pthread get-started/idf-monitor api-guides/tools/idf-monitor get-started-cmake/idf-monitor api-guides/tools/idf-monitor get-started/get-started-devkitc hw-reference/esp32/get-started-devkitc diff --git a/docs/zh_CN/api-reference/system/esp_pthread.rst b/docs/zh_CN/api-reference/system/esp_pthread.rst deleted file mode 100644 index 274bc26cb3..0000000000 --- a/docs/zh_CN/api-reference/system/esp_pthread.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../../../en/api-reference/system/esp_pthread.rst diff --git a/docs/zh_CN/api-reference/system/pthread.rst b/docs/zh_CN/api-reference/system/pthread.rst new file mode 100644 index 0000000000..79c5d3f253 --- /dev/null +++ b/docs/zh_CN/api-reference/system/pthread.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/system/pthread.rst diff --git a/examples/system/pthread/CMakeLists.txt b/examples/system/pthread/CMakeLists.txt new file mode 100644 index 0000000000..43848cbf3b --- /dev/null +++ b/examples/system/pthread/CMakeLists.txt @@ -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(pthread) diff --git a/examples/system/pthread/Makefile b/examples/system/pthread/Makefile new file mode 100644 index 0000000000..7c8714cccd --- /dev/null +++ b/examples/system/pthread/Makefile @@ -0,0 +1,8 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := pthread + +include $(IDF_PATH)/make/project.mk diff --git a/examples/system/pthread/README.md b/examples/system/pthread/README.md new file mode 100644 index 0000000000..047df78c57 --- /dev/null +++ b/examples/system/pthread/README.md @@ -0,0 +1,56 @@ +# Pthread Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +ESP-IDF provides implementations for many standard POSIX Threads (pthread) APIs. These APIs are impmlemented as thin wrappers around FreeRTOS APIs. + +This example demonstrates how to create threads using this API, and how to use the ESP-IDF pthreads extension API to set default thread parameters for the related FreeRTOS tasks. For complete details about supported Pthreads APIs, consult the ESP-IDF Programmers Guide. + +## How to use example + +### Hardware Required + +This example should be able to run on any supported Espressif SoC development board. + +### Configure the project + +``` +idf.py set-target esp32 +``` + +(Replace `esp32` with the name of the chip target you are using.) + +``` +idf.py menuconfig +``` + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +``` +Created thread 0x3ffaff74 +Created larger stack thread 0x3ffb7ca8 +This thread has ID 0x3ffb7ca8 and 15896 bytes free stack +This thread has ID 0x3ffaff74 and 2616 bytes free stack +Thread 0x3ffaff74 exiting +Thread 0x3ffb7ca8 exiting +Threads have exited + +Created thread 0x3ffb44c8 with new default config +This thread has ID 0x3ffb44c8 and 32312 bytes free stack +Thread 0x3ffb44c8 exiting +Thread has exited +``` diff --git a/examples/system/pthread/example_test.py b/examples/system/pthread/example_test.py new file mode 100644 index 0000000000..849afedb23 --- /dev/null +++ b/examples/system/pthread/example_test.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +from __future__ import division, print_function, unicode_literals + +import re +from typing import Any + +import ttfw_idf + + +@ttfw_idf.idf_example_test(env_tag='Example_GENERIC', target=['esp32', 'esp32s2', 'esp32c3']) +def test_examples_pthread(env, _): # type: (Any, Any) -> None + app_name = 'pthread' + dut = env.get_dut(app_name, 'examples/system/pthread') + dut.start_app() + + # Note: this test doesn't really confirm anything, except that threads are created + # and stdout is not being corrupted by multiple threads printing ot it. + dut.expect(re.compile(r'Created thread 0x[\da-f]+')) + dut.expect(re.compile(r'Created larger stack thread 0x[\da-f]+')) + dut.expect(r'Threads have exited') + dut.expect(re.compile(r'Created thread 0x[\da-f]+ with new default config')) + dut.expect('Thread has exited') + + +if __name__ == '__main__': + test_examples_pthread() diff --git a/examples/system/pthread/main/CMakeLists.txt b/examples/system/pthread/main/CMakeLists.txt new file mode 100644 index 0000000000..8e83aec55f --- /dev/null +++ b/examples/system/pthread/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "pthread_example.c" + INCLUDE_DIRS ".") diff --git a/examples/system/pthread/main/component.mk b/examples/system/pthread/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/system/pthread/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/system/pthread/main/pthread_example.c b/examples/system/pthread/main/pthread_example.c new file mode 100644 index 0000000000..43204d1bbb --- /dev/null +++ b/examples/system/pthread/main/pthread_example.c @@ -0,0 +1,67 @@ +/* Pthread 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 +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_pthread.h" + +static void *example_thread(void * arg); + +void app_main(void) +{ + pthread_attr_t attr; + pthread_t thread1, thread2; + esp_pthread_cfg_t esp_pthread_cfg; + int res; + + // Create a pthread with the default parameters + res = pthread_create(&thread1, NULL, example_thread, NULL); + assert(res == 0); + printf("Created thread 0x%x\n", thread1); + + // Create a pthread with a larger stack size using the standard API + res = pthread_attr_init(&attr); + assert(res == 0); + pthread_attr_setstacksize(&attr, 16384); + res = pthread_create(&thread2, &attr, example_thread, NULL); + assert(res == 0); + printf("Created larger stack thread 0x%x\n", thread2); + + res = pthread_join(thread1, NULL); + assert(res == 0); + res = pthread_join(thread2, NULL); + assert(res == 0); + printf("Threads have exited\n\n"); + + // Use the ESP-IDF API to change the default thread attributes + esp_pthread_cfg = esp_pthread_get_default_config(); + esp_pthread_cfg.stack_size = 32768; + esp_pthread_cfg.prio += 2; + ESP_ERROR_CHECK( esp_pthread_set_cfg(&esp_pthread_cfg) ); + + res = pthread_create(&thread1, NULL, example_thread, NULL); + assert(res == 0); + printf("Created thread 0x%x with new default config\n", thread1); + res = pthread_join(thread1, NULL); + assert(res == 0); + printf("Thread has exited\n\n"); +} + +static void *example_thread(void * arg) +{ + usleep(250 * 1000); + printf("This thread has ID 0x%x and %u bytes free stack\n", pthread_self(), uxTaskGetStackHighWaterMark(NULL)); + + sleep(1); + printf("Thread 0x%x exiting\n", pthread_self()); + + return NULL; +}