V2647. MISRA. Structure and union members of atomic objects should not be directly accessed.
Диагностическое правило основано на руководстве MISRA (Motor Industry Software Reliability Association) по разработке программного обеспечения.
Это правило актуально только для языка C.
Прямой доступ (оператор . или ->) к полям структур или объединений объектов атомарных типов может привести к гонке данных. Вместо этого доступ к объекту следует осуществлять с использованием специальных функций из <stdatomic.h> и оператора присваивания =.
Стандарт C определяет следующие функции для доступа к атомарным объектам:
atomic_initatomic_storeatomic_loadatomic_exchangeatomic_compare_exchange
Стандарт гарантирует, что выполнение атомарных операций с данными, которые используются в нескольких потоках, не приведёт к гонке данных. Однако прямой доступ к полям объектов атомарных типов нарушает эту гарантию.
Примечание. Функция atomic_init сама по себе не предотвращает потенциально возможную гонку данных. Если несколько потоков в момент инициализации обращаются к переменной, даже используя атомарные операции, это всё равно может привести к состоянию гонки.
Пример некорректного кода:
typedef struct {
int32_t width;
int32_t height;
} rectangle_t;
_Atomic rectangle_t rect;
int foo(void)
{
rect.width = 300;
return 0;
}
В примере прямая запись rect.width = 300; может привести к гонкам данных и невалидному состоянию объекта.
Руководство MISRA предлагает исправлять подобный код следующим образом:
typedef struct {
int32_t width;
int32_t height;
} rectangle_t;
_Atomic rectangle_t rect;
int foo(void)
{
rectangle_t tmp;
tmp = atomic_load(&rect);
tmp.height = 400;
atomic_store(&rect, tmp);
return 0;
}
Однако такое исправление нельзя считать самодостаточным. Исчезла проблема неатомарного изменения полей в структуре, но добавилась новая потенциальная проблема синхронизации.
Рассмотрим следующий сценарий:
- Первый поток вызвал функцию
fooи скопировал структуру в переменнуюtmp. - Пока первый поток меняет значение
tmp.height, второй поток может поменять значениеrect.width. - Первый поток вызывает функцию
atomic_store, перезаписываетrect.widthстарым значением. Ошибка.
Чтобы избежать такой проблемы, может потребоваться дополнительная синхронизация между разными потоками. Однако данная тема выходит за рамки диагностического правила MISRA.
Данная диагностика классифицируется как:
|