これは、ネイティブの vxworks では非常に簡単なはずです。ここで必要なのはメッセージ キューです。wait_for メソッドはそのまま使用できます。
bool condition::wait_for(mutex& mutex) const
{
unlocker ul(mutex); // relinquish mutex
return wait(event);
} // ul's dtor grabs mutex again
しかし、wait(event) コードは次のようになります。
wait(event)
{
if (msgQRecv(event->q, sigMsgBuf, sigMsgSize, timeoutTime) == OK)
{
// got it...
}
else
{
// timeout, report error or something like that....
}
}
シグナルコードは次のようになります。
signal(event)
{
msgQSend(event->q, sigMsg, sigMsgSize, NO_WAIT, MSG_PRI_NORMAL);
}
したがって、待機を開始する前にシグナルがトリガーされた場合、msgQRecv は、最終的に呼び出されるとすぐにシグナルを返します。その後、上記のように、ul dtor でミューテックスを再度取得できます。
event->q は、イベントの作成時に msgQCreate を呼び出して作成される MSG_Q_ID であり、sigMsg のデータはユーザーによって定義されます... しかし、データの単なるランダムなバイトである場合もあれば、誰が信号を送ったか、または知っておくと便利なその他の情報を含む、よりインテリジェントな構造。
複数のウェイターの更新、これは少しトリッキーです: したがって、物事を単純化するためにいくつかの仮定があります
- 保留中のタスクの数は、イベントの作成時に判明しており、一定です。
- ミューテックスのロックを解除してもよいかどうかを常に示すタスクが 1 つあります。他のすべてのタスクは、イベントが通知/完了されたときに通知を受け取るだけです。
このアプローチでは、上記と同様にカウント セマフォを使用しますが、ロジックが少し追加されています。
wait(event)
{
if (semTake(event->csm, timeoutTime) == OK)
{
// got it...
}
else
{
// timeout, report error or something like that....
}
}
シグナルコードは次のようになります。
signal(event)
{
for (int x = 0; x < event->numberOfWaiters; x++)
{
semGive(event->csm);
}
}
イベントの作成はこのようなものです。この例では、ウェイターの数は一定であり、イベントの作成時にわかっていることを思い出してください。動的にすることもできますが、重要なのは、イベントが発生するたびに、unlocker がミューテックスのロックを解除する前に numberOfWaiters が正しくなければならないということです。
createEvent(numberOfWaiters)
{
event->numberOfWaiters = numberOfWaiters;
event->csv = semCCreate(SEM_Q_FIFO, 0);
return event;
}
numberOfWaiters について意地悪になることはできません:DI はもう一度言います: ロック解除ツールがミューテックスのロックを解除する前に、numberOfWaiters は正しくなければなりません。動的にする (それが必要な場合) には、setNumWaiters(numOfWaiters) 関数を追加し、ロック解除ツールがミューテックスのロックを解除する前に、wait_for 関数でそれを呼び出すことができます。
最後のトリックとして、上記で述べたように、1 つのタスクがミューテックスのロック解除を担当し、残りのタスクはシグナルを待機するという前提があります。つまり、1 つのタスクだけが上記の wait_for() 関数を呼び出し、残りのタスクのタスクは、wait(event) 関数を呼び出すだけです。
これを念頭に置いて、numberOfWaiters は次のように計算されます。
- wait() を呼び出すタスクの数
- wait_for() を呼び出すタスクの場合はプラス 1
もちろん、本当に必要な場合はこれをより複雑にすることもできますが、通常は 1 つのタスクがイベントをトリガーしますが、多くのタスクはそれが完了したことを知りたいため、これが機能する可能性があります。
ただし、基本的な流れは次のとおりです。
init()
{
event->createEvent(3);
}
eventHandler()
{
locker l(mutex);
doEventProcessing();
signal(event);
}
taskA()
{
doOperationThatTriggersAnEvent();
wait_for(mutex);
eventComplete();
}
taskB()
{
doWhateverIWant();
// now I need to know if the event has occurred...
wait(event);
coolNowIKnowThatIsDone();
}
taskC()
{
taskCIsFun();
wait(event);
printf("event done!\n");
}
上記を書くと、すべてのオブジェクト指向の概念が死んでいるように感じますが、うまくいけば、実際には wait と wait_for は同じパラメーターを取るか、パラメーターを持たずに、すべてのデータを持つ同じクラスのメンバーにする必要があります。知っておく必要があります... しかし、それがどのように機能するかの概要です。