Mutex locks are discouraged within the scope of ISRs mainly because of the possibility of blocking the ISR in case the lock has already been acquired by another task for instance.
Out of curiosity though, is there ever really a need to use locks inside an ISR? Say you would buffer a FIFO inside the ISR which shall happen atomically assuming the scheduler isn’t running during this operation.
Mutex’s can’t work in an ISR. PERIOD. As a Mutex is ‘owned’ by the task that took it.
ISRs do use ‘locks’ of a form, and FreeRTOS itself build ‘critical sections’ inside many of the ‘FromISR’ functions when manipulating structures.
The key is that if you want to lock out between a task and an ISR, you can’t use something like a Mutex, where the second request ‘blocks’. as an ISR can’t block an let the program continue and then resume the ISR, but both sides need to use a critical section so the ISR can’t interrupt the code that needs the locking. This is a fairly ‘blunt’ tool, as it blocks ALL ISRs, not just the ones that might contend for the lock.
It is also possible to use a softer lock, (which could be a semaphore) where the ISR tries to take the lock, and if it fails, it sets up flags somewhere so that when whoever has the lock and releases it, it can see that some other request came in and possibly do the deferred action, or the ISR might do something else to mark the deferred operation. This is what happens if you suspend the scheduler, and an ISR wants to wake a task, since the scheduler is suspended, it can’t directly wake the task, so it adds the task to a list of tasks to wake up, and when the scheduler is resumed, it checks if there are any tasks that need to be woken before returning.
as an ISR can’t block an let the program continue and then resume the ISR,
it’s possible but technically not the way to do it since ISRs aren’t supposed to block hence locks shouldn’t be used as opposed to can’t be used inside ISR?
What good is getting a lock if nothing can block to wait for it to unlocK?
And no, it is NOT possible for an ISR to block and return to the thing it interrupted and then get unblocked when the lock is released, at least not with the execution model that FreeRTOS is using.
As I said, the equivalent to what you are trying to do is what a critical section does, which is sort of a GLOBAL lock (but works differently).
Actually I did imagine lock being unnecessary in the first place since no one else would be accessing the shared resource while the ISR is running unless there’s a nested interrupt that happens to access the same resource (which isn’t the case for me)
And no, it is NOT possible for an ISR to block and return to the thing it interrupted and then get unblocked when the lock is released
I understand but I was referring to the case mentioned in my code snippet where ISR accesses a shared resource within a lock. How would the ISR exit without unlocking it (basing it off my snippet)?
The code snippet you provided is using taskENTER_CRITICAL_FROM_ISR which disables the interrupts up till configMAX_SYSCALL_INTERRUPT_PRIORITY so a shared resource can be accessed without any interference by the scheduler, yes?
Yes, by using the critical section multiple code sections can access the shared resource without contention because inside the critical section, no other code can interrupt the flow of execution.
well, strictly speaking, the crit section and configMAX_SYSCALL_INTERRUPT_PRIORITY are not of primary concern here because shared buffers could be corrupted by two ISRs on different priority levels of which one interrupts the other, regardless of whether there is an RTOS involved or not. Or this coulds affect ISRs above MAX_SYSCALL. Both cases are fairly academic, though.
But that’s splitting hairs in two; Richard is absolutely right about the crucial difference between ISRs and tasks (which are the only type of execution threads for which the concept of mutual exclusion or locking makes sense).
The important point to understand is that OS-controlled tasks and ISRs are fundamentally different from each other in the way they are arbitrated. Try to understand that, Furx, and everything else will fall into place.
Oh yes, and one more thing: You (Furx) may want to look into the exclusive access monitor architecture (ldrex and strex instructions) on Cortex M based architectures. Those may actually accomplish exactly what you want to do (I haven’t checked recently if the mechanism works in ISRs, though). In a nutshell, those are store instructions that will always execute (thus never suspend or delay a thread of execution) but may fail on the kernel level because another claimant must release the memory location first. Success or failure of the store is recorded in a processor register. Pretty neat stuff!
shared buffers could be corrupted by two ISRs on different priority levels of which one interrupts the other, regardless of whether there is an RTOS involved or not. Or this coulds affect ISRs above MAX_SYSCALL. Both cases are fairly academic, though.
that’s true, but then how are you protecting the shared resource within ISR context if not through mutex?
Regarding configMAX_SYSCALL_INTERRUPT_PRIORITY, the idea really is the interrupts of higher priority shouldn’t use fromISR() APIs in their respective ISRs to prevent kernel objects from getting corrupted whereas the ISRs with interrupt priority < configMAX_SYSCALL_INTERRUPT_PRIORITY could use fromISR() within them since it’d be guaranteed for said ISRs not to fire as they’re disabled.
through interrupt INHIBITION: A lower pri ISR that executes a code section not to be interrupted by some code in a higher pri ISR must disable the ENTIRE interrupt. Of course that’s not very efficient as the higher pri interrupt will typically execute only few of its instructions that may conflict with the lower Pri interrupt’s “critical” code, but there is no way (aside from the unblocking calls Richard mentioned or things like the Cortex’s EAM, which is something eimilar, but on the MCU level, not the OS level) to selectively prevent higher Pri ISRs code from executing. Thus we must prevent all of the ISR code from running.
The FreeRTOS critical section is a specific case of interrupt inhibition; it blocks all ISRs up to configMAXSyscall levels, ie all OS compliant interrupts. That may not be what you want, as outlined beofre.
On some MCUs it’s also possible to only inhibit the interrupt(s) affected by the “mutual exclusion.”
so basically you’re making use of taskENTER_CRITICAL_FROM_ISR to disable the interrupts within ISR? if not, how else are you disabling the ENTIRE interrupt so it doesn’t get preempted by any high prio interrupt?
yes, that is the OS supported generic way to do that. However, as mentioned before: if the “isr mutual exclusion” involves an ISR that executes above maySYSCALL priority, you’ll need to revert to MCU sprcific inhibition mechanisms.
MCU specific inhibition mechanisms like? disabling the entire interrupt (or another that uses the same resources as the currently running one) within an ISR to ensure it’s not interrupted or preempted?