メインコンテンツ

ロック解除が重複しています

ロック関数はタスク中 2 回呼び出されますが、間にロック解除関数の呼び出しはありません。

説明

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

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

  • タスクはロック関数 my_lock を呼び出します。

  • タスクは対応するロック解除関数 my_unlock を呼び出します。

  • タスクは再度 my_unlock を呼び出します。タスクは my_unlock の2 回の呼び出しの間で my_lock を 2 回目は呼び出しません。

マルチタスキング コードでは、ロック関数はコードのクリティカル セクションを開始し、ロック解除関数はそれを終了させます。タスク task1 がロック関数 my_lock を呼び出すとき、my_lock を呼び出し中の他のタスクは task1 が対応するロック解除関数を呼び出すまで待たなくてはなりません。

この欠陥を検出するには、次のいずれかのメソッドを使用してロック関数とロック解除関数を指定します。

リスク

ロック解除の重複の欠陥は、コーディング エラーを示す可能性があります。別のクリティカル セクションを終了するために異なるロック解除関数を呼び出す必要があった可能性もあります。最初のロック解除関数を途中で呼び出しているが、2 番目の呼び出しだけがクリティカル セクションの終了を意図していた可能性もあります。

修正方法

修正方法は欠陥の根本原因によって異なります。

コードの各クリティカル セクション、つまり、Atomic ブロックとして実行するセクションを特定します。セクションの最初でロック関数を呼び出します。セクションの最後でのみ、ロック関数に対応するロック解除関数を呼び出します。ロック解除関数のそれ以外の冗長な呼び出しはすべて削除します。

以下の修正例を参照してください。この問題を回避するには、同じモジュール内のロック関数とロック解除関数を同じ抽象化レベルで呼び出す手法に従うことをお勧めします。次の例の場合、func はロック関数とロック解除関数を同じレベルで呼び出していますが、func2 はそうではありません。

void func() {
  my_lock();
  {
    ...
  }
  my_unlock();
}

void func2() {
  {
   my_lock();
   ...
  }
  my_unlock();
}

問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。

チェッカーの拡張

Polyspace でサポートされていないロック解除関数を使用する場合があります。その場合、ロック解除関数を、POSIX® の既知の同等の関数にマッピングすることで、このチェッカーを拡張します。サポートされていないマルチスレッド環境への同時実行欠陥チェッカーの拡張を参照してください。

すべて展開する



int global_var;

void BEGIN_CRITICAL_SECTION(void);
void END_CRITICAL_SECTION(void);

void task1(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
}

void task2(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
}

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

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

task1

task2

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

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

 polyspace-bug-finder
   -entry-points task1,task2
   -critical-section-begin BEGIN_CRITICAL_SECTION:cs1
   -critical-section-end END_CRITICAL_SECTION:cs1

task1BEGIN_CRITICAL_SECTION(); でクリティカル セクションを開始します。task1END_CRITICAL_SECTION(); を呼び出してクリティカル セクションを離れます。task1END_CRITICAL_SECTION を再度呼び出しますが、その間 BEGIN_CRITICAL_SECTION を呼び出しません。

修正 — 2 番目のロック解除を削除

2 番目の global_var+=1; をクリティカル セクションの外部に置きたい場合、1 つの修正方法として END_CRITICAL_SECTION への 2 番目の呼び出しを削除できます。ただし、他のタスクが global_var を使用している場合、このコードはデータ レースエラーを発生させることがあります。



int global_var;

void BEGIN_CRITICAL_SECTION(void);
void END_CRITICAL_SECTION(void);

void task1(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
    global_var += 1;
}

void task2(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
}
修正 — 最初のロック解除を削除

2番目の global_var+=1; をクリティカル セクションの内部に置きたい場合、1 つの修正方法として END_CRITICAL_SECTION の最初の呼び出しを削除できます。



int global_var;

void BEGIN_CRITICAL_SECTION(void);
void END_CRITICAL_SECTION(void);

void task1(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    global_var += 1;
    END_CRITICAL_SECTION();
}

void task2(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
}
修正 — 別のロックを追加

2番目の global_var+=1; をクリティカル セクションの内部に置きたい場合、他の修正方法として BEGIN_CRITICAL_SECTION の呼び出しを別に追加することができます。



int global_var;

void BEGIN_CRITICAL_SECTION(void);
void END_CRITICAL_SECTION(void);

void task1(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
}

void task2(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
}

結果情報

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

バージョン履歴

R2014b で導入

すべて展開する