CERT C: Rule 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 Университет Карнеги Меллон, с специального разрешения от его Института программной инженерии.

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

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