docs: Rename esp_pthread docs to pthread, expand details of supported APIs

- Move the code that was in the document out to an example
pull/7041/head
Angus Gratton 2021-04-23 10:00:17 +10:00
rodzic bef80909a8
commit e6d15a0975
14 zmienionych plików z 326 dodań i 80 usunięć

Wyświetl plik

@ -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 </api-reference/system/pthread>` 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.

Wyświetl plik

@ -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

Wyświetl plik

@ -11,7 +11,7 @@ System API
eFuse Manager <efuse>
Error Codes and Helper Functions <esp_err>
ESP HTTPS OTA <esp_https_ota>
ESP pthread <esp_pthread>
POSIX Threads Support <pthread>
Event Loop Library <esp_event>
FreeRTOS <freertos>
FreeRTOS Additions <freertos_additions>

Wyświetl plik

@ -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 <CONFIG_FREERTOS_HZ>`. 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 <esp-pthread>`.
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 <https://github.com/espressif/esp-idf/issues>` 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

Wyświetl plik

@ -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

Wyświetl plik

@ -1 +0,0 @@
.. include:: ../../../en/api-reference/system/esp_pthread.rst

Wyświetl plik

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/system/pthread.rst

Wyświetl plik

@ -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)

Wyświetl plik

@ -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

Wyświetl plik

@ -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
```

Wyświetl plik

@ -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()

Wyświetl plik

@ -0,0 +1,2 @@
idf_component_register(SRCS "pthread_example.c"
INCLUDE_DIRS ".")

Wyświetl plik

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

Wyświetl plik

@ -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 <stdio.h>
#include <unistd.h>
#include <pthread.h>
#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;
}