mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
freertos: Delay context switch from queue/task APIs until exiting critical section
Closes #374: https://github.com/espressif/esp-idf/issues/374
This commit is contained in:
parent
fc15d72038
commit
45581dbaca
@ -79,6 +79,8 @@ extern "C" {
|
||||
#include <xtensa/config/core.h>
|
||||
#include <xtensa/config/system.h> /* required for XSHAL_CLIB */
|
||||
#include <xtensa/xtruntime.h>
|
||||
#include "esp_crosscore_int.h"
|
||||
|
||||
|
||||
//#include "xtensa_context.h"
|
||||
|
||||
@ -261,6 +263,18 @@ void vPortYield( void );
|
||||
void _frxt_setup_switch( void );
|
||||
#define portYIELD() vPortYield()
|
||||
#define portYIELD_FROM_ISR() _frxt_setup_switch()
|
||||
|
||||
static inline uint32_t xPortGetCoreID();
|
||||
|
||||
/* Yielding within an API call (when interrupts are off), means the yield should be delayed
|
||||
until interrupts are re-enabled.
|
||||
|
||||
To do this, we use the "cross-core" interrupt as a trigger to yield on this core when interrupts are re-enabled.This
|
||||
is the same interrupt & code path which is used to trigger a yield between CPUs, although in this case the yield is
|
||||
happening on the same CPU.
|
||||
*/
|
||||
#define portYIELD_WITHIN_API() esp_crosscore_int_send_yield(xPortGetCoreID())
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Task function macros as described on the FreeRTOS.org WEB site. */
|
||||
|
@ -123,15 +123,9 @@ zero. */
|
||||
#if( configUSE_PREEMPTION == 0 )
|
||||
/* If the cooperative scheduler is being used then a yield should not be
|
||||
performed just because a higher priority task has been woken. */
|
||||
#define queueYIELD_IF_USING_PREEMPTION_MUX()
|
||||
#define queueYIELD_IF_USING_PREEMPTION()
|
||||
#else
|
||||
#define queueYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API()
|
||||
#define queueYIELD_IF_USING_PREEMPTION_MUX(mux) { \
|
||||
taskEXIT_CRITICAL(mux); \
|
||||
portYIELD_WITHIN_API(); \
|
||||
taskENTER_CRITICAL(mux); \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -290,7 +284,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
|
||||
{
|
||||
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) == pdTRUE )
|
||||
{
|
||||
queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
|
||||
queueYIELD_IF_USING_PREEMPTION();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -753,7 +747,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
|
||||
/* The queue is a member of a queue set, and posting
|
||||
to the queue set caused a higher priority task to
|
||||
unblock. A context switch is required. */
|
||||
queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
|
||||
queueYIELD_IF_USING_PREEMPTION();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -772,7 +766,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
|
||||
our own so yield immediately. Yes it is ok to
|
||||
do this from within the critical section - the
|
||||
kernel takes care of that. */
|
||||
queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
|
||||
queueYIELD_IF_USING_PREEMPTION();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -785,7 +779,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
|
||||
executed if the task was holding multiple mutexes
|
||||
and the mutexes were given back in an order that is
|
||||
different to that in which they were taken. */
|
||||
queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
|
||||
queueYIELD_IF_USING_PREEMPTION();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -805,7 +799,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
|
||||
our own so yield immediately. Yes it is ok to do
|
||||
this from within the critical section - the kernel
|
||||
takes care of that. */
|
||||
queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
|
||||
queueYIELD_IF_USING_PREEMPTION();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -818,7 +812,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
|
||||
executed if the task was holding multiple mutexes and
|
||||
the mutexes were given back in an order that is
|
||||
different to that in which they were taken. */
|
||||
queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
|
||||
queueYIELD_IF_USING_PREEMPTION();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1493,7 +1487,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
|
||||
{
|
||||
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) == pdTRUE )
|
||||
{
|
||||
queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
|
||||
queueYIELD_IF_USING_PREEMPTION();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1522,7 +1516,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
|
||||
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
|
||||
{
|
||||
/* The task waiting has a higher priority than this task. */
|
||||
queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
|
||||
queueYIELD_IF_USING_PREEMPTION();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -120,14 +120,8 @@ functions but without including stdio.h here. */
|
||||
/* If the cooperative scheduler is being used then a yield should not be
|
||||
performed just because a higher priority task has been woken. */
|
||||
#define taskYIELD_IF_USING_PREEMPTION()
|
||||
#define queueYIELD_IF_USING_PREEMPTION_MUX(mux)
|
||||
#else
|
||||
#define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API()
|
||||
#define taskYIELD_IF_USING_PREEMPTION_MUX(mux) { \
|
||||
taskEXIT_CRITICAL(mux); \
|
||||
portYIELD_WITHIN_API(); \
|
||||
taskENTER_CRITICAL(mux); \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
|
||||
@ -1166,7 +1160,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
|
||||
{
|
||||
if( xCoreID == xPortGetCoreID() )
|
||||
{
|
||||
taskYIELD_IF_USING_PREEMPTION_MUX(&xTaskQueueMutex);
|
||||
taskYIELD_IF_USING_PREEMPTION();
|
||||
}
|
||||
else {
|
||||
taskYIELD_OTHER_CORE(xCoreID, pxNewTCB->uxPriority);
|
||||
@ -1703,7 +1697,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
|
||||
|
||||
if( xYieldRequired == pdTRUE )
|
||||
{
|
||||
taskYIELD_IF_USING_PREEMPTION_MUX(&xTaskQueueMutex);
|
||||
taskYIELD_IF_USING_PREEMPTION();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1895,7 +1889,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
|
||||
/* This yield may not cause the task just resumed to run,
|
||||
but will leave the lists in the correct state for the
|
||||
next yield. */
|
||||
taskYIELD_IF_USING_PREEMPTION_MUX(&xTaskQueueMutex);
|
||||
taskYIELD_IF_USING_PREEMPTION();
|
||||
}
|
||||
else if( pxTCB->xCoreID != xPortGetCoreID() )
|
||||
{
|
||||
@ -2206,7 +2200,7 @@ BaseType_t xAlreadyYielded = pdFALSE;
|
||||
xAlreadyYielded = pdTRUE;
|
||||
}
|
||||
#endif
|
||||
taskYIELD_IF_USING_PREEMPTION_MUX(&xTaskQueueMutex);
|
||||
taskYIELD_IF_USING_PREEMPTION();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
58
components/freertos/test/test_preemption.c
Normal file
58
components/freertos/test/test_preemption.c
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
Unit tests for FreeRTOS preemption
|
||||
*/
|
||||
|
||||
#include <esp_types.h>
|
||||
#include <stdio.h>
|
||||
#include "rom/ets_sys.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/xtensa_api.h"
|
||||
#include "unity.h"
|
||||
#include "soc/cpu.h"
|
||||
|
||||
|
||||
static volatile bool flag;
|
||||
|
||||
static void task_yield(void *param)
|
||||
{
|
||||
QueueHandle_t queue = (QueueHandle_t) param;
|
||||
uint32_t ccount;
|
||||
RSR(CCOUNT, ccount);
|
||||
flag = true;
|
||||
xQueueSendToBack(queue, &ccount, 0);
|
||||
/* This is to ensure that higher priority task
|
||||
won't wake anyhow, due to this task terminating.
|
||||
*/
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
ets_delay_us(1000);
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("Yield from lower priority task, same CPU", "[freertos]")
|
||||
{
|
||||
/* Do this 3 times, mostly for the benchmark value - the first
|
||||
run includes a cache miss so uses more cycles than it should. */
|
||||
for (int i = 0; i < 3; i++) {
|
||||
QueueHandle_t queue = xQueueCreate(1, sizeof(uint32_t));
|
||||
flag = false;
|
||||
|
||||
/* "yield" task sits on our CPU, lower priority to us */
|
||||
xTaskCreatePinnedToCore(task_yield, "YIELD", 2048, (void *)queue, UNITY_FREERTOS_PRIORITY - 1, NULL, UNITY_FREERTOS_CPU);
|
||||
|
||||
uint32_t yield_ccount, now_ccount, delta;
|
||||
TEST_ASSERT( xQueueReceive(queue, &yield_ccount, 100 / portTICK_PERIOD_MS) );
|
||||
RSR(CCOUNT, now_ccount);
|
||||
TEST_ASSERT( flag );
|
||||
|
||||
delta = now_ccount - yield_ccount;
|
||||
printf("Yielding from lower priority task took %u cycles\n", delta);
|
||||
TEST_ASSERT(delta < 10000);
|
||||
|
||||
vQueueDelete(queue);
|
||||
}
|
||||
}
|
@ -9,6 +9,10 @@
|
||||
|
||||
#include <esp_err.h>
|
||||
|
||||
/* Some definitions applicable to Unity running in FreeRTOS */
|
||||
#define UNITY_FREERTOS_PRIORITY 5
|
||||
#define UNITY_FREERTOS_CPU 0
|
||||
|
||||
#define UNITY_EXCLUDE_FLOAT
|
||||
#define UNITY_EXCLUDE_DOUBLE
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
|
||||
#include "unity_config.h"
|
||||
|
||||
void unityTask(void *pvParameters)
|
||||
{
|
||||
@ -15,6 +15,6 @@ void app_main()
|
||||
{
|
||||
// Note: if unpinning this task, change the way run times are calculated in
|
||||
// unity_platform
|
||||
xTaskCreatePinnedToCore(unityTask, "unityTask", 4096, NULL, 5, NULL, 0);
|
||||
xTaskCreatePinnedToCore(unityTask, "unityTask", 4096, NULL,
|
||||
UNITY_FREERTOS_PRIORITY, NULL, UNITY_FREERTOS_CPU);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user