スレッドシステムを、レディキューに一つもスレッドがない状態でスピンするように
変更しました。全てのスレッドが寝た時の割り込み応答性のために。
int
thread_context_switch (int rotate_ready_queue)
{
// already interrupt disabled.
struct thread_control *tc;
struct tc_queue *q;
int i;
// Rotate ready queue request.
if (current_thread->state == THR_RUN && rotate_ready_queue)
{
current_thread->state = THR_READY;
q = __ready_queue (current_thread);
SIMPLEQ_REMOVE_HEAD (q, tc_link);
SIMPLEQ_INSERT_TAIL (q, current_thread, tc_link);
}
// Find next thread.
for (i = 0, tc = NULL; i < THREAD_PRIORITY_MAX; i++)
{
q = thread_ready_queue + i;
if ((tc = SIMPLEQ_FIRST (q)) != NULL)
break;
}
ここまでに変更はありません。レディキューから次に走らせるスレッドがみつかれば
tcにセットされています。
// If saved thread is exists, restore it.
if (tc && saved_thread)
{
次のスレッドはあって、スピンから割り込みが入って、その割り込みハンドラの
中でwakeup_threadが呼ばれて、その呼ばれたスレッドが選ばれた状況です。
DPRINTF ("\t\t\t\t\t%s: spin thread %d restored.\n", __FUNCTION__,
saved_thread->id);
memcpy (&saved_thread->regs, &saved_regs, sizeof (struct reg));
saved_thread = NULL;
スピンから割り込みに入られたスレッドはレジスタセットがスピンしている状
態(下のwhile(1);の部分)を記憶させられてしまっているので、本来のレジスタ
セットに復帰します。(しないと、saved_threadはスケジュールされても延々と
while(1);の部分を走ることになってしまう。)
}
// No thread in ready queue.
if (!tc)
{
レディキューが空の時です。
assert (!saved_thread);
二重には無理。二重に呼ばれるとしたら、スピンから割りこみが入って、割りこみ
ハンドラはどのスレッドもwakeupさせずにこのスケジューラを呼んだ状況。
それは十分に考えられるのだけれど、それはなしにします。どれもwakeupしないな
らスケジューラは呼ばない決まりにします。TSSはできないということ。
saved_thread = current_thread;
memcpy (&saved_regs, ¤t_thread->regs, sizeof (struct reg));
割り込みを開けるために本来の復帰すべき状態を退避しておきます。
DPRINTF ("\t\t\t\t\t%s: spin %d\n", __FUNCTION__, current_thread->id);
intr_enable ();
while (/*CONSTCOND*/1)
; ここで割り込みを待ちます。なんにもしてないので応答性は最高。
sleep命令を入れると応答性が下がるので、スピンです。
assert (0);
/*NOTREACHED*/
このスピンで割りこみを待ちます。スケジューラを呼ばない割り込みハンドラ
であれば、割り込み終了後はここにもどってくるので、ずっとスピンです。ス
ケジューラを呼ぶ割り込みハンドラはは、もう一度、この関数に入ってきて、
上の(tc && saved_thread)処理で退避しておいたレジスタセットに戻るので、
ここでスピンしたスレッドはきちんと元にもどれます。
}
current_thread = tc;
current_thread->state = THR_RUN;
DPRINTF ("switch %d: sp=%x\n", current_thread->id, current_thread->regs.sp);
return 0;
}
要はこのくらいなのだけど、丸一日格闘しました。最初はスピンの中で割り込
み終了を判定してレディキューを探しに行こうとして、そのフラグにCCRのユー
ザビットを割りあててみたのだけど、割り込みハンドラの中でCCRのビットいじっ
てもrteでスタックに退避されたCCRを上書きされてしまうので、だめです。し
かたないので、スタックの一番上にフラグを起くスペースを作ってみたのだけ
ど、それでもどうもうまくいかない。デバッグしながら考え直してやっとこれ。
これがいいかどうかもわからないけれど、ノギスのデータ取りのプログラムで
は納得の応答性が得られたので、よしとします。
デバッグプリントをオンにした時の様子を確認しておきます。
thread_context_switch: thread_context_switch: spin 1
ここでスピンに入った。
thread_context_switch: thread_context_switch: spin thread 1 restored.
割り込みハンドラからのスケジューラ呼び出しで、スピンしていたスレッドの状態は元に戻った。
thread_context_switch: switch 2: sp=847a0
「2」スレッドが割り込みハンドラで起こされた。
monitor_enter:lock by 2
そのスレッドはモニタに入った。
monitor_signal:[2] signal-->1
「1」スレッドのイベント待ち条件がクリアされたので「1」を起こすことにした。
monitor_exit: [2] wakeup------>1
「1」がモニタを出る時に実際に起こされます。
thread_priority: [1] pri 3->0 state = 2
イベント待ちが入口待ちより先に実行されるために、monitor_exitの中で「1」の
優先度は低い3から、「1」の優先度0にまで上げられました。「1」はこの状態では
WAITですが、このあとでthread_wakeupされのでREADYになります。
monitor_exit: unlock by 2
ここでthread_rotate_ready_queueが呼ばれて、
thread_context_switch: switch 1: sp=84500
「1」に制御が移ります。
monitor_wait: lock by 1
イベント待ちで寝ていた「1」が起きてロックを取得しました。
monitor_exit: inherit priority [1] (0->3)
thread_priority: [1] pri 0->3 state = 0
処理が終了したので、継承していた優先度を元に戻しました。
thread_context_switch: switch 2: sp=8476c
「1」のmonitor_exitの最後、優先度継承していたら、thread_priorityを呼ぶので、
「2」に制御が戻ります。(まだ「1」は抜けてない)
thread_context_switch: switch 1: sp=844e4
「2」はなにもすることがなかったので「1」に戻ってきた。
monitor_exit: unlock by 1
ここで「1」がモニタから抜けます。
thread_context_switch: switch 1: sp=8450c
monitor_exit最後のrotate_ready_queueでも自分に戻ってきた。このrotateは
イベントで寝ていたのを起こすためのものなのだけど、誰もいなかったので。
monitor_enter:lock by 1
「1」はまたモニタに入った。
monitor_signal:[1] signal-->3
「3」がイベント待ちなので、3を起こした。
monitor_exit: [1] wakeup------>3
thread_priority: [3] pri 1->3 state = 2
monitor_exit: unlock by 1
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXここ間違い。低い優先度を継承してるXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
これは、優先度継承の時の優先度をカレントスレッドの優先度にしているから。
固定で割り込みスレッドの優先度にしてた時のコードのままだ。どっちに
するか決めておかないと。
thread_context_switch: switch 3: sp=84970
monitor_wait: lock by 3
monitor_exit: inherit priority [3] (3->1)
thread_priority: [3] pri 3->1 state = 0
monitor_exit: unlock by 3
thread_context_switch: switch 3: sp=8497c
monitor_enter:lock by 3
monitor_wait: unlock by 3
thread_context_switch: switch 1: sp=844e8
thread_context_switch: switch 1: sp=84548
---Ready Queue---
<0>:
<1>:
<2>:
<3>: 1
---Thread Status---(remain/total)
[4] W 0 (0/384) app0
[3] W 1 (0/384) sci send
[2] W 0 (0/384) sci recv
[1] R 3 (0/384) root
CCR_PC=0
---Monitor---
W ringbuffer
lock :
event: 3(sci send)
U ringbuffer
lock :
event:
monitor_enter:lock by 1
monitor_wait: unlock by 1
thread_context_switch: thread_context_switch: spin 1
thread_context_switch: thread_context_switch: spin thread 1 restored.
thread_context_switch: switch 4: sp=84bcc
thread_context_switch: thread_context_switch: spin 4
thread_context_switch: thread_context_switch: spin thread 4 restored.
thread_context_switch: switch 2: sp=847a0
monitor_enter:lock by 2
monitor_signal:[2] signal-->1
monitor_exit: [2] wakeup------>1
ちょっと問題をみつけてしまいました。とはいえソフト的にはなんとか満足で
きるとこまでもってこれた。
