mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
feat(freertos): introduce thread-safe context management functions
This commit is contained in:
@@ -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;
|
||||
|
||||
+19
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user