mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'bugfix/pthread_docs' into 'master'
Fix and improve pthread docs, create pthread example Closes IDF-731 and IDFGH-5085 See merge request espressif/esp-idf!13314
This commit is contained in:
commit
b7ad39f694
@ -66,7 +66,7 @@ typedef struct {
|
||||
|
||||
|
||||
static SemaphoreHandle_t s_threads_mux = NULL;
|
||||
static portMUX_TYPE s_mutex_init_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
portMUX_TYPE pthread_lazy_init_lock = portMUX_INITIALIZER_UNLOCKED; // Used for mutexes and cond vars
|
||||
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;
|
||||
@ -585,6 +585,10 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex)
|
||||
if (!mutex) {
|
||||
return EINVAL;
|
||||
}
|
||||
if ((intptr_t) *mutex == PTHREAD_MUTEX_INITIALIZER) {
|
||||
return 0; // Static mutex was never initialized
|
||||
}
|
||||
|
||||
mux = (esp_pthread_mutex_t *)*mutex;
|
||||
if (!mux) {
|
||||
return EINVAL;
|
||||
@ -638,11 +642,11 @@ 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(&pthread_lazy_init_lock);
|
||||
if ((intptr_t) *mutex == PTHREAD_MUTEX_INITIALIZER) {
|
||||
res = pthread_mutex_init(mutex, NULL);
|
||||
}
|
||||
portEXIT_CRITICAL(&s_mutex_init_lock);
|
||||
portEXIT_CRITICAL(&pthread_lazy_init_lock);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -728,6 +732,7 @@ int pthread_mutexattr_init(pthread_mutexattr_t *attr)
|
||||
if (!attr) {
|
||||
return EINVAL;
|
||||
}
|
||||
memset(attr, 0, sizeof(*attr));
|
||||
attr->type = PTHREAD_MUTEX_NORMAL;
|
||||
attr->is_initialized = 1;
|
||||
return 0;
|
||||
@ -769,6 +774,7 @@ int pthread_attr_init(pthread_attr_t *attr)
|
||||
{
|
||||
if (attr) {
|
||||
/* Nothing to allocate. Set everything to default */
|
||||
memset(attr, 0, sizeof(*attr));
|
||||
attr->stacksize = CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT;
|
||||
attr->detachstate = PTHREAD_CREATE_JOINABLE;
|
||||
return 0;
|
||||
@ -778,13 +784,8 @@ int pthread_attr_init(pthread_attr_t *attr)
|
||||
|
||||
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;
|
||||
/* Nothing to deallocate. Reset everything to default */
|
||||
return pthread_attr_init(attr);
|
||||
}
|
||||
|
||||
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize)
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/list.h"
|
||||
#include "pthread_internal.h"
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/time.h>
|
||||
@ -43,12 +44,32 @@ typedef struct esp_pthread_cond {
|
||||
TAILQ_HEAD(, esp_pthread_cond_waiter) waiter_list; ///< head of the list of semaphores
|
||||
} esp_pthread_cond_t;
|
||||
|
||||
int pthread_cond_signal(pthread_cond_t *cv)
|
||||
static int s_check_and_init_if_static(pthread_cond_t *cv)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
if (cv == NULL || *cv == (pthread_cond_t) 0) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (*cv == PTHREAD_COND_INITIALIZER) {
|
||||
portENTER_CRITICAL(&pthread_lazy_init_lock);
|
||||
if (*cv == PTHREAD_COND_INITIALIZER) {
|
||||
res = pthread_cond_init(cv, NULL);
|
||||
}
|
||||
portEXIT_CRITICAL(&pthread_lazy_init_lock);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int pthread_cond_signal(pthread_cond_t *cv)
|
||||
{
|
||||
int res = s_check_and_init_if_static(cv);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
esp_pthread_cond_t *cond = (esp_pthread_cond_t *) *cv;
|
||||
|
||||
_lock_acquire_recursive(&cond->lock);
|
||||
@ -64,8 +85,9 @@ int pthread_cond_signal(pthread_cond_t *cv)
|
||||
|
||||
int pthread_cond_broadcast(pthread_cond_t *cv)
|
||||
{
|
||||
if (cv == NULL || *cv == (pthread_cond_t) 0) {
|
||||
return EINVAL;
|
||||
int res = s_check_and_init_if_static(cv);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
esp_pthread_cond_t *cond = (esp_pthread_cond_t *) *cv;
|
||||
@ -90,8 +112,9 @@ int pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *mut, const struc
|
||||
int ret;
|
||||
TickType_t timeout_ticks;
|
||||
|
||||
if (cv == NULL || *cv == (pthread_cond_t) 0) {
|
||||
return EINVAL;
|
||||
int res = s_check_and_init_if_static(cv);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
esp_pthread_cond_t *cond = (esp_pthread_cond_t *) *cv;
|
||||
@ -180,8 +203,14 @@ int pthread_cond_destroy(pthread_cond_t *cv)
|
||||
if (cv == NULL || *cv == (pthread_cond_t) 0) {
|
||||
return EINVAL;
|
||||
}
|
||||
if (*cv == PTHREAD_COND_INITIALIZER) {
|
||||
return 0; // never initialized
|
||||
}
|
||||
|
||||
esp_pthread_cond_t *cond = (esp_pthread_cond_t *) *cv;
|
||||
if (!cond) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
_lock_acquire_recursive(&cond->lock);
|
||||
if (!TAILQ_EMPTY(&cond->waiter_list)) {
|
||||
|
@ -14,3 +14,5 @@
|
||||
#pragma once
|
||||
|
||||
void pthread_internal_local_storage_destructor_callback(void);
|
||||
|
||||
extern portMUX_TYPE pthread_lazy_init_lock;
|
||||
|
115
components/pthread/test/test_pthread_cond_var.c
Normal file
115
components/pthread/test/test_pthread_cond_var.c
Normal file
@ -0,0 +1,115 @@
|
||||
#include <pthread.h>
|
||||
#include "unity.h"
|
||||
|
||||
typedef struct {
|
||||
pthread_cond_t *cond;
|
||||
pthread_mutex_t *mutex;
|
||||
unsigned delay_ms;
|
||||
} thread_args_t;
|
||||
|
||||
static void *thread_signals(void *arg)
|
||||
{
|
||||
const thread_args_t *targs = (thread_args_t *)arg;
|
||||
int r;
|
||||
|
||||
r = pthread_mutex_lock(targs->mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
|
||||
r = pthread_cond_signal(targs->cond);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
|
||||
r = pthread_mutex_unlock(targs->mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
|
||||
usleep(targs->delay_ms * 1000);
|
||||
|
||||
r = pthread_mutex_lock(targs->mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
|
||||
r = pthread_cond_broadcast(targs->cond);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
|
||||
r = pthread_mutex_unlock(targs->mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *thread_waits(void *arg)
|
||||
{
|
||||
const thread_args_t *targs = (thread_args_t *)arg;
|
||||
int r;
|
||||
|
||||
r = pthread_mutex_lock(targs->mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
|
||||
r = pthread_cond_wait(targs->cond, targs->mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
|
||||
r = pthread_mutex_unlock(targs->mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
|
||||
usleep(targs->delay_ms * 1000);
|
||||
|
||||
r = pthread_mutex_lock(targs->mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
|
||||
struct timespec two_seconds;
|
||||
clock_gettime(CLOCK_REALTIME, &two_seconds);
|
||||
two_seconds.tv_sec += 2;
|
||||
r = pthread_cond_timedwait(targs->cond, targs->mutex, &two_seconds);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
|
||||
r = pthread_mutex_unlock(targs->mutex);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define NUM_THREADS 3
|
||||
|
||||
TEST_CASE("pthread cond wait", "[pthread]")
|
||||
{
|
||||
int r;
|
||||
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
struct {
|
||||
thread_args_t args;
|
||||
pthread_t thread;
|
||||
} wait[NUM_THREADS];
|
||||
struct {
|
||||
thread_args_t args;
|
||||
pthread_t thread;
|
||||
} signal[NUM_THREADS];
|
||||
|
||||
wait[0].args.delay_ms = 50;
|
||||
wait[1].args.delay_ms = 100;
|
||||
wait[2].args.delay_ms = 200;
|
||||
|
||||
signal[0].args.delay_ms = 30;
|
||||
signal[1].args.delay_ms = 150;
|
||||
signal[2].args.delay_ms = 500; // highest delay, ensure that broadcast will be received by all waiter threads
|
||||
|
||||
for (int i = 0; i < NUM_THREADS; i++) {
|
||||
wait[i].args.cond = &cond;
|
||||
wait[i].args.mutex = &mutex;
|
||||
signal[i].args.cond = &cond;
|
||||
signal[i].args.mutex = &mutex;
|
||||
|
||||
r = pthread_create(&signal[i].thread, NULL, thread_signals, &signal[i].args);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
r = pthread_create(&wait[i].thread, NULL, thread_waits, &wait[i].args);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_THREADS; i++) {
|
||||
r = pthread_join(signal[i].thread, NULL);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
pthread_join(wait[i].thread, NULL);
|
||||
TEST_ASSERT_EQUAL_INT(0, r);
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&cond);
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
@ -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.
|
||||
|
@ -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_create_default_pthread_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_create_default_pthread_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
|
||||
|
@ -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>
|
||||
|
147
docs/en/api-reference/system/pthread.rst
Normal file
147
docs/en/api-reference/system/pthread.rst
Normal file
@ -0,0 +1,147 @@
|
||||
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()``
|
||||
|
||||
Static initializer constant ``PTHREAD_COND_INITIALIZER`` is supported.
|
||||
|
||||
.. 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.
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -1 +0,0 @@
|
||||
.. include:: ../../../en/api-reference/system/esp_pthread.rst
|
1
docs/zh_CN/api-reference/system/pthread.rst
Normal file
1
docs/zh_CN/api-reference/system/pthread.rst
Normal file
@ -0,0 +1 @@
|
||||
.. include:: ../../../en/api-reference/system/pthread.rst
|
6
examples/system/pthread/CMakeLists.txt
Normal file
6
examples/system/pthread/CMakeLists.txt
Normal 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(pthread)
|
8
examples/system/pthread/Makefile
Normal file
8
examples/system/pthread/Makefile
Normal file
@ -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
|
56
examples/system/pthread/README.md
Normal file
56
examples/system/pthread/README.md
Normal file
@ -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
|
||||
```
|
27
examples/system/pthread/example_test.py
Normal file
27
examples/system/pthread/example_test.py
Normal file
@ -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()
|
2
examples/system/pthread/main/CMakeLists.txt
Normal file
2
examples/system/pthread/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "pthread_example.c"
|
||||
INCLUDE_DIRS ".")
|
4
examples/system/pthread/main/component.mk
Normal file
4
examples/system/pthread/main/component.mk
Normal file
@ -0,0 +1,4 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
67
examples/system/pthread/main/pthread_example.c
Normal file
67
examples/system/pthread/main/pthread_example.c
Normal file
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user