However, interrupt handlers have an unusual execution context, many harsh constraints in time and space, and their intrinsically asynchronous nature makes them notoriously difficult to debug by standard practice (reproducible test cases generally don't exist), thus demanding a specialized skillset—an important subset of system programming—of software engineers who engage at the hardware interrupt layer.
These bugs are sometimes intermittent, with the mishandled edge case not occurring for weeks or months of continuous operation.
In a modern operating system, upon entry the execution context of a hardware interrupt handler is subtle.
In a low-level microcontroller, the chip might lack protection modes and have no memory management unit (MMU).
If the stack is exceeded into a non-writable (or protected) memory area, the failure will usually occur inside the handler itself (generally the easier case to later debug).
It is common to regularly observe corruption of the stack guard with some kind of watch dog mechanism.
This will catch the majority of stack overflow conditions at a point in time close to the offending operation.
Memory resources at this level of microcontroller are typically far less constrained, so that stacks can be allocated with a generous safety margin.
A modern practice has evolved to divide hardware interrupt handlers into front-half and back-half elements.
The front-half (or first level) receives the initial interrupt in the context of the running process, does the minimal work to restore the hardware to a less urgent condition (such as emptying a full receive buffer) and then marks the back-half (or second level) for execution in the near future at the appropriate scheduling priority; once invoked, the back-half operates in its own process context with fewer restrictions and completes the handler's logical operation (such as conveying the newly received data to an operating system data queue).
Reducing the jitter is most important for real-time operating systems, since they must maintain a guarantee that execution of specific code will complete within an agreed amount of time.
These threads sit on a run queue in the operating system until processor time is available for them to perform processing for the interrupt.