メインコンテンツ

データ レース

複数タスクによる共有変数に対する保護されない非アトミック操作の実行

説明

このチェッカーは、既定の Polyspace® as You Code 解析では非アクティブにされますPolyspace as You Code 解析で非アクティブにされるチェッカー (Polyspace Access)を参照してください

この欠陥は、以下の場合に発生します。

  1. 複数のタスクが保護されない操作を共通の変数に実行。

  2. 少なくとも 1 つのタスクが書き込み操作を実行。

  3. 少なくとも 1 つの操作が非アトミック。アトミック操作と非アトミック操作の両方でデータ レースを検出するには、オプション [-detect-atomic-data-race] を使用します。Extend Data Race Checkers to Atomic Operationsを参照してください。

    マルチタスキング コードでのアトミック操作の定義を参照してください。

欠陥チェッカーは、競合する読み取り/書き込み操作または書き込み/書き込み操作のある共有変数ごとに、1 つのデータ レース結果を報告します。結果ごとのイベント リストに、競合するアクセスのリストが表示されます。このリストは、競合するアクセスの代表的なものを示すもので、すべてを網羅しているわけではありません。

このチェッカーを先にマルチタスキング オプションを指定せずに有効にした場合は、ログに警告が書き込まれます。

Warning: Checker 'Data Race' is activated but no protection have been defined

この欠陥を検出するには、次のいずれかの方法で共有変数のタスクおよび保護を指定します。

リスク

異なるタスクでの操作の順序は制御されないため、データ レースにより共有変数が予測不能な値になる可能性があります。

2 つの書き込み操作間のデータ レースは、書き込み操作と読み取り操作間のデータ レースよりも深刻です。2 つの書き込み操作が相互に干渉し、不確定な値になる可能性があります。書き込みどうしの競合を特定するには、[結果のリスト] ペインの [詳細] 列でフィルターを使用します。これらの競合について、[詳細] 列に次の追加行が表示されます。

 Variable value may be altered by write-write concurrent access.
Polyspace デスクトップ ユーザー インターフェイスでの結果のフィルター処理とグループ化またはPolyspace Access Web インターフェイスでの結果のフィルタリングと並べ替え (Polyspace Access)も参照してください。

修正方法

この欠陥を修正するには、クリティカル セクション、時間的に排他、または別の方法を使用して、共有変数に対する操作を保護します。マルチタスキング コードでの共有変数の保護を参照してください。

再利用できる既存の保護を特定するには、結果に関連付けられている表とグラフを確認します。表では競合する呼び出しの各ペアが示されます。[アクセス保護] 列には、その呼び出しについての既存の保護が表示されます。競合につながる関数呼び出しの順序を確認するには、 アイコンをクリックします。以下の例を参照してください。

チェッカーの拡張

Bug Finder では既定で検出されない可能性がある、演算でのデータ レースをチェックするように、このチェッカーを拡張します。次に例を示します。

チェッカーは共有変数ごとに 1 つのデータ レース結果を報告します。結果のイベント リストには、限られた数の競合する読み取り/書き込みと、書き込みどうしの競合が表示されます。これらの制限はカスタマイズできません。

すべて展開する



int var;
void begin_critical_section(void);
void end_critical_section(void);

void increment(void) {
    var++; 
}

void task1(void)  { 
      increment();
}

void task2(void)  { 
      increment();
}

void task3(void)  { 
     begin_critical_section();
     increment();
     end_critical_section();
}

この例では、マルチタスクの動作をエミュレートするため、以下のオプションを指定しなければなりません。

オプション仕様
マルチタスクを手動で構成
タスク (-entry-points)

task1

task2

task3

クリティカル セクション詳細 (-critical-section-begin -critical-section-end)開始ルーチン終了ルーチン
begin_critical_sectionend_critical_section

コマンド ラインでは以下を使用できます。

 polyspace-bug-finder 
   -entry-points task1,task2,task3
   -critical-section-begin begin_critical_section:cs1
   -critical-section-end end_critical_section:cs1

この例では、task1task2 および task3 のタスクが関数 increment を呼び出します。incrementvar++ 操作を含み、次のような複数のマシン命令に関連します。

  • var の読み込み。

  • 増加した値の var への書き込み。

これらのマシン命令は、task1 および task2 から実行する場合、予想できない順序で同時発生が起こりえます。たとえば、task1 から var の読み込みは task2 から var への書き込み前、後どちらでも起こりえます。したがって、var の値は予測できません。

task3 はクリティカル セクションの内部で increment を呼び出しますが、他のタスクは同じクリティカル セクションを使用していません。task3 のクリティカル セクション内の操作は、他のタスクでの操作と相互に排他的ではありません。

したがって、3 つのタスクが共通の保護が行われていない共有変数に対して操作を行っています。結果の詳細で、競合する関数呼び出しの各ペアが表示されます。

