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

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

Zig版ASP3カーネルのtmevtb_enqueue_reltim

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

前回から1週間あまり間があいてしまいました。 みなさん、いかがお過ごしでしたでしょうか?

今回見ていくのはdly_tskからよばれていたtmevtb_enqueue_reltim関数です。 サービスコールではなくて、カーネル内部で呼び出している関数になります。

いつものようにtmevtb_enqueue_reltim関数の定義を見ていきますね。 kernel/time_event.zigに定義があります。

///
///  相対時間指定によるタイムイベントの登録
///
///  timeで指定した相対時間が経過した後にコールバック関数が呼び出され
///  るように,p_tmevtbで指定したタイムイベントブロックを登録する.コー
///  ルバック関数,コールバック関数へ渡す引数は,p_tmevtbが指すタイム
///  イベントブロック中に設定しておく.
///
pub fn tmevtb_enqueue_reltim(p_tmevtb: *TMEVTB, time: RELTIM) void {
    // 現在のイベント時刻とタイムイベントの発生時刻を求める[ASPD1026].
    update_current_evttim();
    p_tmevtb.evttim = calc_current_evttim_ub() +% time;

    // タイムイベントブロックをヒープに挿入する[ASPD1030].
    tmevtb_insert(p_tmevtb);

    // 高分解能タイマ割込みの発生タイミングを設定する[ASPD1031][ASPD1034].
    if (!in_signal_time and p_tmevtb.index == 0) {
        set_hrt_event();
    }
}

相対時間というのはdly_tskの引数にも使われていたRELTIM型の時間のことで、単位はmsです。

tmevtb_enqueue_reltim関数をざっと見ていくと、久々にZigらしいところがありました! これだけでも今回は価値がありますね。

何かというと、+%演算子です。

+%演算子を最初に見た私の印象は「剰余算に関係あるのかな?」というものでした。 確かに剰余算にも関係あるのですが、どちらかというと+の方が主で、これは加算のための演算子なんです。

普通の加算と違うのは、評価結果の内部がその型の最大値+1を法とする剰余になることが保証されている点です。 いいかえると、オーバーフロー発生時に2の補数表現のラップアラウンド動作を保証されています。

Cの場合も、普通はそれと同じ動作をするんですが、あくまでも規格上はオーバーフローが発生すると未定義の動作になるんですよね。 ちゃんと言語仕様で振る舞いが保証されていると安心感が違います。

tmevtb_enqueue_reltim関数のその他の部分は別の関数を呼び出しているところぐらいでしょうか。

まずは、update_current_evttim関数を見ていきましょう。 定義は同じくkernel/time_event.zigにあります。

///
///  現在のイベント時刻の更新
///
///  current_evttimとcurrent_hrtcntを,現在の値に更新する.
///
pub fn update_current_evttim() void {
    var new_hrtcnt: HRTCNT = undefined;
    var hrtcnt_advance: HRTCNT = undefined;
    var previous_evttim: EVTTIM = undefined;

    new_hrtcnt = target_timer.hrt.get_current();    //[ASPD1013]
    hrtcnt_advance = new_hrtcnt -% current_hrtcnt;  //[ASPD1014]
    if (TCYC_HRTCNT != null) {
        if (new_hrtcnt < current_hrtcnt) {
            hrtcnt_advance +%= TCYC_HRTCNT.?;
        }
    }
    current_hrtcnt = new_hrtcnt;                    //[ASPD1016]

    previous_evttim = current_evttim;
    current_evttim +%= @intCast(EVTTIM, hrtcnt_advance);    //[ASPD1015]
    boundary_evttim = current_evttim -% BOUNDARY_MARGIN;    //[ASPD1011]

    if (monotonic_evttim -% previous_evttim
                              < @intCast(EVTTIM, hrtcnt_advance)) {
        if (current_evttim < monotonic_evttim) {    //[ASPD1045]
            systim_offset +%= @as(SYSTIM, 1) << @bitSizeOf(EVTTIM);
        }
        monotonic_evttim = current_evttim;          //[ASPD1042]
    }
}

この関数の中でも+%演算子がたくさん使われています。 似た演算子で-%も使われていますが、これは減算を行ってオーバーフローが起きたときに%+と同じように処理するものですね。

@intCastは指定した整数型に変換するためのビルトイン関数です。 このビルトイン関数は以前にも登場したのですが、そのときはスルーしていました。

@intCastは元の値を維持して型だけを変換します。 といっても変換後の型でもとの値を表現できない場合もあり得ます。 その場合には未定義の動作になるのですが、Zigの未定義の動作はsafety-protected Undefined Behavior(=は安全性保護付き未定義動作)です。 「安全性保護付き未定義動作」という日本語はZenから借用しました。

「安全性保護付き未定義動作」が起きたときは、@setRuntimeSafetyビルトイン関数を使って安全性チェックを有効にすることができます。 安全性チェックが失敗するとクラッシュするようです。

@asビルトイン関数は型変換というより型強制です。 値が安全で確実に指定した型にできるときにだけ変換に使うことが推奨されています。

tmevtb_enqueue_reltim関数からはほかにも関数が呼ばれているのですが、Zigとして見るべき点はなさそうなので今回はパスします。

Zig版ASP3カーネルに関しては、Zigの習得目的という意味ではあまり面白みがなくなってしまいました。 今回はこのテーマはいったん終了にしたいと思います。

このブログ自体は別の話題で続けていくつもりですので、今後もよろしくお願いします。

それでは!!