This library aims to implement an equivalent to the C11 atomics library. It's intended to be used as a way to enable the use of atomics in a mostly consistent manner to modern C, while still enabling compatibility with older compilers. This is not a drop-in replacement for C11 atomics, but is very similar. Only limited testing has been done so use at your own risk. I'm happy to accept feedback and pull requests with bug fixes.
The following compilers are supported:
- Visual Studio from VC6 (Earlier versions may work, but are untested.)
- GCC starting from 2.7 (Earlier versions lack support for extended inline assembly.)
- Clang
- Intel oneAPI (Tested with 2025.0.4. Intel C++ Compiler Classic has not been tested.)
- TinyCC/TCC (Tested with 0.9.27)
- OpenWatcom (Tested with 2.0)
- Digital Mars
- Borland C++ (Tested with 5.02)
New compilers will use intrinsics. GCC-likes, such as GCC and Clang, will use __atomic_*
intrinsics through a
pre-processor define and should have no overhead. This uses __GNUC__
to detect GCC-likes.
Old compilers, or compilers lacking support for intrinsics, will use inlined assembly. There are two inlined assembly paths: GCC-style (GAS syntax) and MSVC-style. For an old compiler to be supported, it must support one of these two paths. Note that only 32- and 64-bit x86 is supported for inlined assembly. I have not thouroughly tested the inlined assembly paths. It passes basics tests, but things like memory ordering may have some issues. Advice welcome on how to improve this.
In most cases you can use this as a header-only library. Just add c89atomic.h
to your source tree and
include it:
#include "c89atomic.h"
Everything is implemented with defines and inline functions. In some cases there may not be native hardware
support for a given atomic operation in which case the library will emulate it with a global spinlock. If
you get errors about c89atomic_global_lock
being undefined, you need to integrate c89atomic.c into your
build. This will not usually be required on modern compilers.
For practicality, this is not a drop-in replacement for C11's stdatomic.h
. Below are the main differences
between c89atomic and stdatomic.
- All operations require an explicit size which is specified by the name of the function, and only 8-, 16-, 32- and 64-bit operations are supported. Objects of arbitrary sizes are not supported.
- All APIs are namespaced with
c89
. c89atomic_*
data types are undecorated (there is no_Atomic
decoration).
The following types and functions are implemented:
+-----------------------------------------+-----------------------------------------------+
| C11 Atomics | C89 Atomics |
+-----------------------------------------+-----------------------------------------------+
| #include <stdatomic.h> | #include "c89atomic.h" |
+-----------------------------------------+-----------------------------------------------+
| memory_order | c89atomic_memory_order |
| memory_order_relaxed | c89atomic_memory_order_relaxed |
| memory_order_consume | c89atomic_memory_order_consume |
| memory_order_acquire | c89atomic_memory_order_acquire |
| memory_order_release | c89atomic_memory_order_release |
| memory_order_acq_rel | c89atomic_memory_order_acq_rel |
| memory_order_seq_cst | c89atomic_memory_order_seq_cst |
+-----------------------------------------+-----------------------------------------------+
| atomic_flag | c89atomic_flag |
| atomic_bool | c89atomic_bool |
| atomic_int8 | c89atomic_int8 |
| atomic_uint8 | c89atomic_uint8 |
| atomic_int16 | c89atomic_int16 |
| atomic_uint16 | c89atomic_uint16 |
| atomic_int32 | c89atomic_int32 |
| atomic_uint32 | c89atomic_uint32 |
| atomic_int64 | c89atomic_int64 |
| atomic_uint64 | c89atomic_uint64 |
+-----------------------------------------+-----------------------------------------------+
| atomic_flag_test_and_set | c89atomic_flag_test_and_set |
| atomic_flag_test_and_set_explicit | c89atomic_flag_test_and_set_explicit |
+-----------------------------------------+-----------------------------------------------+
| atomic_flag_clear | c89atomic_flag_clear |
| atomic_flag_clear_explicit | c89atomic_flag_clear_explicit |
+-----------------------------------------+-----------------------------------------------+
| atomic_store | c89atomic_store_8 |
| atomic_store_explicit | c89atomic_store_16 |
| | c89atomic_store_32 |
| | c89atomic_store_64 |
| | c89atomic_store_explicit_8 |
| | c89atomic_store_explicit_16 |
| | c89atomic_store_explicit_32 |
| | c89atomic_store_explicit_64 |
+-----------------------------------------+-----------------------------------------------+
| atomic_load | c89atomic_load_8 |
| atomic_load_explicit | c89atomic_load_16 |
| | c89atomic_load_32 |
| | c89atomic_load_64 |
| | c89atomic_load_explicit_8 |
| | c89atomic_load_explicit_16 |
| | c89atomic_load_explicit_32 |
| | c89atomic_load_explicit_64 |
+-----------------------------------------+-----------------------------------------------+
| atomic_exchange | c89atomic_exchange_8 |
| atomic_exchange_explicit | c89atomic_exchange_16 |
| | c89atomic_exchange_32 |
| | c89atomic_exchange_64 |
| | c89atomic_exchange_explicit_8 |
| | c89atomic_exchange_explicit_16 |
| | c89atomic_exchange_explicit_32 |
| | c89atomic_exchange_explicit_64 |
+-----------------------------------------+-----------------------------------------------+
| atomic_compare_exchange_weak | c89atomic_compare_exchange_weak_8 |
| atomic_compare_exchange_weak_explicit | c89atomic_compare_exchange_weak_16 |
| atomic_compare_exchange_strong | c89atomic_compare_exchange_weak_32 |
| atomic_compare_exchange_strong_explicit | c89atomic_compare_exchange_weak_64 |
| | c89atomic_compare_exchange_weak_explicit_8 |
| | c89atomic_compare_exchange_weak_explicit_16 |
| | c89atomic_compare_exchange_weak_explicit_32 |
| | c89atomic_compare_exchange_weak_explicit_64 |
| | c89atomic_compare_exchange_strong_8 |
| | c89atomic_compare_exchange_strong_16 |
| | c89atomic_compare_exchange_strong_32 |
| | c89atomic_compare_exchange_strong_64 |
| | c89atomic_compare_exchange_strong_explicit_8 |
| | c89atomic_compare_exchange_strong_explicit_16 |
| | c89atomic_compare_exchange_strong_explicit_32 |
| | c89atomic_compare_exchange_strong_explicit_64 |
+-----------------------------------------+-----------------------------------------------+
| atomic_fetch_add | c89atomic_fetch_add_8 |
| atomic_fetch_add_explicit | c89atomic_fetch_add_16 |
| | c89atomic_fetch_add_32 |
| | c89atomic_fetch_add_64 |
| | c89atomic_fetch_add_explicit_8 |
| | c89atomic_fetch_add_explicit_16 |
| | c89atomic_fetch_add_explicit_32 |
| | c89atomic_fetch_add_explicit_64 |
+-----------------------------------------+-----------------------------------------------+
| atomic_fetch_sub | c89atomic_fetch_sub_8 |
| atomic_fetch_sub_explicit | c89atomic_fetch_sub_16 |
| | c89atomic_fetch_sub_32 |
| | c89atomic_fetch_sub_64 |
| | c89atomic_fetch_sub_explicit_8 |
| | c89atomic_fetch_sub_explicit_16 |
| | c89atomic_fetch_sub_explicit_32 |
| | c89atomic_fetch_sub_explicit_64 |
+-----------------------------------------+-----------------------------------------------+
| atomic_fetch_or | c89atomic_fetch_or_8 |
| atomic_fetch_or_explicit | c89atomic_fetch_or_16 |
| | c89atomic_fetch_or_32 |
| | c89atomic_fetch_or_64 |
| | c89atomic_fetch_or_explicit_8 |
| | c89atomic_fetch_or_explicit_16 |
| | c89atomic_fetch_or_explicit_32 |
| | c89atomic_fetch_or_explicit_64 |
+-----------------------------------------+-----------------------------------------------+
| atomic_fetch_xor | c89atomic_fetch_xor_8 |
| atomic_fetch_xor_explicit | c89atomic_fetch_xor_16 |
| | c89atomic_fetch_xor_32 |
| | c89atomic_fetch_xor_64 |
| | c89atomic_fetch_xor_explicit_8 |
| | c89atomic_fetch_xor_explicit_16 |
| | c89atomic_fetch_xor_explicit_32 |
| | c89atomic_fetch_xor_explicit_64 |
+-----------------------------------------+-----------------------------------------------+
| atomic_fetch_and | c89atomic_fetch_and_8 |
| atomic_fetch_and_explicit | c89atomic_fetch_and_16 |
| | c89atomic_fetch_and_32 |
| | c89atomic_fetch_and_64 |
| | c89atomic_fetch_and_explicit_8 |
| | c89atomic_fetch_and_explicit_16 |
| | c89atomic_fetch_and_explicit_32 |
| | c89atomic_fetch_and_explicit_64 |
+-----------------------------------------+-----------------------------------------------+
| atomic_thread_fence() | c89atomic_thread_fence |
| atomic_signal_fence() | c89atomic_signal_fence |
+-----------------------------------------+-----------------------------------------------+
| atomic_is_lock_free | c89atomic_is_lock_free_8 |
| | c89atomic_is_lock_free_16 |
| | c89atomic_is_lock_free_32 |
| | c89atomic_is_lock_free_64 |
+-----------------------------------------+-----------------------------------------------+
| (Not Defined) | c89atomic_compare_and_swap_8 |
| | c89atomic_compare_and_swap_16 |
| | c89atomic_compare_and_swap_32 |
| | c89atomic_compare_and_swap_64 |
| | c89atomic_compare_and_swap_ptr |
+-----------------------------------------+-----------------------------------------------+