exponenta event banner

CERT C: CON40-C правил

Не ссылайтесь на атомарную переменную дважды в выражении

Описание

Определение правила

Не ссылайтесь на атомарную переменную дважды в выражении. [1 ]

Внедрение Polyspace

Эта проверка проверяет наличие следующих проблем:

  • Атомарная переменная, доступная дважды в выражении.

  • Атомная нагрузка и последовательность хранения не атомарная.

Примеры

развернуть все

Проблема

Атомарная переменная, доступ к которой осуществляется дважды в выражении, возникает, когда атомарные типы C или C++ std::atomic переменные класса появляются дважды в выражении и существуют:

  • Две операции атомарного считывания для переменной.

  • Операция атомарного чтения и отдельная операция атомарной записи для переменной.

Стандарт C определяет определенные операции с атомарными переменными, которые безопасны для потоков и не вызывают условий гонки данных. В отличие от отдельных операций, пара операций с одной и той же атомной переменной в выражении не безопасна для потоков.

Риск

Поток может изменить атомарную переменную между двумя атомарными операциями, что может привести к условию гонки данных.

Зафиксировать

Не ссылайтесь на атомарную переменную дважды в одном выражении.

Пример - Ссылка на атомарную переменную дважды в выражении
#include <stdatomic.h>

atomic_int n = ATOMIC_VAR_INIT(0);

int compute_sum(void)
{
    return n * (n + 1) / 2;
}

В этом примере глобальная переменная n дважды упоминается в операторе возврата compute_sum(). Значение n может изменяться между двумя различными операциями чтения. compute_sum() может возвращать неверное значение.

Коррекция - передать переменную как аргумент функции

Одной из возможных корректировок является передача переменной в качестве аргумента функции. n. Переменная копируется в память, и операции чтения с копированием гарантируют, что compute_sum() возвращает правильный результат. При передаче переменной типа int вместо типа atomic_int, исправление по-прежнему является действительным.

#include <stdatomic.h>

int compute_sum(atomic_int n)
{
    return n * (n + 1) / 2;
}
Проблема

Атомная нагрузка и последовательность хранения не являются атомарными, если эти функции используются для загрузки, а затем для сохранения атомной переменной.

  • Функции C:

    • atomic_load()

    • atomic_load_explicit()

    • atomic_store()

    • atomic_store_explicit()

  • Функции C++:

    • std::atomic_load()

    • std::atomic_load_explicit()

    • std::atomic_store()

    • std::atomic_store_explicit()

    • std::atomic::load()

    • std::atomic::store()

Поток не может прервать атомарную нагрузку или операцию сохранения атома для переменной, но поток может прервать сохранение и затем загрузить последовательность.

Риск

Поток может изменять переменную между операциями загрузки и хранения, что приводит к условию гонки данных.

Зафиксировать

Чтобы считывать, изменять и сохранять переменную атомарно, используйте оператор составного назначения, например +=, atomic_compare_exchange() или atomic_fetch_*- функции семьи.

Пример - Загрузка и сохранение атомной переменной
#include <stdatomic.h>
#include <stdbool.h>

static atomic_bool flag = ATOMIC_VAR_INIT(false);

void init_flag(void)
{
    atomic_init(&flag, false);
}

void toggle_flag(void)
{
    bool temp_flag = atomic_load(&flag);
    temp_flag = !temp_flag;
    atomic_store(&flag, temp_flag);
}

bool get_flag(void)
{
    return atomic_load(&flag);
}

В этом примере переменная flag типа atomic_bool ссылается дважды внутри toggle_flag() функция. Функция загружает переменную, отменяет ее значение, а затем сохраняет новое значение обратно в переменную. Если вызывается два потока toggle_flag(), второй поток может получить доступ flag между операциями загрузки и хранения первого потока. flag может оказаться в неправильном состоянии.

Исправление - изменение переменной с помощью составного назначения

Одной из возможных корректировок является использование оператора составного назначения для переключения значения flag. Стандарт C определяет операцию с помощью ^= как атомарный.

#include <stdatomic.h>
#include <stdbool.h>

static atomic_bool flag = ATOMIC_VAR_INIT(false);

void toggle_flag(void)
{
    flag ^= 1;
}

bool get_flag(void)
{
    return flag;
}

Проверить информацию

Группа: Правило 14. Параллелизм (CON)
Представлен в R2019a

[1] Данное программное обеспечение было создано компанией MathWorks и включает в себя следующие компоненты: «Веб-сайт SEI CERT-C», © 2017 Университет Карнеги-Меллон, веб-сайт SEI CERT-C + + © 2017 Университет Карнеги-Меллон, "Стандарт кодирования SEI CERT C - Правила разработки безопасных, Надежные и безопасные системы - 2016 Edition ", © 2016 Университет Карнеги-Меллон, и "Стандарт кодирования SEI CERT C++ - Правила разработки безопасных, Надежные и безопасные системы в C++ - 2016 Edition "© 2016 Университет Карнеги-Меллон, со специальным разрешением от его Института программного обеспечения.

ЛЮБОЙ МАТЕРИАЛ УНИВЕРСИТЕТА КАРНЕГИ МЕЛЛОНА И/ИЛИ ЕГО ПРОГРАММНОГО ИНЖЕНЕРНОГО ИНСТИТУТА, СОДЕРЖАЩИЙСЯ В НАСТОЯЩЕМ ДОКУМЕНТЕ, ПОСТАВЛЯЕТСЯ КАК ЕСТЬ. УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ, ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, В ОТНОШЕНИИ ЛЮБЫХ ВОПРОСОВ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИИ ПРИГОДНОСТИ ДЛЯ ЦЕЛЕЙ ИЛИ ТОВАРНОЙ ПРИГОДНОСТИ, ИСКЛЮЧИТЕЛЬНОСТИ ИЛИ РЕЗУЛЬТАТОВ, ПОЛУЧЕННЫХ ОТ ИСПОЛЬЗОВАНИЯ УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ В ОТНОШЕНИИ СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКИХ ПРАВ.

Данное программное обеспечение и связанная с ним документация не были рассмотрены и не одобрены Университетом Карнеги-Меллона или его Институтом разработки программного обеспечения.