This is more kind of a suggestion that I’m looking for.
So, freeRTOS has two flavors for most of the functions. functionISR() and function() is there a way we can unify. I looking to understand if it is possible to design a unified interface. Essentially I want to use a same function from both the interrupt and normal code.
Some ports provide xPortIsInsideInterrupt() and I’ve seen code using it to distinguish the execution context resp. using the appropriate FreeRTOS/FromISR API (including the trailing portYIELD_FROM_ISR() used by some ports).
Such an abstraction won’t work in any case especially you can’t use blocking API calls the same way as in tasks because ISRs can’t and shouldn’t block or wait.
This topic has been discussed in the forum a number of times. See e.g. this post for some further details.
As Hartmut implies, one BIG issue with trying to make a ‘unified’ routine is that what is available to do in that routine is severely limited. Normally, in a task level routine, if something is not available at the moment, you want to block for a bit to see if it come available, but this option isn’t available for an ISR, so wouldn’t be to a unified routine. Also ISR versions need to manually run the scheduler if needed, while task level routine would generally want to have that happen as soon as possible.
FreeRTOS makes the routines separate because it makes each of them more efficient, and it really is rare that a given subroutine that does anything significant is really proper to be done either at task level or ISR level. If you think it is, think carefully if you are trying to do too much in the ISR.
What I’m really trying here is to see if I can abstract away the FreeRTOS kernel calls with an interface. The problem I see is that there are many places where I must be able to send at least a notification that would notify the application. FreeRTOS is asking me to add two APIs for a single functionality to the interface, one for interrupt and one for the system.
It doesn’t look pretty. So, I was checking if there is a way to unify them with just a little impact on the kernel. But I see that is not the case.
Fortunately in computers we don’t primarily go for looks (anyways, beauty is in the eyes of the beholder of the mutex, as the saying goes… ).
It’s a pity you don’t seem to see the issues brought forward by Hartmut and the two Richards. Active (giving) notifications are among the very few system elements that are candidates for unifications in the first place (most other functionalities that have …FromISR() options are subject to potential blocking when called from tasks), and as Richard Barry points out, in ports that support in-ISR detections, you can realize the unification here with zero kernel impact.
The other issue not listed here yet is performance, of course. In ISRs every CPU cycle we can save potentially counts; thus, a unified API call that may waste code for in-ISR detection even if we already know that we are in in ISR wastes unnecessary cycles. The flexibility that FreeRTOS offers to optimize for performance while still maintaining high portability its one its greatest assets.
The duplication of APIs (the “regular” and the “FromISR” varieties) is the weakest aspect of FreeRTOS design and the biggest hindrance for its extensibility. The “performance” and “flexibility” arguments in the aforementioned FAQ are, of course, exaggerated. In fact, the need for the “pxHigherPriorityTaskWoken” parameter in the “FromISR” variety, which then needs to be dragged through layers of calls defeats much of the “performance”. Other RTOSes somehow manage to offer a single, unified API, so it obviously is doable.
A good case in point is the popular FreeRTOS-ESP32 adaptation provided by Espressif. They apparently nixed the “FromISR” variety. But by doing so they have created a different RTOS, incompatible really with FreeRTOS (even though they still call it so…)
The duplication of APIs creates hazards of using inappropriate APIs in a given context, and there is no help from the FreeRTOS to detect or assert that the wrong API is being used. For example, many callbacks run in the ISR context, yet people often use the “regular” APIs in those callbacks.
The duplication of APIs creates big problems for providing higher-level services on top of FreeRTOS. It is simply not possible to hide the duplication, but rather it must be exposed to the highest levels, all the way to the application. For example, I provide an event-driven, active object framework called QP that can run on top of traditional RTOSes. The QP port to FreeRTOS is many times bigger than all other ports to RTOSes like embOS, ThreadX, uC/OS-II, etc., because I have to provide “FromISR” variety for most services, such as: event posting, publishing, or even allocating an event from a pool. This is because all these services can be called from the ISRs.
So, I’d like to join all the other developers in a plea to please reconsider the current design and provide a single, clean, unified API. I really hope that this makes sense to everybody.
I will say that I agree with the decision of FreeRTOS to have separate APIs for ISR and Task contexts for notifications. The big issue seems people write too much code in an ISR, there shouldn’t be many layers of code in the ISR. This seems particularly true when they grab drivers not meant for FreeRTOS and just try to hack them into it.
A second issue I see is that people trying to use a unified API tend to ignore error recovery, as that WILL be very dependent on that context, so it just isn’t done.
The ISR context is special and has constraints and restrictions in all OSs I had to deal with. I can’t see that this can be abstracted away because it’s fundamentally different to task context mainly because ISRs can’t block.
I find it helpful that the calls possible in ISRs are distinguished by their names/API. Otherwise an extra documentation of the restrictions (in ISR context) would be needed. This is often more error prone due to misunderstandings or missing to carefully read the docs.
I afraid there is no silver bullet
Well Miro, this is a fairly obvious and cheap pitch for your product.
I do not see that you have addressed any arguments brought forward. As long as there is only thin air and no argument on your side, there is nothing to debate.
Well alright, then let’s settle on this: It might be called an error to give similar names to functions that don’t exactly do the same thing. Fair enough, define yourself wrappers that disambiguate the names, and everyone will be happy, right?
Like many here, I have come to appreciate the FreeRTOS philosophy. Of course it has pros and cons like every architecture, but I believe that the pros outweigh the cons. Of course you are entitled to disagree, but when you attempt to generate pressure for something wrong, my reaction can only be to ask developers to weigh the pros and cons for themselves based on facts. I’m sure that most who do that will come to the conclusion that FreeRTOS’s way of implementing things has more benefits than drawbacks.
I’m merely pleading for making my life and, apparently other FreeRTOS users, simpler and a safer, because the current API is unsafe. I don’t see how this is wrong. My assumption is that this forum is about making the FreeRTOS better…
I see the current API as safer than an API that implies that an ISR can block.
Yes, there are a few functions that send notifications that will never block that might be able to be combined, but if you DO want an xSemaphoreGive that can be used in an ISR context or not, then you are perfectly able to make one you just need to detect that you are in an ISR, which is doable on most platform, and, since you are likely going to want some sort of framework around your ISR so you don’t need to pass that wasWoken flag, that framework can provide that flag if the hardware can’t.
That overhead of that framework is PRECISELY why FreeRTOS doesn’t do it that way, in FreeRTOS ISR have very little extraneous overhead needed except for what is REALLY needed to run the ISR. They don’t need to pay any overhead for the system to check if it is in an ISR, or added ISR overhead to record that it is in an ISR.
Naturally there are pros and cons to most design decisions, and what is best depends on the use case. So not going to offer opinion, but I would like to correct a couple of things:
That is very dependent on the use case. The other extreme is the conversations I have had with people who request two assembly instructions are swapper around to save a processor cycle because it matters to them. There is a balance between performance and portability (and usability, and code size, etc.) of course.
Additionally the “HigherPriorityTaskWoken” mechanism, which leaves it to the application developer to decide if a context switch should happen immediately or only when an entire transaction is complete, can add significant performance improvements and gives a lot more design flexibility. Context switching once when a message has arrived saves a lot compared to context switching each time a character in the message arrives without having to instead use a different task priority to prevent the latter.
FreeRTOS does assert if you call a blocking API from an interrupt because that is not safe. It does not however assert if you call the ‘FromISR’ version from a task because that is safe and often used as an optimisation - although that is not recommended unless you are an experienced developer and FreeRTOS user.
I’ve seen it done using wrappers that check if you are inside an interrupt or not before calling the appropriate function. That one test is more efficient that having multiple tests within a single handler.
Actually, I thought that the FromISR versions were NOT always safe to use at task level, in particular, for ports that do no allow for nested interrupt, they could only be used inside a critical section, as the ISR critical section code was a no-op (as not needed) and thus you still needed to protect the code from being interrupted.
That is correct - my reply was actually specific to ports that support interrupt nesting. That is very much the majority of ports that get used in production devices these days as those devices tend to use modern 32-bit MCUs. Was trying not to overcomplicate my answer.