CERT C++: CON40-C

Не обращайтесь к атомарной переменной дважды в выражении

Описание

Управляйте определением

Не обращайтесь к атомарной переменной дважды в выражении. [1]

Примеры

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

Описание

Атомарная переменная, к которой получают доступ дважды в выражении, происходит, когда атомарные типы 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;
}

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

Группа: 10. Параллелизм (CON)

Введенный в R2019a


[1]  Это программное обеспечение было создано MathWorks, включающим фрагменты: “Веб-сайт SEI CERT-C”, © 2017 Carnegie Mellon University, веб-сайт SEI CERT-C © 2017 Carnegie Mellon University”, CERT SEI C Кодирование Стандарта – Правил для Разработки безопасных, Надежных и Защищенных систем – 2 016 Выпусков”, © 2016 Carnegie Mellon University, and “CERT SEI Стандарт Кодирования C++ – Правил для Разработки безопасных, Надежных и Защищенных систем на C++ – 2 016 Выпусков” © 2016 Carnegie Mellon University, со специальным разрешением от его Института программной инженерии.

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

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