Skip to content
Tech News
← Back to articles

Safe Lock-free Primitives with iceoryx2's ByteAtomic

read original more articles
Why This Matters

The article highlights the challenges of implementing safe lock-free primitives in multithreaded systems, especially in safety-critical applications. iceoryx2's ByteAtomic offers a promising approach by enabling atomic operations on byte sequences, reducing the risk of data races and undefined behavior. This advancement is significant for the development of high-reliability, lock-free systems in the tech industry and for consumers relying on robust, concurrent software solutions.

Key Takeaways

Safe Lock-free Primitives with iceoryx2's ByteAtomic

Marika Lehmann - 28/07/2026

In multithreaded programming, a common scenario involves multiple threads reading from and modifying shared data concurrently. If this read and write operations are not atomic, a data race occurs. In languages like Rust and C++, which have almost the same memory model, this results in undefined behavior. To prevent this, locks can be used to protect the data from being modified while it is being read. However, traditional locking mechanisms carry the risk of deadlocks which is unacceptable, especially in safety-critical and high-reliability systems.

A common approach to mitigating the described data race without using blocking locks is to utilize a sequence lock. The sequence lock contains the shared data and an atomic counter that has an odd value whenever the data is being updated:

rust struct SequenceLock < T : Copy + Send > { counter : AtomicUsize , data : UnsafeCell < T >, // Provides the necessary interior mutability for the writer. }

Using a sequence lock, a writer thread increments the sequence counter to an odd value, updates the data, and then increments the counter to an even value. A reader thread reads the sequence counter both before and after copying the shared data. If the counter has changed or is currently odd, it indicates that the data was concurrently modified. The reader then discards the corrupted copy and retries.

The Problem: Even if the reader detects that the data was modified and discards the copy before use, the act of copying the non-atomic data itself still triggers undefined behavior. While a sequence lock can detect that a data race occurred, it does not prevent it. Consequently, it is currently not possible to implement a correct sequence lock in Rust or C++ without decomposing the data into smaller, individually atomic parts. This is a known problem, and while there are ongoing proposals to introduce an "atomic memcpy" 1 2 to the Rust and C++ standard libraries, we cannot rely on that feature yet.

Targeting safety-critical and high-reliability systems, iceoryx2 provides a library of lock-free constructs that are based on mechanisms similar to a sequence lock. To make these constructs safe and correct, we need a way to perform memory copies that are atomic at the byte level, ensuring no data races occur. This is why we implemented the byte-wise atomic wrapper ByteAtomic , which we will describe in the following sections. While its concept is simple, achieving true safety required overcoming a subtle but critical issue with uninitialized memory.

To prevent the aforementioned data race and thus the undefined behavior, the ByteAtomic in iceoryx2 provides byte-wise atomic read and write operations on its inner type. This wrapper only guarantees that each byte is updated/read atomically; it does not provide higher-level thread-safety guarantees. Users must still enforce proper synchronization (such as a sequence lock) to prevent torn reads or writes. The wrapper only ensures that the memory copy is not undefined behavior, but it does not guarantee data integrity on its own.

The wrapper's implementation has undergone some refinement as we addressed the complexities of memory safety. The initial version of our ByteAtomic wrapper looked like this:

... continue reading