MCU Atomic Builtins: Check Lock-Free Width Before Calling Them from an ISR
An atomic expression can compile into a library call. Before using GCC atomic builtins in an interrupt handler, verify the exact operation, object width, alignment and target options in the production build. Then inspect the emitted code and any helper it invokes. “Atomic” describes a concurrency guarantee; it does not by itself establish interrupt suitability or a bounded execution time.
This guide uses the GCC 14.2.0 __atomic interface. The executed probe is for x86_64-linux-gnu on a host, not an MCU. No Arm cross-compiler or target hardware was available. Its purpose is to demonstrate an evidence-gathering procedure that must be repeated with the product’s actual compiler, CPU selection, ABI and runtime libraries.
Define the operation being qualified
Create a row for each atomic operation used in an ISR: load, store, exchange, compare-exchange or arithmetic update. Include the type, byte width, actual object alignment, memory order, interrupt priority and every context that accesses the same object. A successful test of a four-byte load does not qualify an eight-byte increment or a packed structure update.
The GCC 14.2.0 atomic-builtin documentation describes target-dependent instruction generation and external runtime calls when a suitable lock-free sequence is unavailable. It also distinguishes the compile-time __atomic_always_lock_free() query from __atomic_is_lock_free(), which may itself call a runtime routine. Perform capability checks during build or initialization, rather than casually adding a capability query inside the ISR under investigation.

Use the compile-time query as a deliberate build gate
For a design that requires a naturally aligned four-byte lock-free object, this original C11 declaration expresses a useful minimum requirement:
_Static_assert(__atomic_always_lock_free(4, 0),
"four-byte lock-free atomics required");
The zero pointer asks the compiler to use its typical-alignment assumptions. It does not verify the placement of a particular object. For the actual object, check its declaration, any enclosing structure, allocator guarantee and linker location. The optional pointer argument can inform the query about alignment, but GCC documents that a target may ignore that argument. Do not turn a successful query into permission for unaligned accesses.
This assertion was compiled on the host probe configuration only. On an MCU where the requirement is unsupported, a build failure is the desired outcome. The response should be a reviewed design alternative, not deleting the assertion and linking a library until the error disappears.
Inspect both compilation and final linkage
- Place each operation in a small, externally visible function so optimization cannot remove the behavior being examined. Use the same types and memory orders as production.
- Compile with the real target flags and optimization settings. Record compiler version, CPU/architecture options, ABI and whether link-time optimization is enabled.
- Inspect assembly and undefined symbols in the object. Look for atomic helpers, other calls, loops and unexpected accesses to surrounding bytes.
- Inspect the final firmware disassembly and link map too. A helper can be resolved by the runtime library, and link-time optimization can change the path seen in an intermediate object.
- If a helper exists, locate its exact implementation and dependencies. Determine whether it uses locks, masks interrupts, retries, or dispatches to a hardware-specific path.
A helper symbol is a reason to investigate, not proof that every platform uses a mutex. Conversely, absence of a helper does not prove a fixed completion bound. An inline retry loop can be lock-free while one particular invocation repeatedly loses progress. The ISR’s worst-case service requirement must be assessed separately.
Ask what happens if the ISR interrupts its competitor
Consider a hypothetical 32-bit controller whose task and ISR update a shared 64-bit diagnostic total. If the selected implementation requires a software lock that the task holds when interrupted, an ISR that waits for that lock can prevent its owner from running. That is the failure scenario to rule out; this article does not assert that a particular MCU runtime implements that lock.
Review every reachable helper and every nested interrupt relationship. An implementation that is acceptable between threads may not be acceptable when one participant preempts another indefinitely. Likewise, disabling local interrupts is not automatically a complete multiprocessor exclusion mechanism. Keep the proof tied to the actual execution model rather than copying an ISR-safe label from a different port.
If the wide update cannot be qualified, reduce the ISR’s obligation. It may record a narrower native-width event, hand work to a thread through an approved primitive, or use a target-specific snapshot protocol with a separate correctness review. Do not split a wide counter into two ordinary writes and assume the reader obtains an atomic value.
What the executed host probe showed
The fixture compiled acquire loads, release stores and relaxed fetch-add operations for one-, two-, four-, eight- and sixteen-byte integer objects using GCC 14.2.0 with C11 and -O2. The sixteen-byte type used the host-supported unsigned __int128 extension. Naturally aligned test objects reported compile-time and runtime lock-free results of true for widths one through eight, and false for sixteen.
The object file referenced __atomic_load_16, __atomic_store_16 and __atomic_fetch_add_16. Smaller probe operations had no unresolved atomic helper symbols. Linking with the installed atomic runtime and running sequential value checks succeeded. No interrupt or concurrent stress test was performed, and these host width results must not be transplanted into an MCU support table.
Finally, keep ordinary-memory atomics separate from device-register access and DMA visibility. A lock-free CPU operation does not establish a peripheral’s register semantics or cache-coherency contract. The acceptance record should name the exact binary, object placement, helper audit, interruption model and target timing evidence. If any of those is unknown, the ISR path remains unqualified even though the source compiles.