V2643. MISRA. All memory synchronization operation should be executed in sequentially consistent order.
This diagnostic rule is based on the MISRA (Motor Industry Software Reliability Association) software development guidelines.
This diagnostic rule is relevant only for C.
Using functions to handle atomic operations implies that the argument specifying the memory model type should always be set to memory_order::memory_order_seq_cst
(sequentially consistent ordering).
When atomic operations are used in multithreaded code, one thread can access a variable value before another thread changes it. To synchronize read/write operations, C provides memory models defined in the memory_order
enumeration.
The selected memory model can impact how the compiler and processor optimize the code. The default behavior is sequentially consistent ordering. This behavior pattern is the most intuitive for developers and the strictest model for compilers and processors. When it's used, read/write operations are performed exactly in the order in which they are written in the code.
This diagnostic rule is intended to minimize errors and exclude dependency of parallel code execution on processor and compiler optimizations.
The following functions from the Concurrency support library should be called with memory_order::memory_order_seq_cst
:
atomic_load_explicit
atomic_store_explicit
atomic_flag_test_and_set_explicit
atomic_flag_clear_explicit
atomic_exchange_explicit
atomic_compare_exchange_strong_explicit
atomic_compare_exchange_weak_explicit
atomic_fetch_add_explicit
atomic_fetch_sub_explicit
atomic_fetch_or_explicit
atomic_fetch_xor_explicit
atomic_fetch_and_explicit
atomic_thread_fence
atomic_signal_fence
.
In addition to explicitly passing the argument, developers can use non-*_explicit
alternatives of these functions. In this case, the memory_order::memory_order_seq_cst
argument will be implicitly passed.
The example of the incorrect code:
_Atomic int flag; // initialized elsewhere with non-zero value
void thread1()
{
// do stuff
atomic_store_explicit(&flag, 0, memory_order_release);
}
void thread2()
{
auto value = atomic_load_explicit(&flag, memory_order_acquire);
while (value != 0)
{
// do stuff
value = atomic_load_explicit(&flag, memory_order_acquire);
}
}
In the synthetic example above, the order of the load
and store
operations cannot be predicted because the non-strict memory model allows the compiler and processor to rearrange the operation order.
The fixed code:
_Atomic int flag; // initialized elsewhere with non-zero value
void thread1()
{
// do some stuff
atomic_store_explicit(&flag, 0, memory_order_seq_cst);
// or use atomic_store(&flag, 0);
}
void thread2()
{
auto value = atomic_load_explicit(&flag, memory_order_seq_cst);
// or declaration via 'atomic_load'
// auto value = atomic_load(&flag);
while (value != 0)
{
// do some stuff
value = atomic_load_explicit(&flag, memory_order_seq_cst);
// or assignment via 'atomic_load'
// value = atomic_load(&flag);
}
}
This diagnostic is classified as:
|