mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-03 03:31:41 +03:00
fix(linux): implement pthread-based locks for soft-preemption safety
The Linux target's sys/lock.h provided no-op inline stubs, which was safe only under the assumption of single-threaded execution. With the new FreeRTOS Linux simulator using soft preemption, an outgoing task can still run concurrently with the incoming task until it reaches a yield point, making no-op locks unsafe. Replace the no-op implementation with real pthread mutexes: - Change _lock_t from `typedef int` to `typedef void *` (pointer to a heap-allocated pthread_mutex_t). - Implement all _lock_* functions in a new lock.c, supporting both normal and recursive mutexes. - Zero-initialized locks are lazily created on first acquire using double-checked locking, preserving newlib/esp_libc semantics. - Add lock.c unconditionally to the linux component sources and link pthread.
This commit is contained in:
@@ -3,7 +3,7 @@ if(NOT "${target}" STREQUAL "linux")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(srcs)
|
||||
set(srcs "lock.c")
|
||||
set(includes "include")
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
|
||||
list(APPEND srcs getrandom.c assert_func.c)
|
||||
@@ -16,6 +16,8 @@ idf_component_register(INCLUDE_DIRS ${includes}
|
||||
REQUIRED_IDF_TARGETS linux
|
||||
SRCS ${srcs})
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE pthread)
|
||||
|
||||
find_library(LIB_BSD bsd)
|
||||
if(LIB_BSD)
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE ${LIB_BSD})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -12,30 +12,23 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* newlib locks implementation for CONFIG_IDF_TARGET_LINUX, single threaded.
|
||||
* Note, currently this doesn't implement the functions required
|
||||
* when _RETARGETABLE_LOCKING is defined. They should be added.
|
||||
/**
|
||||
* Lock type backed by pthread mutexes. A zero-initialized _lock_t is valid
|
||||
* and will be lazily created on first acquire (matching newlib/esp_libc
|
||||
* semantics).
|
||||
*/
|
||||
typedef void * _lock_t;
|
||||
|
||||
/* Compatibility definitions for legacy newlib locking functions */
|
||||
typedef int _lock_t;
|
||||
|
||||
static inline void _lock_init(_lock_t *plock) {}
|
||||
static inline void _lock_init_recursive(_lock_t *plock) {}
|
||||
static inline void _lock_close(_lock_t *plock) {}
|
||||
static inline void _lock_close_recursive(_lock_t *plock) {}
|
||||
static inline void _lock_acquire(_lock_t *plock) {}
|
||||
static inline void _lock_acquire_recursive(_lock_t *plock) {}
|
||||
static inline int _lock_try_acquire(_lock_t *plock)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
static inline int _lock_try_acquire_recursive(_lock_t *plock)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
static inline void _lock_release(_lock_t *plock) {}
|
||||
static inline void _lock_release_recursive(_lock_t *plock) {}
|
||||
void _lock_init(_lock_t *plock);
|
||||
void _lock_init_recursive(_lock_t *plock);
|
||||
void _lock_close(_lock_t *plock);
|
||||
void _lock_close_recursive(_lock_t *plock);
|
||||
void _lock_acquire(_lock_t *plock);
|
||||
void _lock_acquire_recursive(_lock_t *plock);
|
||||
int _lock_try_acquire(_lock_t *plock);
|
||||
int _lock_try_acquire_recursive(_lock_t *plock);
|
||||
void _lock_release(_lock_t *plock);
|
||||
void _lock_release_recursive(_lock_t *plock);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <sys/lock.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/*
|
||||
* Protects lazy initialization of locks that were zero-initialized
|
||||
* (e.g. static _lock_t my_lock = 0;). Two threads calling
|
||||
* _lock_acquire on such a lock race to create the underlying mutex;
|
||||
* this guard serialises the creation.
|
||||
*/
|
||||
static pthread_mutex_t s_lock_init_guard = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void lock_init_generic(_lock_t *plock, int recursive)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
if (recursive) {
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
}
|
||||
pthread_mutex_t *m = calloc(1, sizeof(pthread_mutex_t));
|
||||
if (m == NULL) {
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
abort();
|
||||
}
|
||||
pthread_mutex_init(m, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
*plock = (_lock_t)m;
|
||||
}
|
||||
|
||||
static void lazy_init(_lock_t *plock, int recursive)
|
||||
{
|
||||
if (__atomic_load_n(plock, __ATOMIC_ACQUIRE) == NULL) {
|
||||
pthread_mutex_lock(&s_lock_init_guard);
|
||||
if (*plock == NULL) {
|
||||
lock_init_generic(plock, recursive);
|
||||
}
|
||||
pthread_mutex_unlock(&s_lock_init_guard);
|
||||
}
|
||||
}
|
||||
|
||||
void _lock_init(_lock_t *plock)
|
||||
{
|
||||
*plock = NULL;
|
||||
lock_init_generic(plock, 0);
|
||||
}
|
||||
|
||||
void _lock_init_recursive(_lock_t *plock)
|
||||
{
|
||||
*plock = NULL;
|
||||
lock_init_generic(plock, 1);
|
||||
}
|
||||
|
||||
void _lock_close(_lock_t *plock)
|
||||
{
|
||||
if (*plock) {
|
||||
pthread_mutex_destroy((pthread_mutex_t *)(*plock));
|
||||
free(*plock);
|
||||
*plock = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void _lock_close_recursive(_lock_t *plock)
|
||||
{
|
||||
_lock_close(plock);
|
||||
}
|
||||
|
||||
void _lock_acquire(_lock_t *plock)
|
||||
{
|
||||
lazy_init(plock, 0);
|
||||
pthread_mutex_lock((pthread_mutex_t *)(*plock));
|
||||
}
|
||||
|
||||
void _lock_acquire_recursive(_lock_t *plock)
|
||||
{
|
||||
lazy_init(plock, 1);
|
||||
pthread_mutex_lock((pthread_mutex_t *)(*plock));
|
||||
}
|
||||
|
||||
int _lock_try_acquire(_lock_t *plock)
|
||||
{
|
||||
lazy_init(plock, 0);
|
||||
return (pthread_mutex_trylock((pthread_mutex_t *)(*plock)) == 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
int _lock_try_acquire_recursive(_lock_t *plock)
|
||||
{
|
||||
lazy_init(plock, 1);
|
||||
return (pthread_mutex_trylock((pthread_mutex_t *)(*plock)) == 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
void _lock_release(_lock_t *plock)
|
||||
{
|
||||
if (*plock) {
|
||||
pthread_mutex_unlock((pthread_mutex_t *)(*plock));
|
||||
}
|
||||
}
|
||||
|
||||
void _lock_release_recursive(_lock_t *plock)
|
||||
{
|
||||
_lock_release(plock);
|
||||
}
|
||||
Reference in New Issue
Block a user