I’ve found a lot of discussion about the “FromISR” functions in the normal context of using them, that is, from code known to be executing in interrupt context.
I’m dealing with hardware API (specifically, STM’s HAL abstractions) where it’s not always obvious whether a supplied callback function will be called within interrupt context or not. So far I’ve been finding out by chasing runtime asserts when my callback function calls ISR-unsafe FreeRTOS functions.
So the question is this: Is it safe to call the “FromISR” versions of APIs when not in an interrupt context? If my function ends with a portYIELD_FROM_ISR, is it safe to be called in user context? I know there’s the xPortIsInsideInterrupt(); if I can’t be certain about context when my function is called, should I always check that and only use the “FromISR” methods if that check is true? This thread seems to say the wrong one always leads to an error, but I’m not sure I’m reading it correctly.
The FromISR routines assume that they are not subject to being “task switched” away, and thus can be a bit more optimistic about critical sections. (The might be interrupted and need to handle that).
In general, if you are on a processor that doesn’t handle nested interrupts, they are very risky, as for those enviornments the enterCriticalFromISR becomes a no-op, but that won’t be your issue for Arm Cortex-M processors.
Thanks for the extra explanation of “why.” I really appreciate that! I went ahead and wrote the code to check xPortIsInsideInterrupt() and use the normal or FromISR routines as appropriate, and it seems to be working stably.
Please can you go into details about your short note about interrupts that are also interrupted. Say I have an isr ending with portYIELD_FROM_ISR and that is interrupted by another isr also ending with portYIELD_FROM_ISR. What will happen?
That should not be a problem, because what portYIELD_FROM_ISR does is set a marker that will enforce preemption of the current task once the MCU is back in thread context, and there is no harm in setting the marker again if it is already set.
The only issue here is that the results may be unexpected because portYIELD_FROM_ISR is generally submitted by an ISR in the expactation that the preemption will be in favor of a certain task (typically the one that was signalled to run by the ISR) which in the case of nested task signals may not be what happens. If two nested ISRs both request preemption after having signalled their respective tasks, the lower priority of the two tasks may suffer from degraded scheduling performance (as the preemption flag will not be renewed).
Note, portYEILD_FROM_ISR doesn’t always just set an interrupt marker, it depends on the specific processor port. (that is its behavior on a lot of systems, including the Arm-Cortex systems.)
Some systems, typically because they don’t have an appropriate interrupt to use) do the actual task scheduling in the ISR within the function called. These systems often don’t support interrupt nesting, or it might be totally blocked during the scheduler operation.
If your code might run on one of those machines, it becomes important to have the portYEILD_FROM_ISR call at the VERY END of the ISR, as code after it might (will) be delayed (on such systems) until the switched out task gets switched back in.
Thanks for this interesting topic.
I only have worked with the STM32, and specifically the HAL, for less than one year and have a love/hate relation ship with it.
The HAL works fine when not using FreeRTOS but as soon as FreeRTOS is used, I have some problems with it. As far as I have seen, callbacks are always executed in interrupt context or not - depending on how the callback is initiated.
I have not yet seen callbacks that are called from either task- or interrupt context.
Do you have examples of this ?
I still love the idea of the xPortIsInsideInterrupt() function. This could be useful for my own functions that can be called both from interrupt and task context.
It’s not so much that I’ve seen a single HAL callback occur in both contexts, but that I’m never sure what the context for a particular one will be (until it becomes obvious). Maybe it’s documented somewhere, but I haven’t found it.
Love/hate – you and me both. Very useful API for doing “ordinary” things, but quickly becomes more trouble than it’s worth if you need to get clever.
Since STM-HAL was part of the original message, let me elaborate on that a bit more.
Some of the smaller STM32 chips have shared interrupts so ST decided to have one general interrupt handler per peripheral (or so it seems).
For the H7 that I am currently using, all UARTs have separate interrupts that call one global interrupt handler with a pointer to a structure to point to a specific peripheral and settings.
The HAL disables the interrupt so I have to re-enable it to keep the interrupt firing.
I noted that the interrupt takes ~ 1200 CPU clock cycles when using the HAL interrupt. Optimizing my own interrupt by stripping it down to what I need now only takes ~ 100 cycles. So I tend to start using the HAL framework to validate my application and then replace it with my own functions.
Another strange thing is that the HAL has some knowledge about an RTOS. It has a HAL_LOCK (which is not thread safe by the way) and still uses the HAL_Delay() that just loops without giving control back to the RTOS. So using FreeRTOS in combination with the STM HAL is not an optimal solution
I tend to agree with the final sentence. One of the issues that I find most aggrevating is that the ST HAL installs a timer ISR of its own for the sole purpose of implementing HAL timing issues such as timeouting functions. For practically all my customers, this led to serious up to insolvable maintenance problems because it interfers with the target system architecture on many levels. Consequently, I see very sparse use of the HAL in production systems.