Quetions about the task scheduler

Hi ,
configUSE_PORT_OPTIMISED_TASK_SELECTION is 0 then task selection is performed in a generic way. There might be a racing condition on uxTopReadyPriority.
Please check this failed case:

  1. Tick IRQ is a low priority IRQ.

  2. When Tick IRQ finished this line “UBaseType_t uxTopPriority = uxTopReadyPriority;” , let’s assume uxTopReadyPriority and uxTopPriority is 5 now.
    {
    ==> UBaseType_t uxTopPriority = uxTopReadyPriority;

    /* Find the highest priority queue that contains ready tasks. /
    while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) )
    {
    configASSERT( uxTopPriority );
    –uxTopPriority;
    }

    /
    listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of
    * the same priority get an equal share of the processor time. /
    listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );
    uxTopReadyPriority = uxTopPriority;
    } /
    taskSELECT_HIGHEST_PRIORITY_TASK */

  3. A higher priority IRQ occurs, tick IRQ is switched out. The higher priority IRQ adds a ready task into the ready list and sets uxTopReadyPriority to this task’s priority, let’s assume it is 6.

  4. The higher priority IRQ finished and tick IRQ contine it’s code. uxTopReadyPriority will be set to a value <= 5.

  5. The task scheduler cannot call priority 6 task on next tick IRQ. That would cause some issues.

Thanks,
Yueming

This is exactly why the sys tick irq must run under control of the critical section.

vTaskStartScheduler doesn’t touch uxTopReadyPriority is another solution.

The tick ISR protects against that, and the ISR is different for each port, so if you think there is a bug please also so say which port you are using.

Reading above, I assume that you are talking about ports which support interrupt nesting. Taking an example of Cortex-M4 - before vTaskSwitchContext is called, all the interrupts which can call FreeRTOS APIs (and as a result can alter FreeRTOS lists) are masked: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/portable/GCC/ARM_CM4F/port.c#L463. So the scenario you mentioned is not possible. You can look at this response to understand interrupt priorities in Cortex-M: Understanding priority levels of ISR and FreeRTOS APIs - #16 by aggarg

Again, as Richard said, if you think there is a bug in a specific port, let us know the port.

Thanks.

Thanks, Gaurav, Richard.
My product is with ARM® Cortex®-M33 core. After set configUSE_PORT_OPTIMISED_TASK_SELECTION to 1 and defined corresponding taskSELECT_HIGHEST_PRIORITY_TASK(), a lot of system issues are fixed.
My MCU vendor might implement an incorrect tick ISR, which caused an impossible bug happened. They are working on it. However, this generic way that taskSELECT_HIGHEST_PRIORITY_TASK() changes uxTopReadyPriority isn’t a good idea.

Would you mind explaining to us why you think so?

Thanks!

Do you have configASSERT() defined?

Yes, it is defined by a MCU vendor API.

In my humble opinion, searching the highest priority task in the whole pxReadyTasksLists everytime only sacrifices a little bit performance. If a MCU vendor cares about the performance, they should implement their own optimised task selection.
We cannot assume all MCU vendor can port everything in a perfect shape. Just like my case, we spent a lot of time to figure out our MCU vendor didn’t follow the FreeRTOS reference design.
Would you like to share your ideas about this? I am long for them.

Hi Yueming,

of course I don’t know about the history of the code, but I believe the issue you raise is similar to that one here:

I tend to believe that the reasoning may be similar here: uxTopReadyPriority is only ever written to in the task selector, but read from in several other places. Without having looked at the code explicitly, it seems as if keeping it as a module level static global helps avoiding overuse of the critical section at pxReadyTasksLists access time.

The other issue, of course, is that optimized task selection is about as close to the heart of the OS as one can get, so at least I would assume that whoever tackles that issue should know (or, at the very least, very thoroughly test) what he/she is doing. Programming errors at this level tend to manifest themselves fairly soon (a profiler would probaly testify that the task selection is among the most heavily used code section in FreeRTOS). If there really have been bugs in a vendor’s adaption that you had to pinpoint, it doesn’t shed a bright light on the adaptor’s quality system (my humble opinion).

Whatever, I’m curiously waiting for Richard Barry’s answer to your query as he probably is the one who coded the selection algorithm (and as far as I can tell, he codes very deliberate and meticuluous, so there probably is a good reasons why it is as it is).

This observation is unexpected. The setting of configUSE_PORT_OPTIMISED_TASK_SELECTION should not impact proper system operation. It should add/remove a minor optimization, as the name implies.

The “bug” you are reporting in taskSELECT_HIGHEST_PRIORITY_TASK() is not actually a bug, as explained by @rtel and @aggarg above.

So how to explain your unexpected observation? One explanation is that you might have configured interrupt priorities incorrectly. What version of FreeRTOS are you using? More recent versions of FreeRTOS catch these configuration errors via configASSERT(). Can you share your definition of configASSERT()?

uxTopReadyPriority records the highest priority for which a task is ready to run. It is updated whenever a task is added to ready list: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/tasks.c#L223. This way the scheduler does not need to search for the all the ready lists but only the list for uxTopReadyPriority.
Port optimized task selection usually maintain top ready priority as a bit in a 32-bit number and use a hardware instruction to find the topmost set bit: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/portable/GCC/ARM_CM4F/portmacro.h#L132. It is not yet clear how setting uxTopReadyPriority can cause any problem.

If you are using Cortex-M33, is there a reason not to use the official port: https://github.com/FreeRTOS/FreeRTOS-Kernel/tree/main/portable/GCC/ARM_CM33.

Thanks.

Hi Jeff,
Here is my configASSERT()

#define configASSERT(x)                               EFM_ASSERT(x)
#define EFM_ASSERT(expr)    ((expr) ? ((void)0) : assertEFM(__FILE__, __LINE__))
66  void assertEFM(const char *file, int line)
67  {
68    (void)file;  /* Unused parameter */
69    (void)line;  /* Unused parameter */
70  
71    while (true) {
72    }
73  }

Thanks for your help! My freertos is "V10.4.3".

It is not yet clear how setting uxTopReadyPriority can cause any problem.

I suspect the MCU vendor didn’t set the correct priority for all ISRs.

If you are using Cortex-M33, is there a reason not to use the official port: FreeRTOS-Kernel/portable/GCC/ARM_CM33 at main · FreeRTOS/FreeRTOS-Kernel · GitHub.

I don’t know the details. I can double check with my MCU vendor.

Yueming,

That definition of assertEFM() isn’t ideal for FreeRTOS. Better to mask interrupts prior to entering the forever loop. That way FreeRTOS can’t continue operating, and your tasks won’t attempt to continue operating. For example, use taskDISABLE_INTERRUPTS();.

However, even with your current definition of assertEFM(), you probably would have noticed hitting it. Especially if it were called from an interrupt at a priority higher than configMAX_SYSCALL_INTERRUPT_PRIORITY, a common error caught by configASSERT().

So, back to explaining your observation that problems go away when you use port-optimized task selection. Did you say that your tick ISR is provided by the MCU vendor? And that the tick ISR doesn’t mask interrupts for the call to xTaskIncrementTick()? Maybe that’s still the problem. Has that been resolved yet?

Did you say that your tick ISR is provided by the MCU vendor? And that the tick ISR doesn’t mask interrupts for the call to xTaskIncrementTick() ? Maybe that’s still the problem. Has that been resolved yet?

Yes, it is from our MCU vendor. We didn’t find this failure sympton untill MCU vendor replaced portDISABLE_INTERRUPTS() with portSET_INTERRUPT_MASK_FROM_ISR() for the call to xTaskIncrementTick().

In that case, you might be better off with the official freertos port, as @aggarg suggested. This forum and the freertos maintainers can be more helpful that way too. Hard to tell what other mistakes the MCU vendor may have made.

Hi Jeff,
Got it! Thanks for your help!

What are the definitions of these two macros? I suspect that you are not setting the tick interrupt priority correctly. Also, I am interested in the reason of not using the official port.

Thanks.