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

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

Zig版ASP3カーネルのdly_tsk

こんばんは、めのんです!

しばらく間が開いてしまいましたが、Zig版ASP3カーネルの続きを読んでいきたいと思います。

今回はタスク付属同期機能のサービスコールのひとつdly_tskを見ていくことにします。

一応このサービスコールの説明を簡単にしておきますね。

dly_tskはdelay taskの意味で、指定した時間だけタスクを遅延させます。 遅延している間、タスクは待ち状態になります。 そして指定した時間が経過すると実行可能状態に遷移します。

dly_tskの定義はkernel/task_sync.zigにあります。

///
/// 自タスクの遅延[NGKI1348]
///
pub fn dly_tsk(dlytim: RELTIM) ItronError!void {
    var winfo: WINFO = undefined;
    var tmevtb: TMEVTB = undefined;

    traceLog("dlyTskEnter", .{ dlytim });
    errdefer |err| traceLog("dlyTskLeave", .{ err });
    try checkDispatch();                            //[NGKI1349]
    try checkParameter(validRelativeTime(dlytim));  //[NGKI1351]
    {
        target_impl.lockCpuDsp();
        defer target_impl.unlockCpuDsp();

        var p_selftsk = p_runtsk.?;
        if (p_selftsk.flags.raster) {               //[NGKI3456]
            return ItronError.TerminationRequestRaised;
        }
        else {                                      //[NGKI1353]
            p_selftsk.tstat = TS_WAITING_DLY;
            make_non_runnable(p_selftsk);
            p_selftsk.p_winfo = &winfo;
            winfo.p_tmevtb = &tmevtb;
            tmevtb.callback = wait_tmout_ok;
            tmevtb.arg = @ptrToInt(p_runtsk);
            tmevtb_enqueue_reltim(&tmevtb, dlytim);
            traceLog("taskStateChange", .{ p_selftsk });
            target_impl.dispatch();
            if (winfo.werror) |werror| {
                return werror;
            }
        }
    }
    traceLog("dlyTskLeave", .{ null });
}

Zigの言語仕様としてはとくに見所はありませんが、一通り順に追っていきましょう。

最初は状態や引数のチェックですね。 その次はCPUロック状態に遷移させているようです。

自タスクが終了要求状態、つまりp_selftsk.flags.rasterがtrueであればエラーにしています。

そうでなければ自タスクを待機状態に遷移させています。 ここで、p_winfoというTCBのフィールドを設定しています。 p_winfoはたぶん今回が初めて出てきたように思います。

WINFO型の定義を見てみることにしましょう。 定義はkernel/wait.zigにあります。

///
///  待ち情報ブロック(WINFO)の定義
///
///  タスクが待ち状態の間は,TCBおよびそのp_winfoで指されるWINFOを次の
///  ように設定しなければならない.
///
///  (a) TCBのタスク状態を待ち状態(TS_WAITING_???)にする.
///
///  (b) タイムアウトを監視するために,タイムイベントブロックを登録す
///  る.登録するタイムイベントブロックは,待ちに入るサービスコール処
///  理関数のローカル変数として確保し,それへのポインタをWINFOの
///  p_tmevtbに記憶する.タイムアウトの監視が必要ない場合(永久待ちの
///  場合)には,p_tmevtbをNULLにする.
///
///  同期・通信オブジェクトに対する待ち状態の場合には,標準のWINFOに
///  p_wobjcbフィールドを追加した構造体(WINFO_WOBJ,wait.hで定義)に,
///  待ち対象の同期・通信オブジェクトに依存して記憶することが必要な情
///  報のためのフィールドを追加した構造体(WINFO_???)を定義し,WINFO
///  の代わりに用いる.また,以下の(c)〜(e)の設定を行う必要がある.同
///  期・通信オブジェクトに関係しない待ち(起床待ち,時間経過待ち)の
///  場合には,これらは必要ない.
///
///  (c) TCBを待ち対象の同期・通信オブジェクトの待ちキューにつなぐ.待
///  ちキューにつなぐために,task_queueを使う.
///
///  (d) 待ち対象の同期・通信オブジェクトの管理ブロックへのポインタを,
///  WINFO_WOBJのp_wobjcbに記憶する.
///
///  (e) 待ち対象の同期・通信オブジェクトに依存して記憶することが必要
///  な情報がある場合には,WINFO_???内のフィールドに記憶する.
///
///  待ち状態を解除する際には,待ち解除したタスクに対する返値をWINFOの
///  werrorに設定する.werrorが必要なのは待ち解除以降であるのに対して,
///  p_tmevtbは待ち解除後は必要ないため,メモリ節約のために共用体を使っ
///  ている.そのため,wercdへエラーコードを設定するのは,タイムイベン
///  トブロックを登録解除した後にしなければならない.
///
pub const WINFO = union {
    werror: ?ItronError,    // 待ち解除時のエラー
    p_tmevtb: ?*TMEVTB,     // 待ち状態用のタイムイベントブロック
};

大量のコメントがついていて、私が書くことがなくなっちゃいましたね! でも、なんかこのコメントって、C版のまんまな気がしません?

とりあえずそのあたりはいったん目をつぶりましょう。

結局、WINFO型の肝になっている部分はp_tmevtbのようですから、TMEVTB型を調べればよさそうです。 TMEVTB型の定義はkernel/time_event.zigにあります。

///
///  タイムイベントブロックのデータ型の定義
///
pub const TMEVTB = struct {
    evttim: EVTTIM,     // タイムイベントの発生時刻
    index: usize,       // タイムイベントヒープ中での位置
    callback: CBACK,    // コールバック関数
    arg: usize,         // コールバック関数へ渡す引数
};

どうやら、時刻がevttimになったときにコールバック関数callbackを呼び出すようになっているようです。

dly_tskの定義に戻って、tmevtb_enqueue_reltim関数がきっとTMEVTB型のオブジェクトを組み立ててるんだと思います。

かなり長くなってしまったので、tmevtb_enqueue_reltim関数を追いかけるのは次回回しにしたいと思います。

Zigの言語学習としてはもうネタ切れの感がありますが、こうやってカーネルのソースを見ていくのも悪くないですね。

ちょっと最近はブログの投稿をサボりがちになっていますが、頑張って更新していきますのでどうか見捨てずに応援してください。

それでは!!