I trying to some up with a good solution to over come an issue with my design. I’ve designed a very basic event pool allocator (using C++ templates) to be used by my tasks and each pool has its own lock (therefore I cannot use my allocator within an ISR). Each task has its own queue of base type Event * which allows each tasks to create an event (using my event pool) and submit this to the relevant queues.
However, I also want my ISR routines to also post events on these queues (such as button events). Now, because I cannot use my pool allocator I’ve described (due to using locks), what would be the best solution for this? The naïve solution I can some up with is:
Within my ISR class, have an array of events I would like to post (such as UserButtonEvents) and check to see if space is available to post on the queue. If space is available, pass the address of the element within my array onto the queue and increment accordingly and wrap around.
I could use a queueset but I’m trying to keep my RAM consumption to a minimum (ideally if FreeRTOS has a static queueset, that might help me keep tracking of usage).
Also note, all of my tasks wait on their own queue for events and are only woken up when and event is published. Therefore, I cannot wait on a semaphore for a button event, as I also need to wait on queue for my respective tasks (each tasks needs to reply back to a watchdog tasks).
I’d use the pool also in ISRs and replace the (mutex ?) locks by critical sections. I guess the pool allocator has a lean implementation so the critical section is kept short.
I’d add a FromISR allocator API using taskENTER/EXIT_CRITICAL_FROM_ISR internally, but you could also use a runtime check like xPortIsInsideInterrupt for Cortex-M ports.
Agree with Hartmut although that doesn’t give you the opportunity to block to wait for an event structure to become available if the pool is empty. There are other methods you could use too - but probably all leave you with the challenge of what to do if the ISR cannot obtain an event structure. If it is essential that the ISR obtains a structure then you may want to defer the processing to a task that can block to wait for a structure.
Thanks so much for the replies. FYI I’m using the STM32H7 (M7 Cortex) chip. Thanks Harmut and Richard. This gives me something to think about. More help / ideas would be a real help too. This issue came around when I had to listen for button events (ISR context) as well as receiving watchdog events (application events) and waiting for either event to arrive on the same queue. I’ve written my own event bus and I didn’t take into consideration events generating from ISR routines, such as button events.
I remember a similar issue one of my customers had a number of years back. The solution I came up with was to preallocate events in a “shadow queue” for ISR use; if the app had processed an element and found the ISR’s shadow queue empy or below minimum entry count, it would return the element to the shadow queue instead of the application’s queue. Of course the scheme depended on a statically determinable maximum length of the ISRs shadow queue.
In my applications where using pre-allocated pools or something similar I consider pool exhaustion as error (checked by an assertion) or I accept a possible drop-out. One of the reasons using dedicated pools is guaranteed and deterministic forward progress on allocation. That’s possible with a reasonable estimation of a bounded allocation behavior. Nevertheless waiting for an allocation to proceed from an exhausted pool or heap might be useful for a certain use cases.
Unexpected resource allocation failures are simply bad. Always, everywhere
Thanks for the idea. Good to think somebody else has came across the same issue. This is a personal project of mine (help developing my skills during lockdown) and any guidance will suffice. I’ll have a think and experiment. One thing I will assume, any data sent from the ISR will not be modified by the receiver at all. It’s just a case of the receiver knowing a button has been pressed as well as responding to other events sent from the wider system (i.e. watchdog task).
I have a quick question about this solution. Been thinking about it recently and it seems to be the best solution for my requirements. However, because I’m allocating (marking) and element within my pool for use and posting this on a queue be consumed by my task, I’m not too sure of the following:
De-allocating the event my task has received. Because I cannot mix by application locks with my ISR locks, I’m not too sure of the best approach for to deallocate an event within a task whilst also allocating another event within my ISR. I don’t want to disable all interrupt during the deallocation (although the data sent is only for my user buttons).
I could temporarily disable the ISR which modifies my event pool after allocating and re-enable the interrupt by my task after de-allocating the event?
If allocating and deallocating here are synonyms for dealing with preallocated event pools (which Hartmut and I suggested), then they translate to moving existing events from one FreeRTOS queue to another. Since queue operations are implicitly atomized by criticial sections, you wouldn’t need to do extra work aside from queue boundary checks.