mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-03 10:30:58 +02:00
Merge branch 'feature/adds_pthread_rwlock_timed_locks' into 'master'
feat(pthread): Adds timed locks for pthread_rwlock Closes IDFGH-15461 See merge request espressif/esp-idf!40146
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
|
#include <time.h>
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_attr.h"
|
#include "esp_attr.h"
|
||||||
#include "sys/queue.h"
|
#include "sys/queue.h"
|
||||||
@@ -311,6 +312,93 @@ int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, const struct timespec *abstime)
|
||||||
|
{
|
||||||
|
esp_pthread_rwlock_t *esp_rwlock;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
res = checkrw_lock(rwlock);
|
||||||
|
if (res != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rwlock = (esp_pthread_rwlock_t *)*rwlock;
|
||||||
|
res = pthread_mutex_lock(&esp_rwlock->resource_mutex);
|
||||||
|
if (res != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are active readers or active writers, we go into the timed wait path
|
||||||
|
// otherwise, we go into the immediate success path.
|
||||||
|
if (esp_rwlock->active_readers || esp_rwlock->active_writers) {
|
||||||
|
// The timed wait path
|
||||||
|
if (!abstime || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
|
||||||
|
// The validity of the abstime parameter need be checked
|
||||||
|
// if the lock can not be immediately acquired (see Immediate success path).
|
||||||
|
pthread_mutex_unlock(&esp_rwlock->resource_mutex);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rwlock->waiting_writers++;
|
||||||
|
while (esp_rwlock->active_readers > 0 || esp_rwlock->active_writers > 0) {
|
||||||
|
res = pthread_cond_timedwait(&esp_rwlock->cv, &esp_rwlock->resource_mutex, abstime);
|
||||||
|
if (res == ETIMEDOUT) {
|
||||||
|
esp_rwlock->waiting_writers--;
|
||||||
|
pthread_mutex_unlock(&esp_rwlock->resource_mutex);
|
||||||
|
return ETIMEDOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
esp_rwlock->waiting_writers--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Immediate success path.
|
||||||
|
esp_rwlock->active_writers++;
|
||||||
|
pthread_mutex_unlock(&esp_rwlock->resource_mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, const struct timespec *abstime)
|
||||||
|
{
|
||||||
|
esp_pthread_rwlock_t *esp_rwlock;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
res = checkrw_lock(rwlock);
|
||||||
|
if (res != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rwlock = (esp_pthread_rwlock_t *)*rwlock;
|
||||||
|
res = pthread_mutex_lock(&esp_rwlock->resource_mutex);
|
||||||
|
if (res != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are active writers or waiting writers, we go into the timed wait path
|
||||||
|
// otherwise, we go into the immediate success path.
|
||||||
|
if (esp_rwlock->active_writers || esp_rwlock->waiting_writers) {
|
||||||
|
// The timed wait path
|
||||||
|
if (!abstime || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
|
||||||
|
// The validity of the abstime parameter need be checked
|
||||||
|
// if the lock can not be immediately acquired (see Immediate success path).
|
||||||
|
pthread_mutex_unlock(&esp_rwlock->resource_mutex);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (esp_rwlock->active_writers > 0 || esp_rwlock->waiting_writers > 0) {
|
||||||
|
res = pthread_cond_timedwait(&esp_rwlock->cv, &esp_rwlock->resource_mutex, abstime);
|
||||||
|
if (res == ETIMEDOUT) {
|
||||||
|
pthread_mutex_unlock(&esp_rwlock->resource_mutex);
|
||||||
|
return ETIMEDOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Immediate success path.
|
||||||
|
esp_rwlock->active_readers++;
|
||||||
|
pthread_mutex_unlock(&esp_rwlock->resource_mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Hook function to force linking this file */
|
/* Hook function to force linking this file */
|
||||||
void pthread_include_pthread_rwlock_impl(void)
|
void pthread_include_pthread_rwlock_impl(void)
|
||||||
{
|
{
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
*/
|
*/
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
@@ -382,3 +383,95 @@ TEST_CASE("trywrlock lock statically initialized lock", "[pthread][rwlock]")
|
|||||||
TEST_ASSERT_EQUAL_INT(pthread_rwlock_unlock(&rwlock), 0);
|
TEST_ASSERT_EQUAL_INT(pthread_rwlock_unlock(&rwlock), 0);
|
||||||
TEST_ASSERT_EQUAL_INT(pthread_rwlock_destroy(&rwlock), 0);
|
TEST_ASSERT_EQUAL_INT(pthread_rwlock_destroy(&rwlock), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct timespec get_abstime_ms(int ms)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
ts.tv_sec = tv.tv_sec + ms / 1000;
|
||||||
|
ts.tv_nsec = (tv.tv_usec * 1000) + (ms % 1000) * 1000000;
|
||||||
|
if (ts.tv_nsec >= 1000000000) {
|
||||||
|
ts.tv_sec += 1;
|
||||||
|
ts.tv_nsec -= 1000000000;
|
||||||
|
}
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pthread_rwlock_timedrdlock invalid params", "[pthread][rwlock][timed]")
|
||||||
|
{
|
||||||
|
struct timespec ts = get_abstime_ms(100);
|
||||||
|
TEST_ASSERT_EQUAL_INT(EINVAL, pthread_rwlock_timedrdlock(NULL, &ts));
|
||||||
|
|
||||||
|
pthread_rwlock_t rwlock = 0;
|
||||||
|
TEST_ASSERT_EQUAL_INT(EINVAL, pthread_rwlock_timedrdlock(&rwlock, &ts));
|
||||||
|
|
||||||
|
ts.tv_nsec = 1000000000; // invalid nanoseconds
|
||||||
|
pthread_rwlock_t rwlock2;
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_init(&rwlock2, NULL));
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_wrlock(&rwlock2));
|
||||||
|
TEST_ASSERT_EQUAL_INT(EINVAL, pthread_rwlock_timedrdlock(&rwlock2, &ts)); // invalid nanoseconds
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_unlock(&rwlock2));
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_destroy(&rwlock2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pthread_rwlock_timedwrlock invalid params", "[pthread][rwlock][timed]")
|
||||||
|
{
|
||||||
|
struct timespec ts = get_abstime_ms(100);
|
||||||
|
TEST_ASSERT_EQUAL_INT(EINVAL, pthread_rwlock_timedwrlock(NULL, &ts));
|
||||||
|
|
||||||
|
pthread_rwlock_t rwlock = 0;
|
||||||
|
TEST_ASSERT_EQUAL_INT(EINVAL, pthread_rwlock_timedwrlock(&rwlock, &ts));
|
||||||
|
|
||||||
|
ts.tv_nsec = -1; // invalid nanoseconds
|
||||||
|
pthread_rwlock_t rwlock2;
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_init(&rwlock2, NULL));
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_timedwrlock(&rwlock2, &ts));
|
||||||
|
TEST_ASSERT_EQUAL_INT(EINVAL, pthread_rwlock_timedwrlock(&rwlock2, &ts)); // invalid nanoseconds
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_unlock(&rwlock2));
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_destroy(&rwlock2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pthread_rwlock_timedrdlock succeeds immediately", "[pthread][rwlock][timed]")
|
||||||
|
{
|
||||||
|
pthread_rwlock_t rwlock;
|
||||||
|
struct timespec ts = get_abstime_ms(200);
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_init(&rwlock, NULL));
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_timedrdlock(&rwlock, &ts));
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_unlock(&rwlock));
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_destroy(&rwlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pthread_rwlock_timedwrlock succeeds immediately", "[pthread][rwlock][timed]")
|
||||||
|
{
|
||||||
|
pthread_rwlock_t rwlock;
|
||||||
|
struct timespec ts = get_abstime_ms(200);
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_init(&rwlock, NULL));
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_timedwrlock(&rwlock, &ts));
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_unlock(&rwlock));
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_destroy(&rwlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pthread_rwlock_timedrdlock times out if writer holds lock", "[pthread][rwlock][timed]")
|
||||||
|
{
|
||||||
|
pthread_rwlock_t rwlock;
|
||||||
|
pthread_rwlock_init(&rwlock, NULL);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_wrlock(&rwlock));
|
||||||
|
struct timespec ts = get_abstime_ms(100);
|
||||||
|
TEST_ASSERT_EQUAL_INT(ETIMEDOUT, pthread_rwlock_timedrdlock(&rwlock, &ts));
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_unlock(&rwlock));
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_destroy(&rwlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pthread_rwlock_timedwrlock times out if reader holds lock", "[pthread][rwlock][timed]")
|
||||||
|
{
|
||||||
|
pthread_rwlock_t rwlock;
|
||||||
|
pthread_rwlock_init(&rwlock, NULL);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_rdlock(&rwlock));
|
||||||
|
struct timespec ts = get_abstime_ms(100);
|
||||||
|
TEST_ASSERT_EQUAL_INT(ETIMEDOUT, pthread_rwlock_timedwrlock(&rwlock, &ts));
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_unlock(&rwlock));
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, pthread_rwlock_destroy(&rwlock));
|
||||||
|
}
|
||||||
|
@@ -154,6 +154,8 @@ The following API functions of the POSIX reader-writer locks specification are i
|
|||||||
* `pthread_rwlock_wrlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html>`_
|
* `pthread_rwlock_wrlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html>`_
|
||||||
* `pthread_rwlock_trywrlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_trywrlock.html>`_
|
* `pthread_rwlock_trywrlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_trywrlock.html>`_
|
||||||
* `pthread_rwlock_unlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_unlock.html>`_
|
* `pthread_rwlock_unlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_unlock.html>`_
|
||||||
|
* `pthread_rwlock_timedwrlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_timedwrlock.html>`_
|
||||||
|
* `pthread_rwlock_timedrdlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_timedrdlock.html>`_
|
||||||
|
|
||||||
The static initializer constant ``PTHREAD_RWLOCK_INITIALIZER`` is supported.
|
The static initializer constant ``PTHREAD_RWLOCK_INITIALIZER`` is supported.
|
||||||
|
|
||||||
|
@@ -154,6 +154,8 @@ ESP-IDF 中实现了 POSIX 读写锁规范的以下 API 函数:
|
|||||||
* `pthread_rwlock_wrlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html>`_
|
* `pthread_rwlock_wrlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html>`_
|
||||||
* `pthread_rwlock_trywrlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_trywrlock.html>`_
|
* `pthread_rwlock_trywrlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_trywrlock.html>`_
|
||||||
* `pthread_rwlock_unlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_unlock.html>`_
|
* `pthread_rwlock_unlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_unlock.html>`_
|
||||||
|
* `pthread_rwlock_timedwrlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_timedwrlock.html>`_
|
||||||
|
* `pthread_rwlock_timedrdlock() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_timedrdlock.html>`_
|
||||||
|
|
||||||
支持静态初始化器常量 ``PTHREAD_RWLOCK_INITIALIZER``。
|
支持静态初始化器常量 ``PTHREAD_RWLOCK_INITIALIZER``。
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user