In this lesson, you will learn how to safely share data between high-priority interrupt handlers and low-priority RTOS tasks using specialized synchronization primitives.
When a task holds a standard mutex, it relies on the RTOS scheduler to manage blocking and unblocking. However, Interrupt Service Routines (ISRs) cannot "block"—if an ISR attempts to wait for a mutex that a task currently holds, the system will crash or dead-lock, as the scheduler cannot switch context away from an interrupt. Conversely, if a task is interrupted while holding a resource, the ISR might attempt to access that resource in an inconsistent state, leading to race conditions.
To bridge this gap, we use Interrupt-Safe Mutexes (or often, critical sections combined with semaphore signaling). Since an ISR cannot sleep, the synchronization mechanism must ensure the ISR can either bypass the lock, check for availability without waiting, or defer the resource access.
The Mechanism
The safest approach is to treat the interrupt as a "producer" and the task as a "consumer" using a try-lock pattern. If the ISR needs to modify a buffer shared with a task, it shouldn't try to "own" the mutex; instead, it should use a non-blocking operation or a hardware-level spinlock (if the architecture supports it) to protect the memory briefly.
// ISR accessing a shared buffer
void UART_RX_ISR() {
if (TryLock_InterruptSafe(&shared_buffer_lock)) {
buffer[head++] = read_hardware();
Unlock_InterruptSafe(&shared_buffer_lock);
} else {
// Drop data or flag an overflow error
}
}
In the application task, you must lock the resource using the same InterruptSafe mechanism. Because the task can be preempted by the ISR, the InterruptSafe lock implementation typically performs a hardware-level operation, such as disabling interrupts globally for a few clock cycles or using atomic load-store instructions to ensure that the task-level lock and ISR-level access are mutually exclusive.
By using these specialized primitives, you ensure that the task never holds the lock in a way that prevents the ISR from executing, and the ISR never inadvertently blocks the CPU.
Key Takeaway: Interrupt-safe mutexes allow tasks and ISRs to share resources by replacing traditional scheduler-based blocking with non-blocking, atomic synchronization methods that respect the execution constraints of interrupt contexts.