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

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

Zig版ASP3カーネルのtask.cre_task関数

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

前回は静的APIのCRE_TASKの中をのぞいてみました。 だんだんZigらしいコードが出てきていよいよ面白くなってきましたね。

今回はそのCRE_TSKから呼ばれているtask.cre_task関数を見ていくことにします。

CRE_TSKも本当ならCfgData.CRE_TSKと呼ぶべきなので、task.cre_tskも単にcre_tskと呼んでもいいのですが、大文字小文字が違うだけなので紛らわしいですかねー 小文字の方はtask.cre_tskと呼ぶことにしました。

まずは定義を見てみましょう。 task.cre_tsk関数の定義はkernel/task.zigにあります。

///
///  タスクの生成(静的APIの処理)
///
pub fn cre_tsk(comptime ctsk: T_CTSK) ItronError!TINIB {
    // tskatrが無効の場合(E_RSATR)[NGKI1028][NGKI3526][ASPS0009]
    // [NGKI1016]
    //(TA_ACT,TA_NOACTQUE,TARGET_TSKATR以外のビットがセットされている場合)
    try checkValidAtr(ctsk.tskatr, TA_ACT|TA_NOACTQUE | TARGET_TSKATR);

    // itskpriが有効範囲外の場合(E_PAR)[NGKI1034]
    //(TMIN_TPRI <= itskpri && itskpri <= TMAX_TPRIでない場合)
    try checkParameter(validTaskPri(ctsk.itskpri));

    // stkszがターゲット定義の最小値(TARGET_MIN_STKSZ,未定義の場合は1)
    // よりも小さい場合(E_PAR)[NGKI1042]
    try checkParameter(ctsk.stksz >= TARGET_MIN_STKSZ);

    // stkszがターゲット定義の制約に合致しない場合(E_PAR)[NGKI1056]
    try checkParameter((ctsk.stksz & (CHECK_STKSZ_ALIGN - 1)) == 0);

    // ターゲット依存のエラーチェック
    if (@hasDecl(target_impl, "checkCreTsk")) {
        try target_impl.checkCreTsk(ctsk);
    }

    // タスクのスタック領域の確保
    //
    // この記述では,タスク毎に別々のスタック領域が割り当てられる保証
    // がない.コンパイラのバージョンが上がると,誤動作する可能性があ
    // る.
    comptime const stksz = TOPPERS_ROUND_SZ(ctsk.stksz, STACK_ALIGN);
    comptime const stk = if (ctsk.stk) |stk| stk
        else &struct {
            var stack: [stksz]u8 align(STACK_ALIGN) = undefined;
        }.stack;

    // タスク初期化ブロックを返す
    return TINIB{ .tskatr = ctsk.tskatr,
                  .exinf = ctsk.exinf,
                  .task = ctsk.task,
                  .ipri = internalTaskPrio(ctsk.itskpri),
                  .tskinictxb = if (@hasDecl(target_impl, "TSKINICTXB"))
                         target_impl.genTskIniCtxB(stksz, stk)
                     else .{ .stksz = stksz, .stk = stk, }, };
}

この関数はTINIB、つまりタスク初期化ブロックを組み立てるためのもののようです。

前半はパラメータチェックなので省略します。 今回は後半部分から見ていくことにします。

後半部分の最初でTOPPERS_ROUND_SZ関数を呼び出しています。 この関数、C版ではマクロだったんですがZig版では普通の関数になっています。

一応説明しておくと、この関数は第2引数でしていした値の倍数になるように第1引数を切り上げるためのものです。 スタック領域なので、どんな型の変数を使ってもアラインメントの問題が起きないようにするために必要になります。

次に出てくるstkの初期化部分がZigらしいですね。 Zigではifも式なので何らかの評価結果の値を持ちます。 Cでいえば? : を使う三項の条件演算のような使い方ができます。

T_CTSK.stkはnullableでしたので、nullでなければ値が|stk|で指定したstkに格納されます。 そしてそのままstkをifの評価結果としています。

nullの場合は無名構造体を作って、その中にstackという配列型のフィールドを作っています。 そしてそのポインタを評価結果としているようです。

ここでいくつか見どころがあります。

ひとつはalignで、これは変数のアラインメントを指定するのだと思います。 こういう記述が楽にできるのがいいですね。 CでもC11から_Alignas指定子が導入されたんですけど、Zigには初めから備わっています。

次にundefinedです。 Zigでは変数は必ず初期化しないといけないのですが、明示的にundefinedを指定することであえて初期化を省略できるようです。

task.cre_tsk関数の最後でTINIB型のタスク初期化ブロックを組み立てて返しています。

これに関してはそんなに見どころはないのですが、ひとつだけ。 @hasDeclというのはビルトイン関数のひとつで、引数で指定した名前の宣言があるかどうかを判定するようです。 今回はターゲット依存部にTSKINICTXBという名前の宣言があるかどうかを判定しています。

はい。 これで一通りtask.cre_task関数を読み終わりました。

次回のネタはまたこれから考えますね。

それでは!!