From 353e81da633f9fc50815bcaa6415628bf06f7541 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 28 Sep 2017 12:08:34 +1000 Subject: [PATCH] freertos: Also test (& handle) the case where scheduler is disabled on other CPU... ie CPU A has scheduler disabled and task blocked on Q. CPU B sends to Q (or gives mutex, etc.) Task on CPU A should be woken on scheduler resume. --- components/freertos/tasks.c | 9 ++- .../freertos/test/test_suspend_scheduler.c | 61 ++++++++++++++++++- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 15cdaad65..f20715903 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -3039,7 +3039,7 @@ BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) TCB_t *pxUnblockedTCB; BaseType_t xReturn; BaseType_t xTaskCanBeReady; -UBaseType_t i; +UBaseType_t i, uxTargetCPU; /* THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION. It can also be called from a critical section within an ISR. */ @@ -3067,6 +3067,7 @@ UBaseType_t i; the task is pinned to is running or because a scheduler is running on any CPU. */ xTaskCanBeReady = pdFALSE; if ( pxUnblockedTCB->xCoreID == tskNO_AFFINITY ) { + uxTargetCPU = xPortGetCoreID(); for (i = 0; i < portNUM_PROCESSORS; i++) { if ( uxSchedulerSuspended[ i ] == ( UBaseType_t ) pdFALSE ) { xTaskCanBeReady = pdTRUE; @@ -3074,7 +3075,9 @@ UBaseType_t i; } } } else { - xTaskCanBeReady = uxSchedulerSuspended[ pxUnblockedTCB->xCoreID ] == ( UBaseType_t ) pdFALSE; + uxTargetCPU = pxUnblockedTCB->xCoreID; + xTaskCanBeReady = uxSchedulerSuspended[ uxTargetCPU ] == ( UBaseType_t ) pdFALSE; + } if( xTaskCanBeReady ) @@ -3086,7 +3089,7 @@ UBaseType_t i; { /* The delayed and ready lists cannot be accessed, so hold this task pending until the scheduler is resumed on this CPU. */ - vListInsertEnd( &( xPendingReadyList[ xPortGetCoreID() ] ), &( pxUnblockedTCB->xEventListItem ) ); + vListInsertEnd( &( xPendingReadyList[ uxTargetCPU ] ), &( pxUnblockedTCB->xEventListItem ) ); } if ( tskCAN_RUN_HERE(pxUnblockedTCB->xCoreID) && pxUnblockedTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) diff --git a/components/freertos/test/test_suspend_scheduler.c b/components/freertos/test/test_suspend_scheduler.c index b429c7c39..c613dc647 100644 --- a/components/freertos/test/test_suspend_scheduler.c +++ b/components/freertos/test/test_suspend_scheduler.c @@ -49,7 +49,7 @@ static void counter_task_fn(void *vp_config) In the FreeRTOS implementation, this exercises the xPendingReadyList for that core. */ -TEST_CASE("Handle pending context switch while scheduler disabled", "[freertos]") +TEST_CASE("Scheduler disabled can handle a pending context switch on resume", "[freertos]") { isr_count = 0; isr_semaphore = xSemaphoreCreateMutex(); @@ -122,7 +122,7 @@ TEST_CASE("Handle pending context switch while scheduler disabled", "[freertos]" while scheduler is suspended, and should be started once the scheduler resumes. */ -TEST_CASE("Handle waking multiple tasks while scheduler suspended", "[freertos]") +TEST_CASE("Scheduler disabled can wake multiple tasks on resume", "[freertos]") { #define TASKS_PER_PROC 4 TaskHandle_t tasks[portNUM_PROCESSORS][TASKS_PER_PROC] = { 0 }; @@ -163,7 +163,7 @@ TEST_CASE("Handle waking multiple tasks while scheduler suspended", "[freertos]" } } - ets_delay_us(1000); /* Let the other CPU do some things */ + ets_delay_us(200); /* Let the other CPU do some things */ for (int p = 0; p < portNUM_PROCESSORS; p++) { for (int t = 0; t < TASKS_PER_PROC; t++) { @@ -192,3 +192,58 @@ TEST_CASE("Handle waking multiple tasks while scheduler suspended", "[freertos]" } } } + +static volatile bool sched_suspended; +static void suspend_scheduler_5ms_task_fn(void *ignore) +{ + vTaskSuspendAll(); + sched_suspended = true; + for (int i = 0; i <5; i++) { + ets_delay_us(1000); + } + xTaskResumeAll(); + sched_suspended = false; + vTaskDelete(NULL); +} + +#ifndef CONFIG_FREERTOS_UNICORE +/* If the scheduler is disabled on one CPU (A) with a task blocked on something, and a task + on B (where scheduler is running) wakes it, then the task on A should be woken on resume. +*/ +TEST_CASE("Scheduler disabled on CPU B, tasks on A can wake", "[freertos]") +{ + TaskHandle_t counter_task; + SemaphoreHandle_t wake_sem = xSemaphoreCreateMutex(); + xSemaphoreTake(wake_sem, 0); + counter_config_t count_config = { + .trigger_sem = wake_sem, + .counter = 0, + }; + xTaskCreatePinnedToCore(counter_task_fn, "counter", 2048, + &count_config, UNITY_FREERTOS_PRIORITY + 1, + &counter_task, !UNITY_FREERTOS_CPU); + + xTaskCreatePinnedToCore(suspend_scheduler_5ms_task_fn, "suspender", 2048, + NULL, UNITY_FREERTOS_PRIORITY - 1, + NULL, !UNITY_FREERTOS_CPU); + + /* counter task is now blocked on other CPU, waiting for wake_sem, and we expect + that this CPU's scheduler will be suspended for 5ms shortly... */ + while(!sched_suspended) { } + + xSemaphoreGive(wake_sem); + ets_delay_us(1000); + // Bit of a race here if the other CPU resumes its scheduler, but 5ms is a long time... */ + TEST_ASSERT(sched_suspended); + TEST_ASSERT_EQUAL(0, count_config.counter); // the other task hasn't woken yet, because scheduler is off + TEST_ASSERT(sched_suspended); + + /* wait for the rest of the 5ms... */ + while(sched_suspended) { } + + ets_delay_us(100); + TEST_ASSERT_EQUAL(1, count_config.counter); // when scheduler resumes, counter task should immediately count + + vTaskDelete(counter_task); +} +#endif