Task context switch happend between two same priority tasks with configUSE_TIME_SLICING=0

Hi Experts,

I’m using FreeRTOS version Kernel V10.0.1 without any changes in kernel code.
Two same priority tasks created and running, configUSE_TIME_SLICING was set to 0, the simple code shown as below,

prvTaskA()
{
    while(1)
    {
        printf("A");
        vTaskDelay(1);
    }
}

prvTaskB()
{
    while(1)
    {
       printf("Enter");
       while(1)
       {
            //wait 500ms
       }
       printf("Exit");
    }
}

In my test, between the print out info “Enter” and “Exit”, I saw a lot “A” were print out.
From my understanding Task A has no chance to excute if Task B running state since they has the same priority and configUSE_TIME_SLICING = 0.

I looked into the xTaskIncrementTick function, I found below code was entered and the priority value is Daemon timer Task since I start a Reload timer running.

if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
    printf("*%d*",pxTCB->uxPriority);
    xSwitchRequired = pdTRUE;
}
else
{
     mtCOVERAGE_TEST_MARKER();
}

I have two questions

  1. Why the code above use the pxTCB->uxPriority >= pxCurrentTCB->uxPriority, It means if two task have same priority can be switched?
  2. In my use case, Can I still use configUSE_TIME_SLICING= 0 to achieve two same priority task not switching until one task finish the processing.

Thanks
Best Regards.

To make sure I don’t misunderstand - are you saying you have a software timer that preempts the task that is sat in a null loop? If so it would be expected that both tasks run because even though you are not using time slicing the two tasks will still run in turn in between each time the timer task runs.

Hi Barry,
Thanks for your quick reponse.
Yes, I have periodic software timer running.
For example, I have three tasks A,B,C, Task A and Task B have the same priority, Task C is the Timer Task has the highest priority.

When Task B is running, kernel will switch to Task C if it is ready. But when Task C finished processing, it should switch back to Task B, why Task A has chance to run?

Thanks.
Best Regards.

The comment in the above code is a bit confusing. Why 500ms? Isn’t this a for ever loop like for(;;)?

And also: printf() can be a “trouble maker”, unless you have inspected its code. It may use a lot of stack space, and it may be non-reentrant. In this case, it would be safer just to increment some variables declared as volatile:

unsigned task_a_loop_count = 0U;
void prvTaskA( void * pvParameters )
{
    while(1)
    {
        //printf(“A”);
        task_a_loop_count++;
        vTaskDelay(1);
    }
}

and inspect them during a break. Also:

while(1)
{
    task_b_loop_count++;
    //wait 500ms
}

Hi Tibosch,

Thanks for pointing out to me.
I wrote the wrong code here, my purpose is explain Task B is always in ready state same as the code you provide.

If change the as you provide, at the begining only task_b_loop_count increase, there was no change to run task A. Then high prioroity Task C preempted, Task c finished processing.
After that task_b_loop_count and task_a_loop_count increased together, it means Task A and Task B run in turn.

My understanding is Task B switch to Task C, and then back to task B, since Task B and Task A have same priority and configUSE_TIME_SLICING = 0, so Task A will always has no chance to run.

Thanks.
Best Regards.

@rtel @htibosch
Hi,
Actually I have same target want to achieve as below link posted.

My understanding is that same priority means same priority. If you want B to deterministically be preferred over A, give B a higher priority. If two tasks have the same prio, the OS is basically free to choose whichever it grants CPU time when both are ready and have the currently highest priority.

Look at it this way: Without C or an ISR in place, the two are the highest pri players in the game and can fight it out among each other, but as soon as the scheduler comes back from something else, the cards are reshuffled.

There are other RTOSs that follow different philosophies. The now defunkt AMX, for example, treated same pri tasks as you suggested, which implies that tasks on the same priority (for example, parallel receiver tasks of several serial interfaces) must explicitly pass the baton back and forth between each other to prevent starvation. I never liked that architecture.

Don’t make assumptions. “Equal priority” means exactly that.

When Task C blocks, the schedule goes to the Highest Priority Ready Task, and the rule to break ties is the one that has been the longest since it has run, so Tasks A and B will alternate between interruptions.

configUSE_TIME_SLICING doesn’t force one task to keep running like you are assuming but says that ‘extra’ scheduling slots just because of the tick interrupt aren’t added. This lowers the system overhead a bit at the cost of a possibility that if nothing does happen to interrupt a task, it can starve other task of the same priority.

1 Like

@richard-damon @rtel
Hi All,

Below explaination copied from
With configUSE_TIME_SLICING set to 0 a context switch should not occur
on a tick interrupt, but only when a higher priority task enters the
Ready state, or when an interrupt other than the tick interrupt requests
a context switch.

It mentioned a higher priority task enter the ready state, then a context switch will happen. It make sense to me. But now in my test if a equal priority task enter the ready state, the context switch happend as well. That’s the point I can‘t accept.

I think the related code is “if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )” in tasks.c file casue the context switch between two same priority task. Can I change the “>=” to “>”, what will the issues happened, potential risk if I do this change?

Thanks.
Best Regards.

Is this the line you are referring to? https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/tasks.c#L2832 - there a context switch is triggered by another task’s block period expired, rather than just because there is a tick interrupt. Setting configUSE_TIMESLICING to 0 prevents this line from executing: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/tasks.c#L2853, which would otherwise trigger a context switch on every tick interrupt - the tick interrupt marking the end of the time slice.

Arguably the first linked line could be “greater than” rather than “greater than or equal to” as it will cause a context switch to a task that just left the blocked state, which you would prevent by setting configUSE_PREEMPTION to 0, but its not a context switch cause by the end of the running task’s time slice.