メインコンテンツ

Deadlock

ロック関数の呼び出し順による 2 つのタスクの相互ブロック

説明

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

この欠陥は、以下の理由で、複数のタスクがクリティカル セクション (CS) にスタックされた場合に発生します。

  • 各 CS は別の CS が終了するのを待機します。

  • クリティカル セクション (CS) は循環を形成します。以下に例を示します。

    • CS #1 は CS #2 が終了するのを待機し、CS #2 は CS #1が終了するのを待機します。

    • CS #1 は CS #2 が終了するのを待機し、CS #2 は CS #3 が終了するのを待機し、CS #3 は CS #1 が終了するのを待機します。

Polyspace はコードのクリティカル セクションが指定された形式に準拠すると想定します。クリティカル セクションはロック関数およびロック解除関数の呼び出しの間に置かれています。タスク my_task がロック関数 my_lock を呼び出すとき、my_lock を呼び出し中の他のタスクは my_task が対応するロック解除関数を呼び出すまで待たなくてはなりません。

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

リスク

各タスクは、別のタスクのクリティカル セクションが終了するのを待機しているため続行できません。このプログラムはいつまでもフリーズし続ける可能性があります。

修正方法

修正方法は欠陥の根本原因によって異なります。次のいずれかの方法で、タスク間の周期的な順序の中断を試みることができます。

  • 特定のシーケンスにおけるデッドロックに関連するすべてのクリティカル セクションを書き出します。タスク内でクリティカル セクションのロック関数を呼び出す場合は常に、そのシーケンス内の順序に従います。以下の例を参照してください。

  • デッドロックに関連するいずれかのクリティカル セクションが割り込みで発生する場合、すべてのタスクのクリティカル セクションの間、すべての割り込みの無効化を試みます。すべての割り込みを無効にする (-routine-disable-interrupts -routine-enable-interrupts) を参照してください。

この欠陥のレビューは、クリティカル セクションでのすべての操作が本当にAtomic ブロックとしての実行を意図しているかどうかをチェックする機会になります。クリティカル セクションは必要最小限に留めることをお勧めします。

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

チェッカーの拡張

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

すべて展開する



void task1(void);
void task2(void);

int var;
void perform_task_cycle(void) {
 var++;
}

void begin_critical_section_1(void);
void end_critical_section_1(void);

void begin_critical_section_2(void);
void end_critical_section_2(void);

void task1() {
 while(1) {
    begin_critical_section_1();
    begin_critical_section_2();
    perform_task_cycle();
    end_critical_section_2();
    end_critical_section_1();
 } 
}

void task2() {
 while(1) {
    begin_critical_section_2();
    begin_critical_section_1();
    perform_task_cycle();
    end_critical_section_1();
    end_critical_section_2();
 } 
}

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

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

task1

task2

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

[デッドロック] は命令を以下の順番で実行するために発生します。

  1. task1begin_critical_section_1 を呼び出す。

  2. task2begin_critical_section_2 を呼び出す。

  3. task1 が命令 begin_critical_section_2(); に到達。task2 が既に begin_critical_section_2 を呼び出し済みのため、task1task2end_critical_section_2 を呼び出すのを待機します。

  4. task2 が命令 begin_critical_section_1(); に到達。task1 が既に begin_critical_section_1 を呼び出し済みのため、task2task1end_critical_section_1 を呼び出すのを待機します。

修正 - 両方のタスクで同じロック順序に従う

1 つの修正方法として、ロック関数とロック解除関数の呼び出し順序を task1 および task2 で同じにします。



void task1(void);
void task2(void);
void perform_task_cycle(void);

void begin_critical_section_1(void);
void end_critical_section_1(void);

void begin_critical_section_2(void);
void end_critical_section_2(void);

void task1() {
 while(1) {
    begin_critical_section_1();
    begin_critical_section_2();
    perform_task_cycle();
    end_critical_section_2();
    end_critical_section_1();
 } 
}

void task2() {
 while(1) {
    begin_critical_section_1();
    begin_critical_section_2();
    perform_task_cycle();
    end_critical_section_2();
    end_critical_section_1();
 } 
}


int var;
void performTaskCycle() {
 var++;
}

void lock1(void);
void lock2(void);
void lock3(void);


void unlock1(void);
void unlock2(void);
void unlock3(void);

void task1() {
 while(1) {
    lock1();
    lock2();
    performTaskCycle();
    unlock2();
    unlock1();
 } 
}

void task2() {
 while(1) {
    lock2();
    lock3();
    performTaskCycle();
    unlock3();
    unlock2();
 } 
}

void task3() {
 while(1) {
    lock3();
    lock1();
    performTaskCycle();
    unlock1();
    unlock3();
 } 
}

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

オプション仕様
マルチタスクを手動で構成
エントリ ポイント

task1

task2

task3

クリティカル セクション詳細開始ルーチン終了ルーチン
lock1unlock1
lock2unlock2
lock3unlock3

[デッドロック] は命令を以下の順番で実行するために発生します。

  1. task1lock1 を呼び出す。

  2. task2lock2 を呼び出す。

  3. task3lock3 を呼び出す。

  4. task1 が命令 lock2(); に到達。task2 が既に lock2 を呼び出し済みのため、task1unlock2 の呼び出しを待機。

  5. task2 が命令 lock3(); に到達。task3 が既に lock3 を呼び出し済みのため、task2unlock3 の呼び出しを待機。

  6. task3 が命令 lock1(); に到達。task1 が既に lock1 を呼び出し済みのため、task3unlock1 の呼び出しを待機。

修正 — 周期的な順序を中断

クリティカル セクション間の周期的な順序を中断するには、コード内のすべてのロック関数は一定の順序に従わなくてはなりません。次に例を示します。

  1. lock1

  2. lock2

  3. lock3

1 つのタスク内で複数ロック関数を使用する場合、シーケンス内に現れる順に使用します。たとえば、 lock1 の後に lock2 を使用できますが、lock2 の後には lock1 を使用できません。



int var;
void performTaskCycle() {
 var++;
}

void lock1(void);
void lock2(void);
void lock3(void);

void unlock1(void);
void unlock2(void);
void unlock3(void);

void task1() {
 while(1) {
    lock1();
    lock2();
    performTaskCycle();
    unlock2();
    unlock1();
 } 
}

void task2() {
 while(1) {
    lock2();
    lock3();
    performTaskCycle();
    unlock3();
    unlock2();
 } 
}

void task3() {
 while(1) {
    lock1();
    lock3();
    performTaskCycle();
    unlock3();
    unlock1();
 } 
}

結果情報

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

バージョン履歴

R2014b で導入