アイコンをクリックすると、読み取りまたは書き込み操作のエントリ ポイントから始まる関数呼び出し順序が表示されます。task3 から始まる操作がクリティカル セクションに含まれていることも確認できます。[アクセス保護] エントリでは、クリティカル セクションを開始、終了するロック関数とロック解除関数が表示されます。この例では、関数 begin_critical_sectionend_critical_section が表示されます。

修正 —クリティカル セクションに操作を配置

考えられる 1 つの修正方法として、クリティカル セクション内に操作を配置します。クリティカル セクションは複数の方法で実装できます。次に例を示します。

  • クリティカル セクションに var++ を配置できます。task1 がクリティカル セクションに入るとき、他のタスクは task1 がクリティカル セクションを離れるまで入ることができません。3 つのタスクからの var++ 操作は互いに干渉できません。

    関数 increment でクリティカル セクションを実装するために、var++ 操作を begin_critical_section および end_critical_section の呼び出しの間に配置します。

    
    
    int var;
    
    void begin_critical_section(void);
    void end_critical_section(void);
    
    void increment(void) {
          begin_critical_section();
          var++;
          end_critical_section(); 
    }
    
    void task1(void)  { 
          increment();
    }
    
    void task2(void)  { 
          increment();
    }
    
    void task3(void)  { 
          increment();
    }
    

  • 3 つのタスクで、同じクリティカル セクション内に increment の呼び出しを配置できます。task1 がクリティカル セクションに入るとき、他のタスクは task1 がクリティカル セクションを離れるまで入ることができません。3 つのタスクからの increment の呼び出しは互いに干渉できません。

    クリティカル セクションを実装するには、3 つのそれぞれのタスク内で begin_critical_sectionend_critical_section の呼び出しの間に increment を呼び出します。

    
    
    int var;
    
    void begin_critical_section(void);
    void end_critical_section(void);
    
    void increment(void) {
          var++;       
    }
    
    void task1(void)  { 
         begin_critical_section();
         increment();
         end_critical_section();
    }
    
    void task2(void)  { 
         begin_critical_section();
         increment();
         end_critical_section();
    }
    
    void task3(void)  { 
         begin_critical_section();
         increment();
         end_critical_section();
    }

修正 — タスクを時間的に排他にする

別の修正方法として、タスク task1task2 および task3 を時間的に排他にします。時間的に排他なタスクは同時に実行することはできません。

[構成] ペインで、以下の追加オプションを指定します。

コマンド ラインでは以下を使用できます。

 polyspace-bug-finder 
     -temporal-exclusions-file "C:\exclusions_file.txt"
ここで、ファイル C:\exclusions_file.txt には以下の行があります。
task1 task2 task3

#include <pthread.h>

pthread_mutex_t count_mutex;
long long count;


void* increment_count(void* args)
{
    count = count + 1;
    return NULL;
}

void* set_count(void *args)
{
    long long c;
    c = count;
    return NULL;
}

int main(void)
{
    pthread_t thread_increment;
    pthread_t thread_get;

    pthread_create(&thread_increment, NULL, increment_count, NULL);
    pthread_create(&thread_get, NULL, set_count, NULL);
    
    pthread_join(thread_get, NULL);
    pthread_join(thread_increment, NULL);

    return 1;
}

この例では、Bug Finder は pthread_create を使用した個別のスレッドの作成を検出します。ID が thread_increment であるスレッドでの演算 count = count + 1 と、ID が thread_get であるスレッドでの演算 c = count が競合するため、[データ レース] 欠陥が報告されます。変数 count は一般的な保護が使用されておらず、複数のスレッドからアクセスされます。

この 2 つの競合する操作は非アトミックです。32 ビット ターゲットでの演算 c = count は非アトミックです。マルチタスキング コードでのアトミック操作の定義を参照してください。

修正 — pthread_mutex_lockpthread_mutex_unlock のペアによる操作の保護

変数 count に対する同時アクセスを防ぐために、クリティカル セクションを使用して count に対する操作を保護します。関数 pthread_mutex_lock と関数 pthread_mutex_unlock を使用して、クリティカル セクションを実装します。

#include <pthread.h>

pthread_mutex_t count_mutex;
long long count;


void* increment_count(void* args)
{
    pthread_mutex_lock(&count_mutex);
    count = count + 1;
    pthread_mutex_unlock(&count_mutex);
    return NULL;        
}

void* set_count(void *args)
{
    long long c;
    pthread_mutex_lock(&count_mutex);
    c = count;
    pthread_mutex_unlock(&count_mutex);
    return NULL;
}

int main(void)
{
    pthread_t thread_increment;
    pthread_t thread_get;

    pthread_create(&thread_increment, NULL, increment_count, NULL);
    pthread_create(&thread_get, NULL, set_count, NULL);

    pthread_join(thread_get, NULL);
    pthread_join(thread_increment, NULL);

    return 1;
}

結果情報

グループ: 同時実行
言語: C | C++
既定値: オン
コマンド ライン構文: DATA_RACE
影響度: High

バージョン履歴

R2014b で導入