Compare and swap C0x
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Compare-and-swap, usually abbreviated as CAS, is a core atomic primitive for lock-free programming in C++11 and later. It lets a thread update shared state only if that state still equals an expected value. In modern C++ this is exposed through std::atomic and compare_exchange_weak or compare_exchange_strong.
CAS Semantics in C++11
CAS compares an atomic value with expected. If they match, it writes desired atomically and returns true. If they do not match, it writes the current atomic value back into expected and returns false.
That second behavior matters. Many bugs come from forgetting that expected is modified on failure.
Basic example:
On success, value becomes 42. On failure, expected becomes the observed value.
weak Versus strong
compare_exchange_strong fails only on genuine mismatch. compare_exchange_weak may fail spuriously, which means it can return false even when values match. Spurious failure is allowed so some architectures can generate faster instructions.
Practical rule:
- use
strongwhen attempting once - use
weakin retry loops
Typical weak loop pattern:
The loop retries until update succeeds.
Memory Ordering and CAS
CAS operations support memory order arguments. This controls visibility guarantees between threads. Many lock-free bugs come from incorrect memory ordering choices.
Common pair:
- success:
memory_order_releaseormemory_order_acq_rel - failure:
memory_order_relaxedormemory_order_acquire
For beginners, memory_order_seq_cst is simpler and safer, though potentially slower.
Use explicit orderings only when you understand the synchronization graph you need.
Lock-Free Stack Push Example
CAS is often used to update linked-list heads in lock-free structures.
This demonstrates the classic retry pattern, though full production code must also handle safe memory reclamation.
Real-World Concerns
CAS is powerful but not a universal replacement for mutexes. Under heavy contention, constant retries can waste CPU and reduce throughput. Lock-based approaches can be better for fairness or complex multi-step critical sections.
Another concern is ABA problems in pointer-based lock-free structures. A value can change from A to B and back to A, causing CAS to succeed even though intermediate state existed. Techniques such as tagged pointers or hazard pointers are used to mitigate this.
Common Pitfalls
- Forgetting that
expectedis overwritten when CAS fails. - Using
compare_exchange_weakonce without a retry loop. - Choosing memory orders inconsistently across related loads and stores.
- Assuming lock-free code is always faster under high contention.
- Ignoring ABA and reclamation issues in pointer-based designs.
Summary
- CAS atomically updates a value only when expected and current values match.
strongis for single-attempt semantics,weakis for retry loops.- Memory ordering is part of correctness, not only performance.
- CAS enables lock-free patterns but adds complexity around contention and reclamation.
- Use CAS intentionally where its trade-offs are justified.

