feat(freertos): introduce thread-safe context management functions

This commit is contained in:
wuzhenghui
2026-06-18 09:41:39 +08:00
parent e389f3e4df
commit 90f6eeb327
13 changed files with 214 additions and 5 deletions
@@ -327,6 +327,24 @@ and vPortExitCritical() from precompiled libraries (.a) thereby failing linking.
void vPortEnterCritical(void);
void vPortExitCritical(void);
/**
* @brief Claim thread-safe region start
* If claimed, vPortEnterCritical/vPortExitCritical on the current core are no-ops.
* Only can be used in single-core running context with interrupts disabled.
* @note !!! Caller must guarantee thread safety between Claim and Disclaim !!!
*/
void xPortThreadSafeClaim(void);
/**
* @brief Claim thread-safe region end
* Restores normal port critical behavior
* Only can be used in single-core running context with interrupts disabled.
* @note !!! Caller must guarantee thread safety between Claim and Disclaim !!!
*/
void xPortThreadSafeDisclaim(void);
extern volatile bool port_xThreadSafeClaimed;
//IDF task critical sections
#define portTRY_ENTER_CRITICAL(lock, timeout) ({(void) lock; (void) timeout; vPortEnterCritical(); pdPASS;})
#define portENTER_CRITICAL_IDF(lock) ({(void) lock; vPortEnterCritical();})
@@ -1,11 +1,13 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <stdbool.h>
#include <string.h>
#include "esp_compiler.h"
#include "soc/soc_caps.h"
#include "soc/periph_defs.h"
#include "soc/system_reg.h"
@@ -75,6 +77,7 @@ StackType_t *xIsrStackTop = &xIsrStack[0] + (configISR_STACK_SIZE & (~((portPOIN
// Variables used for IDF style critical sections. These are orthogonal to FreeRTOS critical sections
static UBaseType_t port_uxCriticalNestingIDF = 0;
static UBaseType_t port_uxCriticalOldInterruptStateIDF = 0;
volatile bool port_xThreadSafeClaimed = false;
/* ------------------------------------------------ IDF Compatibility --------------------------------------------------
* - These need to be defined for IDF to compile
@@ -82,8 +85,25 @@ static UBaseType_t port_uxCriticalOldInterruptStateIDF = 0;
// ------------------ Critical Sections --------------------
void xPortThreadSafeClaim(void)
{
configASSERT(!xPortCanYield());
configASSERT(!port_xThreadSafeClaimed);
port_xThreadSafeClaimed = true;
}
void xPortThreadSafeDisclaim(void)
{
configASSERT(!xPortCanYield());
configASSERT(port_xThreadSafeClaimed);
port_xThreadSafeClaimed = false;
}
void vPortEnterCritical(void)
{
if (unlikely(port_xThreadSafeClaimed)) {
return;
}
// Save current interrupt threshold and disable interrupts
UBaseType_t old_thresh = ulPortSetInterruptMask();
// Update the IDF critical nesting count
@@ -96,6 +116,9 @@ void vPortEnterCritical(void)
void vPortExitCritical(void)
{
if (unlikely(port_xThreadSafeClaimed)) {
return;
}
/* Critical section nesting coung must never be negative */
configASSERT( port_uxCriticalNestingIDF > 0 );
@@ -293,6 +316,7 @@ BaseType_t xPortStartScheduler(void)
{
uxInterruptNesting = 0;
port_uxCriticalNestingIDF = 0;
port_xThreadSafeClaimed = false;
uxSchedulerRunning = 0;
#if configNUM_CORES > 1
port_uxCoreStartupDone[xPortGetCoreID()] = 0;
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -321,6 +321,24 @@ static inline void __attribute__((always_inline)) vPortCPUReleaseMutex(portMUX_T
// ------------------ Critical Sections --------------------
/**
* @brief Claim thread-safe region start
* If claimed, vPortEnterCritical/vPortExitCritical on the current core are no-ops.
* Only can be used in single-core running context with interrupts disabled.
* @note !!! Caller must guarantee thread safety between Claim and Disclaim !!!
*/
void xPortThreadSafeClaim(void);
/**
* @brief Claim thread-safe region end
* Restores normal port critical behavior
* Only can be used in single-core running context with interrupts disabled.
* @note !!! Caller must guarantee thread safety between Claim and Disclaim !!!
*/
void xPortThreadSafeDisclaim(void);
extern volatile bool port_xThreadSafeClaimed;
BaseType_t xPortEnterCriticalTimeout(portMUX_TYPE *lock, BaseType_t timeout);
static inline void __attribute__((always_inline)) vPortEnterCriticalIDF(portMUX_TYPE *lock)
@@ -5,8 +5,10 @@
*/
#include "sdkconfig.h"
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "esp_compiler.h"
#include "FreeRTOS.h"
#include "task.h" //For vApplicationStackOverflowHook
#include "port_systick.h"
@@ -102,6 +104,7 @@ Variables used by IDF critical sections only (SMP tracks critical nesting inside
*/
BaseType_t port_uxCriticalNestingIDF[portNUM_PROCESSORS] = {0};
BaseType_t port_uxCriticalOldInterruptStateIDF[portNUM_PROCESSORS] = {0};
volatile bool port_xThreadSafeClaimed = false;
/*
*******************************************************************************
@@ -113,8 +116,25 @@ volatile StackType_t DRAM_ATTR __attribute__((aligned(16))) port_IntStack[portNU
/* One flag for each individual CPU. */
volatile uint32_t port_switch_flag[portNUM_PROCESSORS];
void xPortThreadSafeClaim(void)
{
configASSERT(!xPortCanYield());
configASSERT(!port_xThreadSafeClaimed);
port_xThreadSafeClaimed = true;
}
void xPortThreadSafeDisclaim(void)
{
configASSERT(!xPortCanYield());
configASSERT(port_xThreadSafeClaimed);
port_xThreadSafeClaimed = false;
}
BaseType_t xPortEnterCriticalTimeout(portMUX_TYPE *lock, BaseType_t timeout)
{
if (unlikely(port_xThreadSafeClaimed)) {
return pdPASS;
}
/* Interrupts may already be disabled (if this function is called in nested
* manner). However, there's no atomic operation that will allow us to check,
* thus we have to disable interrupts again anyways.
@@ -143,6 +163,9 @@ BaseType_t xPortEnterCriticalTimeout(portMUX_TYPE *lock, BaseType_t timeout)
void vPortExitCriticalIDF(portMUX_TYPE *lock)
{
if (unlikely(port_xThreadSafeClaimed)) {
return;
}
/* This function may be called in a nested manner. Therefore, we only need
* to re-enable interrupts if this is the last call to exit the critical. We
* can use the nesting count to determine whether this is the last exit call.
@@ -338,6 +361,7 @@ BaseType_t xPortStartScheduler( void )
BaseType_t coreID = xPortGetCoreID();
port_xSchedulerRunning[coreID] = 1;
port_uxCoreStartupDone[coreID] = 0;
port_xThreadSafeClaimed = false;
#if configNUM_CORES > 1
// Workaround for non-thread safe multi-core OS startup (see IDF-4524)
@@ -6,7 +6,7 @@
*
* SPDX-License-Identifier: MIT
*
* SPDX-FileContributor: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileContributor: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
@@ -6968,6 +6968,11 @@ static void prvResetNextTaskUnblockTime( void )
void vTaskEnterCritical( void )
{
if( port_xThreadSafeClaimed )
{
return;
}
traceENTER_vTaskEnterCritical();
portDISABLE_INTERRUPTS();
@@ -7095,6 +7100,11 @@ static void prvResetNextTaskUnblockTime( void )
void vTaskExitCritical( void )
{
if( port_xThreadSafeClaimed )
{
return;
}
traceENTER_vTaskExitCritical();
if( xSchedulerRunning != pdFALSE )
@@ -142,6 +142,10 @@ The following functions were modified to accommodate SMP behavior:
- Queues no longer use queue locks (see `queueUSE_LOCKS`)
- Queues now just use critical sections and skips queue locking
- Queue functions can now execute within a single critical section block
- New `xPortThreadSafeClaim()` / `xPortThreadSafeDisclaim()`
- While claimed, port-layer vPortEnterCritical/vPortExitCritical on the current core are no-ops
- Claim only with interrupts disabled on the current core; one active claim system-wide; must pair with Disclaim
- Documented in `portmacro.h` and `docs/en/api-reference/system/freertos_idf.rst`
## Single Core Differences
@@ -230,6 +230,22 @@ void vPortEnterCritical(void);
*/
void vPortExitCritical(void);
/**
* @brief Claim thread-safe region start
* If claimed, vPortEnterCritical/vPortExitCritical on the current core are no-ops.
* Only can be used in single-core running context with interrupts disabled.
* @note !!! Caller must guarantee thread safety between Claim and Disclaim !!!
*/
void xPortThreadSafeClaim(void);
/**
* @brief Claim thread-safe region end
* Restores normal port critical behavior
* Only can be used in single-core running context with interrupts disabled.
* @note !!! Caller must guarantee thread safety between Claim and Disclaim !!!
*/
void xPortThreadSafeDisclaim(void);
#if (configNUM_CORES > 1)
/**
* @brief Enter an SMP critical section with a timeout
@@ -35,6 +35,7 @@
*----------------------------------------------------------------------*/
#include "sdkconfig.h"
#include <stdbool.h>
#include <string.h>
#include "soc/soc_caps.h"
#include "soc/periph_defs.h"
@@ -48,6 +49,7 @@
#include "esp_private/crosscore_int.h"
#include "hal/crosscore_int_ll.h"
#include "esp_attr.h"
#include "esp_compiler.h"
#include "esp_system.h"
#include "esp_intr_alloc.h"
#include "esp_log.h"
@@ -97,6 +99,7 @@ _Static_assert(offsetof( StaticTask_t, pxDummy8 ) == PORT_OFFSET_PX_END_OF_STACK
volatile UBaseType_t port_xSchedulerRunning[portNUM_PROCESSORS] = {0}; // Indicates whether scheduler is running on a per-core basis
volatile UBaseType_t port_uxInterruptNesting[portNUM_PROCESSORS] = {0}; // Interrupt nesting level. Increased/decreased in portasm.c
volatile UBaseType_t port_uxCriticalNesting[portNUM_PROCESSORS] = {0};
volatile bool port_xThreadSafeClaimed = false;
volatile UBaseType_t port_uxOldInterruptState[portNUM_PROCESSORS] = {0};
volatile UBaseType_t xPortSwitchFlag[portNUM_PROCESSORS] = {0};
volatile UBaseType_t port_uxCoreStartupDone[portNUM_PROCESSORS] = {0}; // Indicates whether the core has completed its startup sequence
@@ -156,6 +159,7 @@ BaseType_t xPortStartScheduler(void)
BaseType_t coreID = xPortGetCoreID();
port_uxInterruptNesting[coreID] = 0;
port_uxCriticalNesting[coreID] = 0;
port_xThreadSafeClaimed = false;
port_xSchedulerRunning[coreID] = 0;
port_uxCoreStartupDone[coreID] = 0;
@@ -525,6 +529,9 @@ void vPortClearInterruptMaskFromISR(UBaseType_t prev_int_level)
#if (configNUM_CORES > 1)
BaseType_t __attribute__((optimize("-O3"))) xPortEnterCriticalTimeout(portMUX_TYPE *mux, BaseType_t timeout)
{
if (unlikely(port_xThreadSafeClaimed)) {
return pdPASS;
}
/* Interrupts may already be disabled (if this function is called in nested
* manner). However, there's no atomic operation that will allow us to check,
* thus we have to disable interrupts again anyways.
@@ -552,6 +559,9 @@ BaseType_t __attribute__((optimize("-O3"))) xPortEnterCriticalTimeout(portMUX_TY
void __attribute__((optimize("-O3"))) vPortExitCriticalMultiCore(portMUX_TYPE *mux)
{
if (unlikely(port_xThreadSafeClaimed)) {
return;
}
/* This function may be called in a nested manner. Therefore, we only need
* to re-enable interrupts if this is the last call to exit the critical. We
* can use the nesting count to determine whether this is the last exit call.
@@ -599,12 +609,29 @@ void vPortExitCriticalCompliance(portMUX_TYPE *mux)
}
#endif /* (configNUM_CORES > 1) */
void xPortThreadSafeClaim(void)
{
configASSERT(!xPortCanYield());
configASSERT(!port_xThreadSafeClaimed);
port_xThreadSafeClaimed = true;
}
void xPortThreadSafeDisclaim(void)
{
configASSERT(!xPortCanYield());
configASSERT(port_xThreadSafeClaimed);
port_xThreadSafeClaimed = false;
}
void vPortEnterCritical(void)
{
#if (configNUM_CORES > 1)
esp_rom_printf("vPortEnterCritical(void) is not supported on multi-core targets. Please use vPortEnterCriticalMultiCore(portMUX_TYPE *mux) instead.\n");
abort();
#endif /* (configNUM_CORES > 1) */
if (unlikely(port_xThreadSafeClaimed)) {
return;
}
BaseType_t state = portSET_INTERRUPT_MASK_FROM_ISR();
port_uxCriticalNesting[0]++;
@@ -619,6 +646,9 @@ void vPortExitCritical(void)
esp_rom_printf("vPortExitCritical(void) is not supported on multi-core targets. Please use vPortExitCriticalMultiCore(portMUX_TYPE *mux) instead.\n");
abort();
#endif /* (configNUM_CORES > 1) */
if (unlikely(port_xThreadSafeClaimed)) {
return;
}
/* Critical section nesting count must never be negative */
configASSERT( port_uxCriticalNesting[0] > 0 );
@@ -8,7 +8,7 @@
*
* SPDX-License-Identifier: MIT
*
* SPDX-FileContributor: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileContributor: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
@@ -229,6 +229,22 @@ static inline void __attribute__((always_inline)) vPortEnterCritical(portMUX_TYP
*/
void vPortExitCritical(portMUX_TYPE *mux);
/**
* @brief Claim thread-safe region start
* If claimed, vPortEnterCritical/vPortExitCritical on the current core are no-ops.
* Only can be used in single-core running context with interrupts disabled.
* @note !!! Caller must guarantee thread safety between Claim and Disclaim !!!
*/
void xPortThreadSafeClaim(void);
/**
* @brief Claim thread-safe region end
* Restores normal port critical behavior
* Only can be used in single-core running context with interrupts disabled.
* @note !!! Caller must guarantee thread safety between Claim and Disclaim !!!
*/
void xPortThreadSafeDisclaim(void);
/**
* @brief FreeRTOS Compliant version of xPortEnterCriticalTimeout()
*
@@ -33,6 +33,7 @@
*/
#include "sdkconfig.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -40,6 +41,7 @@
#include <xtensa_context.h>
#include "soc/soc_caps.h"
#include "esp_attr.h"
#include "esp_compiler.h"
#include "esp_private/crosscore_int.h"
#include "esp_private/esp_int_wdt.h"
#include "esp_system.h"
@@ -75,6 +77,7 @@ const DRAM_ATTR uint32_t offset_xCoreID = offsetof(StaticTask_t, xDummyCoreID);
volatile unsigned port_xSchedulerRunning[portNUM_PROCESSORS] = {0}; // Indicates whether scheduler is running on a per-core basis
unsigned port_interruptNesting[portNUM_PROCESSORS] = {0}; // Interrupt nesting level. Increased/decreased in portasm.c, _frxt_int_enter/_frxt_int_exit
BaseType_t port_uxCriticalNesting[portNUM_PROCESSORS] = {0};
volatile bool port_xThreadSafeClaimed = false;
BaseType_t port_uxOldInterruptState[portNUM_PROCESSORS] = {0};
volatile unsigned port_uxCoreStartupDone[portNUM_PROCESSORS] = {0}; // Indicates whether the core has completed its startup sequence
@@ -479,8 +482,25 @@ BaseType_t xPortInterruptedFromISRContext(void)
// ------------------ Critical Sections --------------------
void xPortThreadSafeClaim(void)
{
configASSERT(!xPortCanYield());
configASSERT(!port_xThreadSafeClaimed);
port_xThreadSafeClaimed = true;
}
void xPortThreadSafeDisclaim(void)
{
configASSERT(!xPortCanYield());
configASSERT(port_xThreadSafeClaimed);
port_xThreadSafeClaimed = false;
}
BaseType_t __attribute__((optimize("-O3"))) xPortEnterCriticalTimeout(portMUX_TYPE *mux, BaseType_t timeout)
{
if (unlikely(port_xThreadSafeClaimed)) {
return pdPASS;
}
/* Interrupts may already be disabled (if this function is called in nested
* manner). However, there's no atomic operation that will allow us to check,
* thus we have to disable interrupts again anyways.
@@ -508,6 +528,9 @@ BaseType_t __attribute__((optimize("-O3"))) xPortEnterCriticalTimeout(portMUX_TY
void __attribute__((optimize("-O3"))) vPortExitCritical(portMUX_TYPE *mux)
{
if (unlikely(port_xThreadSafeClaimed)) {
return;
}
/* This function may be called in a nested manner. Therefore, we only need
* to re-enable interrupts if this is the last call to exit the critical. We
* can use the nesting count to determine whether this is the last exit call.
@@ -5,7 +5,7 @@
#pragma once
#ifndef IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP
#define IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP 215
#define IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP 230
#endif
#ifndef IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP_PSRAM
#define IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP_PSRAM 300