メインコンテンツ

CERT C++: CON40-C

Do not refer to an atomic variable twice in an expression

説明

ルール定義

式の中でアトミック変数を 2 回参照しないようにします。1

Polyspace 実装

ルール チェッカーは以下の問題をチェックします。

  • 式の中でアトミック変数へのアクセスが 2 回行われています

  • アトミックな読み込みと保存のシーケンスがアトミックではありません

すべて展開する

問題

式の中でアトミック変数へのアクセスが 2 回行われていますは、C のアトミック型または C++ の std::atomic クラスの変数が 1 つの式の中で 2 回登場する場合に発生します。これには次のようなものがあります。

  • 変数に対する 2 回のアトミックな読み取り操作。

  • 変数に対するアトミックな読み取り操作と別個のアトミックな書き込み操作。

C 標準では、スレッドセーフになっていて、データ レース状態を引き起こさない、アトミック変数に対する特定の操作が定義されています。個別の操作とは異なり、1 つの式における同じアトミック変数に対する 1 組の操作はスレッドセーフではありません。

リスク

スレッドは、1 組のアトミック操作の間にアトミック変数を変更することができ、その結果、データ レース状態になる可能性があります。

修正方法

同じ式の中でアトミック変数を 2 回参照しないようにします。

例 - 1 つの式内でアトミック変数を 2 回参照

この例を実行するには、以下のオプションを使用します。

  • -cpp-version cpp11

  • -compiler gnu4.9

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

この例では、グローバル変数 ncompute_sum() の return ステートメント内で 2 回参照されています。n の値は、2 つの別個の読み取り操作の間に変わる可能性があります。compute_sum() は誤った値を返すおそれがあります。

修正 — 変数を関数の引数として渡す

1 つの修正方法として、変数を関数の引数 n として渡します。変数はメモリにコピーされ、そのコピーに対する読み取り操作により compute_sum() で正しい結果が返されることが保証されます。atomic_int 型の代わりに 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

  • -compiler 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); //Noncompliant
}

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

この例では、atomic_bool 型の変数 flag が関数 toggle_flag() 内で 2 回参照されています。この関数は変数を読み込み、その値を否定して、新しい値を元の変数に格納します。2 つのスレッドで toggle_flag() を呼び出す場合、2 つ目のスレッドは 1 つ目のスレッドの読み込み操作と格納操作の間に flag にアクセスできます。最終的に flag が不適切な状態になる可能性があります。

修正 — 複合代入を使用して変数を変更

1 つの修正方法として、関数 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 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.