A couple questions related to critical sections and ulCriticalNesting, w.r.t. FreeRTOS 8.1.x specifically:
task.h’s documentation for taskENTER_CRITICAL states:
Macro to mark the start of a critical code region. Preemptive context
switches cannot occur when in a critical region.
What is the exact definition of “Preemptive context switches”? In this context, I believe that it solely refers to context-switches induced by a timer interrupt. In other words, application-initiated context switches are not included in the definition, and hence if an application calls FreeRTOS APIs that might yield, block or change a thread’s priority, context-switching might still occur.
In particular, invocation of APIs such as taskYIELD, vTaskPrioritySet, vTaskDelayUntil, xTaskCreate, (and more) can all cause context switches even inside a critical section. Can you confirm this?
Is it true that all ports must context-switch the ulCriticalNesting value, either directly as part of portSAVE_CONTEXT/portRESTORE_CONTEXT, or by setting portCRITICAL_NESTING_IN_TCB=1, and then likely using vTaskEnter/ExitCritical to implement portENTER/EXIT_CRITICAL?
I believe this is true, since the “IRQ disabled” state is intended to be a per-task setting, i.e. the following represents how critical sections and interupt masking is intended to wokk:
Task 1 enters a critical section. Interrupts are now disabled.
Task 1 yields or blocks.
Task 2 is selected by the scheduler.
Task 2’s context is restored. This (probably) causes interrupts to be re-enabled (depending on task 2’s previous state).
Task 2 yields, blocks, or is pre-empted.
Task 1 is selected by the scheduler.
Task 1’s context is restored. This causes interrupts to be disabled again, since this is part of task 1’s context.
For this to work, I believe that ulCriticalNesting must always be context-switched along with other application state. Otherwise, if Task 2 above were to enter and exit a critical section ulCriticalNesting wouldn’t reach 0, and hence interrupts wouldn’t be re-enabled when its critical section was left.
Background: I ported FreeRTOS to my CPU, and copied code from a port that didn’t context switch ulCriticalNesting. I created an application that ran various of the FreeRTOS demo tasks. I found that pretty soon, the only task running was a CPU-bound thread, and no pre-emption was taking place. Once I fixed the port to context-switch ulCriticalNesting, everything worked as I expected. I believe the issue is exactly as I outlined in the previous two paragraphs. However, a colleague disagrees, believing the issue is that the real fix is to make portYIELD_WITHIN_API use a SW interrupt rather than SVC call.
Assuming that ulCriticalNesting must be context-switched, I don’t understand how some ports are doing so.
For example, Source/portable/CCS/ARM_Cortex-R4/. Its implementation of portDISABLE/ENABLE_INTERRUPTS uses ulCriticalNesting, yet nothing in portASM.asm seems to actually context-switch it.
Thanks for any help!