kopia lustrzana https://github.com/espressif/esp-idf
pthread: Add support for PTHREAD_COND_INITIALIZER
Includes unit test for condition variables in C (previous test was C++ only)pull/7041/head
rodzic
e6d15a0975
commit
a6dea64106
|
@ -66,7 +66,7 @@ typedef struct {
|
||||||
|
|
||||||
|
|
||||||
static SemaphoreHandle_t s_threads_mux = NULL;
|
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
|
static SLIST_HEAD(esp_thread_list_head, esp_pthread_entry) s_threads_list
|
||||||
= SLIST_HEAD_INITIALIZER(s_threads_list);
|
= SLIST_HEAD_INITIALIZER(s_threads_list);
|
||||||
static pthread_key_t s_pthread_cfg_key;
|
static pthread_key_t s_pthread_cfg_key;
|
||||||
|
@ -581,6 +581,10 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex)
|
||||||
if (!mutex) {
|
if (!mutex) {
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
if ((intptr_t) *mutex == PTHREAD_MUTEX_INITIALIZER) {
|
||||||
|
return 0; // Static mutex was never initialized
|
||||||
|
}
|
||||||
|
|
||||||
mux = (esp_pthread_mutex_t *)*mutex;
|
mux = (esp_pthread_mutex_t *)*mutex;
|
||||||
if (!mux) {
|
if (!mux) {
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
|
@ -634,11 +638,11 @@ static int pthread_mutex_init_if_static(pthread_mutex_t *mutex)
|
||||||
{
|
{
|
||||||
int res = 0;
|
int res = 0;
|
||||||
if ((intptr_t) *mutex == PTHREAD_MUTEX_INITIALIZER) {
|
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) {
|
if ((intptr_t) *mutex == PTHREAD_MUTEX_INITIALIZER) {
|
||||||
res = pthread_mutex_init(mutex, NULL);
|
res = pthread_mutex_init(mutex, NULL);
|
||||||
}
|
}
|
||||||
portEXIT_CRITICAL(&s_mutex_init_lock);
|
portEXIT_CRITICAL(&pthread_lazy_init_lock);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
#include "freertos/list.h"
|
#include "freertos/list.h"
|
||||||
|
#include "pthread_internal.h"
|
||||||
|
|
||||||
#include <sys/queue.h>
|
#include <sys/queue.h>
|
||||||
#include <sys/time.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
|
TAILQ_HEAD(, esp_pthread_cond_waiter) waiter_list; ///< head of the list of semaphores
|
||||||
} esp_pthread_cond_t;
|
} 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) {
|
if (cv == NULL || *cv == (pthread_cond_t) 0) {
|
||||||
return EINVAL;
|
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;
|
esp_pthread_cond_t *cond = (esp_pthread_cond_t *) *cv;
|
||||||
|
|
||||||
_lock_acquire_recursive(&cond->lock);
|
_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)
|
int pthread_cond_broadcast(pthread_cond_t *cv)
|
||||||
{
|
{
|
||||||
if (cv == NULL || *cv == (pthread_cond_t) 0) {
|
int res = s_check_and_init_if_static(cv);
|
||||||
return EINVAL;
|
if (res) {
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_pthread_cond_t *cond = (esp_pthread_cond_t *) *cv;
|
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;
|
int ret;
|
||||||
TickType_t timeout_ticks;
|
TickType_t timeout_ticks;
|
||||||
|
|
||||||
if (cv == NULL || *cv == (pthread_cond_t) 0) {
|
int res = s_check_and_init_if_static(cv);
|
||||||
return EINVAL;
|
if (res) {
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_pthread_cond_t *cond = (esp_pthread_cond_t *) *cv;
|
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) {
|
if (cv == NULL || *cv == (pthread_cond_t) 0) {
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
if (*cv == PTHREAD_COND_INITIALIZER) {
|
||||||
|
return 0; // never initialized
|
||||||
|
}
|
||||||
|
|
||||||
esp_pthread_cond_t *cond = (esp_pthread_cond_t *) *cv;
|
esp_pthread_cond_t *cond = (esp_pthread_cond_t *) *cv;
|
||||||
|
if (!cond) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
_lock_acquire_recursive(&cond->lock);
|
_lock_acquire_recursive(&cond->lock);
|
||||||
if (!TAILQ_EMPTY(&cond->waiter_list)) {
|
if (!TAILQ_EMPTY(&cond->waiter_list)) {
|
||||||
|
|
|
@ -14,3 +14,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
void pthread_internal_local_storage_destructor_callback(void);
|
void pthread_internal_local_storage_destructor_callback(void);
|
||||||
|
|
||||||
|
extern portMUX_TYPE pthread_lazy_init_lock;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -92,6 +92,8 @@ Condition Variables
|
||||||
* ``pthread_cond_wait()``
|
* ``pthread_cond_wait()``
|
||||||
* ``pthread_cond_timedwait()``
|
* ``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
|
.. note:: These functions can be called from tasks created using either pthread or FreeRTOS APIs
|
||||||
|
|
||||||
Thread-Specific Data
|
Thread-Specific Data
|
||||||
|
@ -113,7 +115,6 @@ The ``pthread.h`` header is a standard header and includes additional APIs and f
|
||||||
|
|
||||||
* ``pthread_cancel()`` returns ``ENOSYS`` if called.
|
* ``pthread_cancel()`` returns ``ENOSYS`` if called.
|
||||||
* ``pthread_condattr_init()`` 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.
|
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.
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue