freertos: Schedule tasks immediately when they are created on opposite core

This commit is contained in:
Angus Gratton 2017-02-28 15:13:30 +11:00
parent 20212ee823
commit 8de26e434c
2 changed files with 66 additions and 51 deletions

View file

@ -1046,25 +1046,59 @@ UBaseType_t x;
}
/*-----------------------------------------------------------*/
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode, const BaseType_t xCoreID )
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode, BaseType_t xCoreID )
{
TCB_t *curTCB;
BaseType_t i;
TCB_t *curTCB, *tcb0, *tcb1;
/* Ensure interrupts don't access the task lists while the lists are being
updated. */
taskENTER_CRITICAL(&xTaskQueueMutex);
{
uxCurrentNumberOfTasks++;
//If the task has no affinity and nothing is scheduled on this core, just throw it this core.
//If it has affinity, throw it on the core that needs it if nothing is already scheduled there.
BaseType_t xMyCore = xCoreID;
if ( xMyCore == tskNO_AFFINITY) xMyCore = xPortGetCoreID();
if( pxCurrentTCB[ xMyCore ] == NULL )
// Determine which core this task starts on
if ( xCoreID == tskNO_AFFINITY )
{
if ( portNUM_PROCESSORS == 1 )
{
xCoreID = 0;
}
else
{
// if the task has no affinity, put it on either core if nothing is currently scheduled there. Failing that,
// put it on the core where it will preempt the lowest priority running task. If neither of these are true,
// queue it on the currently running core.
tcb0 = pxCurrentTCB[0];
tcb1 = pxCurrentTCB[1];
if ( tcb0 == NULL )
{
xCoreID = 0;
}
else if ( tcb1 == NULL )
{
xCoreID = 1;
}
else if ( tcb0->uxPriority < pxNewTCB->uxPriority && tcb0->uxPriority < tcb1->uxPriority )
{
xCoreID = 0;
}
else if ( tcb1->uxPriority < pxNewTCB->uxPriority )
{
xCoreID = 1;
}
else
{
xCoreID = xPortGetCoreID(); // Both CPU have higher priority tasks running on them, so this won't run yet
}
}
}
// If nothing is running on this core, put the new task there now
if( pxCurrentTCB[ xCoreID ] == NULL )
{
/* There are no other tasks, or all the other tasks are in
the suspended state - make this the current task. */
pxCurrentTCB[ xMyCore ] = pxNewTCB;
pxCurrentTCB[ xCoreID ] = pxNewTCB;
if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
{
@ -1090,19 +1124,11 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
so far. */
if( xSchedulerRunning == pdFALSE )
{
/* Scheduler isn't running yet. We need to determine on which CPU to run this task. */
for ( i=0; i<portNUM_PROCESSORS; i++ )
/* Scheduler isn't running yet. We need to determine on which CPU to run this task.
Schedule now if either nothing is scheduled yet or we can replace a task of lower prio. */
if ( pxCurrentTCB[xCoreID] == NULL || pxCurrentTCB[xCoreID]->uxPriority <= pxNewTCB->uxPriority )
{
/* Can we schedule this task on core i? */
if (xCoreID == tskNO_AFFINITY || xCoreID == i)
{
/* Schedule if nothing is scheduled yet, or overwrite a task of lower prio. */
if ( pxCurrentTCB[i] == NULL || pxCurrentTCB[i]->uxPriority <= pxNewTCB->uxPriority )
{
pxCurrentTCB[i] = pxNewTCB;
break;
}
}
pxCurrentTCB[xCoreID] = pxNewTCB;
}
}
else
@ -1130,37 +1156,27 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
if( xSchedulerRunning != pdFALSE )
{
taskENTER_CRITICAL(&xTaskQueueMutex);
curTCB = pxCurrentTCB[ xPortGetCoreID() ];
taskENTER_CRITICAL(&xTaskQueueMutex);
curTCB = pxCurrentTCB[ xCoreID ];
/* Scheduler is running. If the created task is of a higher priority than an executing task
then it should run now.
ToDo: This only works for the current core. If a task is scheduled on an other processor,
the other processor will keep running the task it's working on, and only switch to the newer
task on a timer interrupt. */
//No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires.
if( curTCB->uxPriority < pxNewTCB->uxPriority )
then it should run now.
*/
if( curTCB == NULL || curTCB->uxPriority < pxNewTCB->uxPriority )
{
/* Scheduler is running. If the created task is of a higher priority than an executing task
then it should run now.
No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires.
*/
if( tskCAN_RUN_HERE( xCoreID ) && curTCB->uxPriority < pxNewTCB->uxPriority )
if( xCoreID == xPortGetCoreID() )
{
taskYIELD_IF_USING_PREEMPTION_MUX(&xTaskQueueMutex);
}
else if( xCoreID != xPortGetCoreID() ) {
else {
taskYIELD_OTHER_CORE(xCoreID, pxNewTCB->uxPriority);
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
taskEXIT_CRITICAL(&xTaskQueueMutex);
taskEXIT_CRITICAL(&xTaskQueueMutex);
}
else
{

View file

@ -25,8 +25,7 @@ static void task_event_group_call_response(void *param)
for (int i = 0; i < COUNT; i++) {
/* Wait until the common "call" bit is set, starts off all tasks
(clear on return) */
while (!xEventGroupWaitBits(eg, BIT_CALL, true, false, portMAX_DELAY)) {
}
TEST_ASSERT( xEventGroupWaitBits(eg, BIT_CALL, true, false, portMAX_DELAY) );
/* Set our individual "response" bit */
xEventGroupSetBits(eg, BIT_RESPONSE(task_num));
@ -42,25 +41,25 @@ TEST_CASE("FreeRTOS Event Groups", "[freertos]")
eg = xEventGroupCreate();
done_sem = xSemaphoreCreateCounting(NUM_TASKS, 0);
/* Note: task_event_group_call_response all have higher priority than us, so will block together.
/* Note: task_event_group_call_response all have higher priority than this task, so on this core
they will always preempt this task.
This is important because we need to know they'll all have blocked on BIT_CALL each time we
signal it, or they get out of sync.
This is important because we need to know all tasks have blocked on BIT_CALL each time we signal it,
or they get out of sync.
*/
for (int c = 0; c < NUM_TASKS; c++) {
xTaskCreatePinnedToCore(task_event_group_call_response, "tsk_call_resp", 4096, (void *)c, configMAX_PRIORITIES - 1, NULL, c % portNUM_PROCESSORS);
}
/* Scheduler weirdness (bug?), if we don't sleep a few ticks here then the tasks on the other CPU aren't running yet... */
vTaskDelay(10);
/* Tasks all start instantly, but this task will resume running at the same time as the higher priority tasks on the
other processor may still be setting up, so give a tick for them to also block on BIT_CALL... */
vTaskDelay(1);
for (int i = 0; i < COUNT; i++) {
if (i % 100 == 0) {
//printf("Call %d\n", i);
}
/* signal all tasks with "CALL" bit... */
xEventGroupSetBits(eg, BIT_CALL);
TEST_ASSERT_EQUAL(ALL_RESPONSE_BITS, xEventGroupWaitBits(eg, ALL_RESPONSE_BITS, true, true, 100 / portMAX_DELAY));
TEST_ASSERT_EQUAL_HEX16(ALL_RESPONSE_BITS, xEventGroupWaitBits(eg, ALL_RESPONSE_BITS, true, true, 100 / portMAX_DELAY));
}
/* Ensure all tasks cleaned up correctly */