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;
}

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

Группа: 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". УНИВЕРСИТЕТ КАРНЕГИ-МЕЛЛОН НЕ ДАЕТ ГАРАНТИЙ НИКАКОГО ВИДА, ИЛИ ВЫРАЗИЛ ИЛИ ПОДРАЗУМЕВАЛ, ОТНОСИТЕЛЬНО ЛЮБОГО ВОПРОСА ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИЛ, ГАРАНТИЯ ПРИГОДНОСТИ ДЛЯ ЦЕЛИ ИЛИ ВЫСОКОГО СПРОСА, ИСКЛЮЧИТЕЛЬНОСТИ, ИЛИ ЗАКАНЧИВАЕТСЯ ПОЛУЧЕННЫЙ ИЗ ИСПОЛЬЗОВАНИЯ МАТЕРИАЛА. УНИВЕРСИТЕТ КАРНЕГИ-МЕЛЛОН НЕ ДАЕТ ГАРАНТИИ НИКАКОГО ВИДА ОТНОСИТЕЛЬНО СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКОГО ПРАВА.

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