Данное диагностическое правило основано на руководстве MISRA (Motor Industry Software Reliability Association) по разработке программного обеспечения.
Это правило актуально только для языка C.
При использовании функций для работы с атомарными операциями аргумент, отвечающий за тип модели памяти, должен всегда иметь значение memory_order::memory_order_seq_cst
(последовательная согласованность).
При использовании в многопоточном коде атомарных операций может сложиться ситуация, когда один поток обратится к значению переменной раньше, чем другой поток это значение изменит. Для синхронизации операций чтения/записи в языке C существуют модели памяти, определённые в перечислении memory_order
.
Выбранная модель памяти влияет на то, как компилятор и процессор оптимизируют код. Поведением по умолчанию является sequentially consistent ordering (последовательная согласованность). Эта модель поведения наиболее интуитивно понятна для человека. А также это самая строгая модель для компилятора и процессора. При её использовании операции чтения/записи будут выполняться чётко в том порядке, в котором они написаны в коде.
Данное диагностическое правило призвано уменьшить вероятность ошибки и исключить зависимость выполнения параллельного кода от оптимизаций процессора и компилятора.
Ниже перечислены функции из Concurrency support library, которые должны вызываться со значением 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
.Помимо явной передачи аргумента можно использовать не *_explicit
альтернативы вышеописанных функций. В таком случае будет неявно передан аргумент memory_order::memory_order_seq_cst
.
Пример некорректного кода:
_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);
}
}
В приведённом выше синтетическом примере порядок выполнения операций load
и store
не может быть заранее предсказан, поскольку нестрогая модель памяти позволит компилятору и процессору менять очередность операций.
Корректный код:
_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);
}
}
Данная диагностика классифицируется как:
|