タイマー関数

ある関数を周期的に呼び出すには、POSIXタイマーやSIGALRMのようにシグナルハンドラを使うことになるけど、Infernoだとチャネルの同期的な性質を利用して、次のように書ける。

implement Timer;

include "sys.m";
include "draw.m";

sys : Sys;

Timer : module {
  init : fn(nil : ref Draw->Context, nil : list of string);
};

init(nil : ref Draw->Context, nil : list of string)
{
  sync := chan of int;
  n := 10;

  sys = load Sys Sys->PATH;

  spawn timer(sync, n);

  for (i := 0; i < n; i++) {
    <- sync;
    sys->print("%d\n", i);
  }
}

timer(sync : chan of int, n : int)
{
  for (i := 0; i < n; i++) {
    sys->sleep(1000);
    sync <-= 1;
  }
}

変数syncが同期に使っているチャネルだ。

ちなみに、このプログラム実行時にpsでスレッドの状態を見てみると次のようになる。

% ps | grep Timer
      34       19    oraccha    0:00.0       recv    73K Timer
      35       19    oraccha    0:00.0    release     1K Timer[$sys]

5カラム目がスレッドの状態である。Recvはチャネルの受信待ち状態で、メインスレッドが"<- sync;"で待っていることを意味する。当然、Sendという送信待ち状態も存在する。もう一方のスレッド(spawnしたtimerスレッド)の状態はreleaseと表示されている。これはDis VMのレディキューから切り離され、組込みモジュールの実行完了を待っていることを意味する。組込みモジュールということはCで実装されているので、Dis VMの管理外ということになる。"[$sys]"という表示から察しがつくように、Sysモジュール、つまりこの場合はsleep()待ちしている状態である。

スレッドの状態を列挙すると、alt、broken、exiting、ready、recv、release、sendとなる。馴染みがないのはaltという状態だろうが、これは次のエントリで紹介するalt文での待ち状態になる。