Semaphore behaviour between the thread and an ISR

225 views Asked by At

I have this question lingering in my mind since I’m new to RTOS concepts.

Let’s suppose a task/thread and an ISR are using a semaphore resource. Now Interrupt triggers and ISR tries to acquire a semaphore resource but is used by another task. Does it lead to blocking condition for an ISR? Generally we are not supposed to block an ISR. How is this scenario handled by an OS/RTOS? Are there any solutions/APIs inculcated either FreeRTOS or Linux Kernel?

2

There are 2 answers

2
Clifford On

You should not use blocking calls in an interrupt context. Most RTOS will emit an error (at least in a debug build) and halt or otherwise prevent you from doing that.

The thing is of course that ISR's run to completion. If you block to wait on a semaphore, nothing will run to actually give it. You can only take a semaphore that has previously been given.

If you do take a semaphore in an ISR it must use non-blocking semantics, i.e. immediately return with and indication of whether the semaphore was available or not. Depending on the specific RTOS, that may be a special version of the sem take, or simply require a zero timeout argument. However such a use of a semaphore has no advantage in most cases over a simple shared variable of an atomic type.

That said, interrupts are asynchronous and a semaphore is a synchronisation primitive. It is hard to see a circumstance where you would makes sense to take a semaphore in an ISR. I would question your design if you find yourself doing that. The more likely usage is for the ISR to give (or increment) the semaphore to signal a waiting task.

It is not hard to avoid - the ISR can simply do nothing other than signal a thread context (with a semaphore or event flag), then that thread context can wait on a semaphore to synchronise with some other thread context or other ISR if necessary. That is to say that if handling an interrupt event involves a need to wait on some other context, then the handling should be deferred to a thread context that allows blocking. See for example this on deferred interrupt handling in FreeRTOS.

RTOS's vary in how they handle what APIs can be called from an IST. Some simply require non-blocking (zero timeout) use of standard APIs where others have ISR specific versions. For example FreeRTOS has xSemaphoreTakeFromISR() which does not block. I would not take the presence of such a function as an indication that it makes any sense to use it however! A lot of FreeRTOS is like that ;-)

0
Sparky On

Does it lead to blocking condition for an ISR?.

Short answer--no. Long answer--it's complicated.

Here is what generally happens. If one has an actual need to take a semaphore from an ISR, then the caller should be specify a zero/no-wait timeout. In this scenario, when the semaphore is unavailable, it will immediately fail and return. If a non-zero timeout was specified and the semaphore was not available, then it ought to fail immediately.

Then there is the scenario what if an ISR a non-zero timeout was specified, but the semaphore was available--should that work? That will depend upon the OS. However, in my view it is best if it results in an error as the system ought not reward using a non-zero timeout in an ISR.

But what might happen if the system actually tried to block inside the ISR? My head spins at this as the specifics are going to vary from OS to OS. The short answer is nothing good. The long answer is "it depends". However, one potential scenario is ...

  1. The system would mistakenly think that the interrupted thread is the one that should block.
  2. Register values and stack information will (incorrectly) be saved to that thread's control block.
  3. That interrupted thread will (incorrectly) be marked as blocked and a new thread scheduled in.
  4. Things might even appear to progress normally for a while ... until it does not.

Why might it not? If the hardware requires some kind of special end of interrupt instruction sequence, that never happened because there was bad context switch. This could potentially lead to interrupts never getting serviced again.

What else? If there is a dedicated interrupt stack, that has been hijacked by that previously identified interrupted thread. Any subsequent interrupts are going to further corrupt the stack (and saved registers) that is now being used by that interrupted thread. Crashes can then manifest after that thread gets context switched back in.

Again, no good can come of this.

How can we block inside an ISR? Busy wait/spin until some condition is met. This can be done provided that the condition being tested is fulfilled by something else. On an SMP system, this might be a spin lock--the spin lock is made available by a different CPU. Or, one may be spin waiting for a ready bit to be set by hardware. Either way, there is no context switching being done on the spinning CPU in this scenario.