• Imagix 4D ユーザガイド
  • 目次

[タスク間のイベントの遷移] レポート

[タスク間のイベントの遷移] レポートは [タスク内のイベント呼出し] レポートと同様に、マルチタスク・システムにおいて、同期イベントの使用を確認するために利用します。

イベントメカニズムは C / C++ プログラムで明示されないため、両レポートでは最初にイベントメカニズムを特定する必要があります。イベント関数およびイベントそのものは、[イベント定義] ダイアログで指定します。このイベントとは、イベント間の情報伝達で使用されるオペレーティング・システムの共有リソースに対する、ソースコード・レベルの識別子(マクロ、列挙リテラル、定数変数)です。一方イベント関数は、オペレーティング・システム固有の関数であり、イベントの待機(pend)、イベントの通知(最新のタスクに共有リソースへのアクセスを提供)、イベントのクリア(すべてのタスクを再び待機状態とするため、利用可能なあらゆる通知を削除)するために使用されます。クリアのイベント関数は、通知のイベント関数の変種とみなされ、特殊なパラメータ値によって通知ではなくクリアの実行が命令されます。

タスク間の同期における主な問題の1つはデッドロック、つまり各タスクが他のタスクによるイベント通知を、永遠に待ち続けることです。[タスク間のイベントの遷移] レポートは、潜在的なデッドロックおよび、その他の問題の識別に役立ちます。このレポートは、タスクが相互に待機している条件を検証するために、プログラムをブラウジングする起点となります。

以下に、taskX が taskY に、動作を開始する準備が整ったことを合図する、簡単な例をあげます。

#define EVENTA 1

extern void PostEvent(int event);
extern void WaitEvent(int event);

void taskX() {
   // 割り込みで開始、関連項目をセットアップ
   PostEvent(EVENTA);
}

void taskY() {
   while (1) {
    WaitEvent(EVENTA);
    // 続いて動作を開始
   }
}

以下のコード用に生成されるレポートは、2つのタスク間で情報を伝達するための EVENTA の使用方法を提示します。

Event Transition Between Tasks

Key:
        P:                task posts this event
        W:                task waits for this event
        C:                task clears this event

Settings:
        Wait event function:       WaitEvent
        Post event function:       PostEvent
        Number of events defined:  1
                                   

Task Definitions
Tasks are from User Defined Tasks
Name        Members  Graph  Root
taskX             2    [+]  taskX
taskY             2    [+]  taskY

Events                                  Task1     Task2     Task3 ...

                                        taskX
                                           .      taskY
----------------------------------------   =         =      
EVENTA .................................   P         W      

以下に、より複雑な例を示します。ここでは合図を行うためタスクをいくつか呼び出し、複数のイベントを使用しています。また、この例では、データフロー解析による値の伝播および式の解釈に関するサポート方法も示しており、同時にある特殊なケースのイベントが通知されます。

// イベント
#define EVENT1 1
#define EVENT2 2
#define EVENT3 4
#define EVENT4 8

void PostEvent(int event, int mode);
void WaitEvent(int event);

#define POSTMODE 1   // 通常の通知
#define OS_FLAG_CLR 2  // 通知済みのイベントのクリア

void task1() {
    PostEvent(EVENT1, POSTMODE);
    // OS_FLAG_CLR のため無視
    PostEvent(~(EVENT4|EVENT2), OS_FLAG_CLR);  
}

void task2() {
    WaitEvent(EVENT1);
}

void task3_f1(int p) {
    int event;
    if (p == 13) event = EVENT2;
        else event = EVENT4;
    if (p) {
        PostEvent(event, POSTMODE);   // 直接代入のローカル var
    } else {
        WaitEvent(EVENT3);
    }
}

void task3() {
    int b = 2;
    if (b)
    while (b--)
        task3_f1(b);
    WaitEvent(EVENT2);
}

