えーと前回のタイマの実装で書いたのだけど, 現状のKOZOSのタイマ処理にはバグがあって, タイマがすでに動作中の状態で kz_timer() を呼び出すと, ualarm()しなおすために現在動作中のタイマも再起動されてしまう.
これを修正するためには,前回のタイマ起動時からの経過時間を測定して その差分を考慮する必要があるのだが,まああまり難しい修正ではないので ちょっと直してみた.
(2009/04/10 ライセンスに関する文書として,KL-01とLICENSEを追加. 詳しくは第43回を参照)
以下は前回からの差分.
まず,kz_timer() の処理用関数である thread_timer() に以下の修正が入っている.
+static struct timeval alarm_tm;
+
static int thread_timer(int msec)
{
kz_timebuf **tmpp;
kz_timebuf *tmp;
+ struct timeval tm;
+ int diffmsec;
tmp = malloc(sizeof(*tmp));
tmp->next = NULL;
tmp->thp = current;
+ gettimeofday(&tm, NULL);
+ if (timers) {
+ diffmsec = (tm.tv_sec - alarm_tm.tv_sec) * 1000 +
+ (tm.tv_usec - alarm_tm.tv_usec) / 1000;
+ if (timers->msec > diffmsec)
+ timers->msec -= diffmsec;
+ else
+ timers->msec = 0;
+ }
+
for (tmpp = &timers; *tmpp; tmpp = &((*tmpp)->next)) {
if (msec < (*tmpp)->msec) {
(*tmpp)->msec -= msec;
break;
}
msec -= (*tmpp)->msec;
}
if (msec == 0) msec++;
tmp->msec = msec;
tmp->next = *tmpp;
*tmpp = tmp;
- ualarm(timers->msec * 1000, 0);
+ alarm_tm = tm;
+ if (tmpp == &timers)
+ ualarm(timers->msec * 1000, 0);
putcurrent();
return 0;
}
gettimeofday()により現在時刻を取得し,
前回 ualarm() が行われた時刻(現在カウント中のタイマが設定された時刻)
からの差分を計算し,現在カウント中のタイマ資源の時刻を修正するというものだ.
従来は例えば100msのタイマが70ms経過したところで150msのタイマを設定すると,
修正後の処理では前回設定時刻からの経過時間が差分として計算に入れられるため, 上のような問題は無い.さらにタイマが先頭に挿入されたときのみ, ualarm()によるタイマ設定を行うようになっている (タイマが動作中でタイマキューの後ろのほうにタイマが挿入されるならば, 現在動作中のタイマを上書きする意味は無いので).
SIGALRM発生時の処理は以下のようになる.
void alarm_handler()
{
kz_timebuf *tmp;
sendmsg(timers->thp, 0, 0, NULL);
tmp = timers;
timers = timers->next;
free(tmp);
if (timers) {
+ gettimeofday(&alarm_tm, NULL);
ualarm(timers->msec * 1000, 0);
}
}
ualarm()によるタイマ設定時には,後々の経過時間測定用に,
その時点の時刻を保存しておくようにしてある.
サンプルプログラムによって動作確認する.
前回のサンプルプログラムに対して,スレッドを3つにして, 100ms,150ms,151msの間隔でタイマをかけて文字列表示を行うようになっている.実行結果は以下のようになる.
% ./koz func1 func2 func3 func1 func2 func3 func1 func2 func1 func3 func1 func2 func3 func1 ^C %まあそれっぽく表示されているようだ.