Zephyr Meta-IRQ Threads: Review the Cooperative-Code Assumptions They Can Break
A Zephyr meta-IRQ thread can preempt lower-priority ordinary threads even when they are cooperative or hold a scheduler lock. Before enabling that priority class, audit code that treats “another thread cannot run here” as its shared-state protection. Meta-IRQ work remains thread-context work, but its scheduling contract is stronger than an ordinary high-priority worker’s.
This guide uses Zephyr v3.7.0 on one CPU with meta-IRQ priorities configured. It does not prescribe Cortex-M hardware interrupt priority numbers, claim SMP exclusion or substitute for each API’s context rules. No target interrupt or scheduling tests were executed.
Map execution context and preemption separately
The 3.7.0 thread documentation describes meta-IRQ as a special subclass at the highest end of the cooperative-priority range. Such threads can preempt lower-priority cooperative and scheduler-locked threads. They still use their own thread stacks and can themselves be interrupted by hardware interrupts.
The distinction matters because “not a hardware ISR” and “cannot interrupt this thread’s update” are different statements. A function’s legality follows its documented execution-context requirements; the application’s consistency proof follows who can run between its operations.
| Context | Important boundary | Review consequence |
|---|---|---|
| Hardware ISR | Interrupt-context API restrictions apply | Use only operations documented for that ISR context |
| Meta-IRQ thread | Can run ahead of lower-priority cooperative or scheduler-locked code | Audit shared-state assumptions and bounded work |
| Ordinary cooperative thread | Its usual nonpreemption assumption has this exception | Cooperative execution alone cannot exclude meta-IRQ access |
| Scheduler-locked ordinary thread | Scheduler lock does not exclude meta-IRQ preemption | Do not use that lock as the entire cross-context data protocol |

Inspect the effective priority classification
The tagged kernel configuration defines CONFIG_NUM_METAIRQ_PRIORITIES as a reserved set at the numerically lowest end of the priority space. Review every thread’s actual priority against that range. A numeric priority that previously meant ordinary cooperative execution can have a different classification after the configuration changes.
Build a table of thread name, creation site, initial priority, runtime priority changes, ordinary/meta-IRQ class and accessed objects. Include library-created workers and driver subsystems. Do not limit the audit to the one new thread whose feature motivated the change.
The documentation also warns about immediate-start static meta-IRQ threads affecting initialization ordering. Establish which dependencies exist before the worker becomes runnable. A handler that executes earlier than expected can see uninitialized state even if the steady-state ownership protocol is sound.
Find data protected only by scheduling assumptions
Search for multi-field updates surrounded by scheduler locking, operations documented as safe because “all callers are cooperative,” and helper functions that assume they cannot be reentered. Include diagnostic snapshots, list manipulation, buffer publication and object retirement.
For each object, identify the exact point where its invariant temporarily stops holding. For example, an update may change a count before replacing the corresponding pointer. If a meta-IRQ reader can execute between those writes, reading the pair can be invalid even though both individual loads are naturally sized.
A volatile qualifier does not make the pair one transaction. Likewise, moving the same code to a meta-IRQ thread does not make a shared buffer immutable or stop DMA from accessing it. Separate data ownership, publication ordering and device coherence rather than treating priority as a substitute for all three.
Qualify the wakeup path as part of the operation
The thread guide explains that waking a meta-IRQ thread from lower-priority thread context can behave like a synchronous handoff; after an ISR wake, the meta-IRQ work runs before the current CPU returns to ordinary application code. Plan for that ordering instead of assuming the producer continues immediately after its wake operation.
Trace the complete path: event capture, publication of owned data, operation making the meta-IRQ thread runnable, worker execution and return to interrupted work. A timestamp on the wake call alone does not establish when application processing resumed.
Keep the worker’s execution and any blocking dependencies bounded by an explicit service requirement. Although it is a thread, long work at this scheduling position can postpone a large part of the application. Audit each API by its actual context and contract; do not mechanically switch all calls to ISR variants because the worker’s name contains “IRQ.”
Use a controlled shared-state test
- Select a representative object whose update previously relied on cooperative execution or scheduler locking.
- Add test-only gates around the point where its invariant is temporarily incomplete, without changing the intended synchronization mechanism.
- Trigger a supported wake path for the meta-IRQ reader at that point. Capture updater identity, worker identity and the fields observed.
- Repeat with the proposed corrected ownership or publication protocol. Require the reader to see a valid old or new state, or to defer access explicitly.
- Test initialization, sustained event bursts and retirement while a wake is pending. Remove the gates from production and retain the test configuration.
These cases are proposed, not measured results. Use an isolated target and a bounded trace so instrumentation does not add an accidental blocking boundary. A passing ordinary cooperative run is a control; it does not cover the enabled meta-IRQ configuration.
Hypothetical example: a deferred reader sees half an update
Imagine a hypothetical sensor service updating a buffer descriptor while its ordinary thread holds the scheduler lock. A hardware interrupt wakes a meta-IRQ worker that reads that descriptor. The worker can execute before the ordinary update completes and observe mismatched fields.
A repair might give the descriptor one owner and hand off a completed immutable snapshot, or use synchronization proven appropriate for all participating contexts. The correct choice depends on latency and lifetime constraints. Broadly disabling interrupts around a lengthy update is not an automatic replacement for the missing design.
Release evidence
Deliver the priority-class map, shared-object invariant audit, wake-path trace and bounded-work requirements. Require a documented reason for meta-IRQ use, including what ordinary threading could not provide. Recheck when changing the reserved priority count or adding callbacks. The acceptance question is whether the exceptional preemption is safe and useful for this product, not merely whether its worker appears responsive in a short demo.