// より複雑なイベントの式
int pevents = ~0;
void task4() {
    if (pevents & EVENT4)
        pevents &= ~EVENT1;
    else
        pevents &= ~(EVENT1 | EVENT3);
    PostEvent(pevents, POSTMODE);
    int wevents = EVENT1 | EVENT3 | EVENT4;
    if (pevents)
        wevents &= ~EVENT1;
    WaitEvent(wevents);
}

#define MULTI_EVENT1 (EVENT1 | EVENT3)
#define MULTI_EVENT2 (MULTI_EVENT1 | EVENT2)

void task5() {
    int c = 3;
    int event = EVENT4;
    if (c == 13) event |= MULTI_EVENT1;
    if (c) {
        PostEvent(event, POSTMODE);  // 式をともなうローカル変数
    }else {
        WaitEvent(MULTI_EVENT2);
    }
 }

// イベントの式で使用する配列
static const int execDefaultCallbackEvents[] =
{   // タスク id によるインデックスは、"tasks.h" で定義されたタスク id に必ず対応する必要があります
    EVENT1,           // SUPERVISOR タスク - デフォルトのコールバック・イベント
    NULL,                           // DISK       タスク - 無し
    NULL,                           // XFER       タスク - 無し
    NULL,                           // HOST       タスク - 無し
    NULL,                           // EXEC       タスク - 無し
    EVENT4               // BACKGROUND タスク - デフォルトのコールバック・イベント
};
void task6() {
    int taskid = 0;
    WaitEvent(execDefaultCallbackEvents[taskid]);
};

出力されるレポートでは、マトリックス形式で、通知、待機、クリアを実行するタスクを表示します。このレポートを確認するにあたり、注目すべき点がいくつかあります。1度も通知呼び出しを行わないイベント(行)は、確実にデッドロックの条件として考慮できます。また、2つのタスク(列)が、複数のイベント(行)を使用して情報の伝達を行っており、さらに各行の別々のタスクで通知と待機が発生しているケースでは、より複雑なデットロックが生じる可能性があります。この場合、それらの制御フローおよび条件によって、タスクが互いに待機しないことが保証されるか否かの確認を取る必要があるかもしれません。[タスク内のイベント呼出し] レポートを使用することで、デッドロックが発生していないことを見極めるために、個々の待機の呼び出し箇所をブラウジングすることが可能です。

Event Transition Between Tasks

Key:
        P:                task posts this event
        W:                task waits for this event
        C:                task clears this event

Settings:
        Post event function:       PostEvent
        Wait event function:       WaitEvent
        Number of events defined:  4
                                   

Task Definitions
Tasks are from User Defined Tasks
Name        Members  Graph  Root
Task1             2    [+]  task1
Task2             2    [+]  task2
Task3             4    [+]  task3
Task4             3    [+]  task4
Task5             3    [+]  task5
Task6             2    [+]  task6

Events                           Task1     Task2     Task3 ...

                                 Task1
                                 .      Task2
                                 .         .      Task3
                                 .         .         .      Task4
                                 .         .         .         .      Task5
                                 .         .         .         .         .      Task6
------------------------------   =         =         =         =         =         =      
EVENT1 .......................   P         W         .         .         PW        W      
EVENT2 .......................   P         .         PW        P         W         .      
EVENT3 .......................   .         .         W         W         PW        .      
EVENT4 .......................   P         .         P         PW        P         W 

このイベントレポートの制限として、待機イベントおよび通知イベントの関数呼び出しに使用されるイベントパラメータは、静的に決定できる必要があることに注意してください。つまり、パラメータがイベントそのもの(マクロ、列挙リテラル、定数変数)、イベントを示す単純な式("EVENTA | EVENTB" など)、あるいは現在の関数内にある一連のイベントすべてに対して設定されるローカル変数のいずれかになる必要がある、ということを意味します。グローバル変数や現在の関数に渡されるパラメータを、イベントパラメータにすることはできません。