exponenta event banner

CERT C++: CON40-C

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

Описание

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

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

Внедрение Polyspace

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

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

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

Примеры

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

Проблема

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

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

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

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

Риск

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

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

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

Пример - Ссылка на атомарную переменную дважды в выражении

Для выполнения этого примера используйте следующие параметры:

  • -cpp-version cpp11

  • -компилятор gnu4.9

#include <atomic>
std::atomic<int> n(5);
int compute_sum(void)
{
    return n * (n + 1) / 2;
}

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

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

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

#include <atomic>
int compute_sum(std::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_*- функции семьи.

Пример - Загрузка и сохранение атомной переменной

Для выполнения этого примера используйте следующие параметры:

  • -cpp-version cpp11

  • -компилятор gnu4.9

#include <atomic>
#include <stdbool.h>
using namespace std;
static atomic<bool> flag(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 может оказаться в неправильном состоянии.

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

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

#include <atomic>
#include <stdbool.h>
using namespace std;
static atomic<bool> flag(false);


void toggle_flag(void)
{
  bool old_flag = atomic_load(&flag);
  bool new_flag;
  do {
    new_flag = !old_flag;
  } while (!atomic_compare_exchange_weak(&flag, &old_flag, new_flag));

}

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

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

Группа: 10. Параллелизм (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 Университет Карнеги-Меллон, со специальным разрешением от его Института программного обеспечения.

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

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