Interrupt safe code
Skill Amey-Thakur/AI-SKILLS/skills/embedded-iot/interrupt-safe-code
Write ISRs that stay minimal, share data through volatile-correct handoffs, and defer real work safely. Use when writing interrupt handlers or debugging corruption that only happens sometimes.From its SKILL.md
npx -y skills add Amey-Thakur/AI-SKILLS --skill interrupt-safe-codeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 25 days oldThe repository was created 25 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
3.6 KB, 797 tokens by cl100k_base, as published. Nobody here has run it
Interrupt-safe code
An interrupt can fire between any two instructions. Every shared byte between ISR and main context is a race unless the handoff is designed; "only happens sometimes" corruption is almost always this.
Method
- Keep ISRs to a few microseconds. Read the hardware, clear the flag, stash the data, set a signal, return: no loops over unbounded data, no floating point (context-save costs), no allocation, no logging, and never a blocking call. Everything else defers to task context (the "top half / bottom half" split; see rtos-task-design for the receiving side).
- Hand off through single-writer structures. The canonical ISR-to-main pipe is a lock-free single-producer single-consumer ring buffer: ISR writes head, main reads tail, each index written by exactly one side, sized power-of-two. For scalars: write the value, then set a flag; reader clears flag, then reads (order matters: the flag is the release). Multi-word data needs the critical-section route instead: torn reads of a 32-bit value on an 8/16-bit MCU are real.
- Use volatile for what it is. Every variable shared with an
ISR (and every hardware register) is
volatile: it forces the compiler to actually load/store. It does not provide atomicity or ordering between variables (see jvm-memory-model for the same trap at higher altitude); where the ISA reorders or buffers (Cortex-M DMB cases, DMA visibility), add the platform's barriers explicitly. - Bound critical sections; prefer them rare. Where main context must touch multi-word shared state: disable the specific interrupt (or use priority masking, BASEPRI-style) for the handful of cycles needed, saving/restoring prior state so nesting works; measure the worst-case masked window against your latency budget (a masked UART at 921600 baud drops bytes fast). Redesign toward the SPSC handoff whenever a critical section grows.
- Respect priority and nesting design. Assign priorities
from real deadlines (motor fault above telemetry), know
which ISRs can preempt which, and keep shared-data analysis
per priority pair; on RTOSes, only the ISR-safe API variants
(
...FromISR) may be called, with the deferred-wakeup pattern (give semaphore/queue, request context switch on exit; see rtos-task-design's priority-inversion notes). - Verify with the ugly tests. Stress at maximum interrupt rate while main context hammers the shared paths; assert ring-buffer invariants and overflow counters (dropped events must be counted, not silent: see embedded-memory-constraints' exhaustion rule); measure ISR duration and max latency with a scope/logic analyzer on a GPIO toggle (see embedded-debugging): the only honest profiler at this level.
Boundaries
- This is single-core discipline; multi-core MCUs add true concurrency where volatile-plus-masking is insufficient: use the platform's spinlocks/atomics and memory model docs.
- DMA is a second "interrupt context" writing memory behind the compiler's back: buffers shared with DMA need the same volatile/barrier/cache-maintenance care (plus cache invalidation on cores with data cache).
- If most logic is migrating into ISRs for latency, the design wants a faster main loop or an RTOS with proper priorities, not bigger handlers (see rtos-task-design).
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.