めのん@ひとりプログラミング同好会

このブログはすべてフィクションであり、実在の人物、団体とは一切関係ありません

Zig版ASP3カーネルのslp_tsk

こんにちは、めのんです!

平日になりましたので、今回はいつものようにZig版TOPPERS/ASP3カーネルの話題です。 念のため前回のリンクも貼っておきますね。

menonfled.hateblo.jp

今回見ていくのはタスク付属同期機能の代表的なサービスコールのひとつslp_tskです。 slp_tskの定義はkernel/task_sync.zigにあります。

///
///  起床待ち[NGKI1252]
///
pub fn slp_tsk() ItronError!void {
    var winfo: WINFO = undefined;

    traceLog("slpTskEnter", .{});
    errdefer |err| traceLog("slpTskLeave", .{ err });
    try checkDispatch();                        //[NGKI1254]
    {
        target_impl.lockCpuDsp();
        defer target_impl.unlockCpuDsp();

        var p_selftsk = p_runtsk.?;
        if (p_selftsk.flags.raster) {           //[NGKI3455]
            return ItronError.TerminationRequestRaised;
        }
        else if (p_selftsk.flags.wupque > 0) {
            p_selftsk.flags.wupque -= 1;        //[NGKI1259]
        }
        else {
            make_wait(TS_WAITING_SLP, &winfo);  //[NGKI1260]
            traceLog("taskStateChange", .{ p_selftsk });
            target_impl.dispatch();
            if (winfo.werror) |werror| {
                return werror;
            }
        }
    }
    traceLog("slpTskLeave", .{ null });
}

実質二十数行しかない関数ですなんですが、本質的な処理はmake_wait関数にあるはずです。 確かC版のASP3カーネルでもそうなっていましたから。

Zigの文法的な見所はとくにないのですが、C版ではlock_cpu_dspやunlock_cpu_dspというマクロが使われていたので、どこで定義されているのか分かりにくかったのですが、Zig版ではtarget_impl.lockCpuDspのようになっているのでターゲット依存部で定義されていることがすぐにわかります。 こういうのはZigの利点かもしれませんね。

それでは、slp_tskの肝になるmake_wait関数の定義を見ていきましょう。 kernel/wait.zigに定義があるようです。

///
///  待ち状態への遷移
///
///  実行中のタスクを待ち状態に遷移させる.具体的には,実行中のタスク
///  のタスク状態をtstatにしてレディキューから削除し,TCBのp_winfoフィー
///  ルド,WINFOのp_tmevtbフィールドを設定する.
///
pub fn make_wait(tstat: u8, p_winfo: *WINFO) void {
    p_runtsk.?.tstat = tstat;
    make_non_runnable(p_runtsk.?);
    p_runtsk.?.p_winfo = p_winfo;
    p_winfo.* = WINFO{ .p_tmevtb = null };
}

拍子抜けするほど短い関数でした。

内容を見ていくと、p_runtskが参照しているTCBに情報を設定しているほかはmake_non_runnable関数の呼び出しのようです。 make_non_runnable関数も見ていきましょう。 定義はkernel/task.zigにあります。

///
///  実行できる状態から他の状態への遷移
///
///  p_tcbで指定されるタスクをレディキューに挿入する.また,必要な場合
///  には,実行すべきタスクを更新する.
///
pub fn make_non_runnable(p_tcb: *TCB) void {
    const prio = p_tcb.prio;
    const p_queue = &ready_queue[prio];

    p_tcb.task_queue.delete();
    if (p_queue.isEmpty()) {
        ready_primap.clear(prio);
        if (p_schedtsk == p_tcb) {
            assert(dspflg);
            p_schedtsk = if (ready_primap.isEmpty()) null
                else searchSchedtsk();
        }
    }
    else {
        if (p_schedtsk == p_tcb) {
            assert(dspflg);
            p_schedtsk = getTCBFromQueue(p_queue.p_next);
        }
    }
}

見た感じだと、レディキューから指定したタスクを取り除いて、次に実行するタスクをp_schedtskに設定しているだけのようです。

slp_tskはセマフォやイベントフラグのようなタスク間同期・通信オブジェクトの状態変化を待ているわけではないですし、タイマーイベントを待っているわけでもありません。 wup_tskで起こされるまで待つだけなので、別のキューにつなぐわけでもなく単純なんだろうなと思いました。

次回はwup_tskかtslp_tskを見ていきたいのですが、タイマーイベントを先に見るならdly_tskが先の方がいいかもしれませんね。 今夜までに考えておくことにします。

それでは!!