Zig版ASP3カーネルのwup_tsk
こんばんは、めのんです!
今日のお昼はタスク付属同期機能のサービスコールslp_tskを見ていきました。
slp_tskはタスクを起床待ち、もっと簡単な言葉を使えば寝かしつけるためのサービスコールでした。 タスクを寝かしつけたままにするわけにはいきませんので、どこかで起こしてあげないといけませんね。 そのためのサービスコールがwup_tskです。 定義はslp_tskと同じくkernel/task_sync.zigにあります。
/// /// タスクの起床[NGKI3531] /// pub fn wup_tsk(tskid: ID) ItronError!void { var p_tcb: *TCB = undefined; traceLog("wupTskEnter", .{ tskid }); errdefer |err| traceLog("wupTskLeave", .{ err }); try checkContextUnlock(); //[NGKI1265] if (tskid == TSK_SELF and !target_impl.senseContext()) { p_tcb = p_runtsk.?; //[NGKI1275] } else { p_tcb = try checkAndGetTCB(tskid); //[NGKI1267] } { target_impl.lockCpu(); defer target_impl.unlockCpu(); if (isDormant(p_tcb.tstat)) { //[NGKI1270] return ItronError.ObjectStateError; } else if (isWaitingSlp(p_tcb.tstat)) { wait_complete(p_tcb); //[NGKI1271] requestTaskDispatch(); } else if (p_tcb.flags.wupque < TMAX_WUPCNT) { p_tcb.flags.wupque += 1; //[NGKI1273] } else { return ItronError.QueueingOverflow; //[NGKI1274] } } traceLog("wupTskLeave", .{ null }); }
今回もZigの言語使用としては見所はとくにありません。 関数の肝になるのはwait_complete関数とrequestTaskDispatch関数のようです。
このうちrequestTaskDispatch関数はターゲット依存部で定義されたディスパッチ関数を読んでいるだけだと思いますし実際そうです。
ですので、今回はwait_complete関数をさらに見ていくことにします。 定義はkernel/wait.zigにあります。
/// /// 待ち解除 /// /// p_tcbで指定されるタスクの待ち状態を解除する.具体的には,タイムイ /// ベントブロックが登録されていれば,それを登録解除する.また,タス /// ク状態を更新し,待ち解除したタスクからの返値をnull(エラー無し) /// とする.待ちキューからの削除は行わない. /// pub fn wait_complete(p_tcb: *TCB) void { wait_dequeue_tmevtb(p_tcb); p_tcb.p_winfo.* = WINFO{ .werror = null }; make_non_wait(p_tcb); }
これはさらにwait_dequeue_tmevtb関数も見る必要がありそうですね。 定義はやはりkernel/wait.zigにあります。
/// /// 時間待ちのためのタイムイベントブロックの登録解除 /// /// p_tcbで指定されるタスクに対して,時間待ちのためのタイムイベントブ /// ロックが登録されていれば,それを登録解除する. /// pub fn wait_dequeue_tmevtb(p_tcb: *TCB) void { if (p_tcb.p_winfo.p_tmevtb) |p_tmevtb| { tmevtb_dequeue(p_tmevtb); } }
待ったました! という感じです。 久々に登場する新しいZigの文法です。
ifのあとに||で囲んだ変数には条件式の値が入ります。 どういうことかというとifの条件式にOptional、つまりnullableに評価される式を書いた場合、nullではない(=true)に評価された場合に、値が||で囲んだ変数に格納されます。 上の例でいえば、p_tcb.p_winfo.p_tmevtbがnullでなければその値がp_tmevtbに格納されます。 このあたりはいかにもZigらしいですね。
さらにはmake_non_wait関数も見る必要があります。 これも定義はkernel/wait.zigにあります。
/// /// 待ち解除のためのタスク状態の更新 /// /// p_tcbで指定されるタスクを,待ち解除するようタスク状態を更新する. /// 待ち解除するタスクが実行できる状態になる場合は,レディキューにつ /// なぐ. /// pub fn make_non_wait(p_tcb: *TCB) void { assert(isWaiting(p_tcb.tstat)); if (!isSuspended(p_tcb.tstat)) { // 待ち状態から実行できる状態への遷移 p_tcb.tstat = TS_RUNNABLE; traceLog("taskStateChange", .{ p_tcb }); make_runnable(p_tcb); } else { // 二重待ち状態から強制待ち状態への遷移 p_tcb.tstat = TS_SUSPENDED; traceLog("taskStateChange", .{ p_tcb }); } }
これはとくに見所はありませんね。 make_runnable関数はact_tskから呼ばれていたmake_active関数とは微妙に異なるのでしょうね。 定義はkernel/task.zigにあります。
/// /// 実行できる状態への遷移 /// /// p_tcbで指定されるタスクをレディキューに挿入する.また,必要な場合 /// には,実行すべきタスクを更新する. /// pub fn make_runnable(p_tcb: *TCB) void { const prio = p_tcb.prio; ready_queue[prio].insertPrev(&p_tcb.task_queue); ready_primap.set(prio); if (dspflg) { if (p_schedtsk == null or prio < p_schedtsk.?.prio) { p_schedtsk = p_tcb; } } }
act_tskを見たときにはmake_active関数まで深掘りしていなかったと思うので、この機会に見ていくことにします。
/// /// 休止状態から実行できる状態への遷移 /// /// p_tcbで指定されるタスクの状態を休止状態から実行できる状態とする. /// pub fn make_active(p_tcb: *TCB) void { target_impl.activateContext(p_tcb); p_tcb.tstat = TS_RUNNABLE; traceLog("taskStateChange", .{ p_tcb }); make_runnable(p_tcb); }
どうやらmake_active関数は休止状態から実装可能状態へ遷移させる処理が入っているだけのようです。 あとはmake_runnable関数の仕事ですね。
make_runnable関数はレディキューに登録して、必要ならp_schedtskを設定しているだけですね。 だんだんZigにも慣れてきてスラスラ読めるようになってきました。
それでは今回はここまでにします。
明日からは少し更新頻度が落ちるかもしれませんが、なるべく1日に1回以上は更新できるように頑張りますので応援お願いします。
それでは!!