alt
チャネルを使ってプログラムを組むと、一つのスレッドで複数のチャネルからの入出力を待ちたくなる。UNIXでいうところのselect/pollのように。そこで使うのが、alt文*1。
ここではtkcmdというTKインタプリタ(?)からコードを引用している。見た目はcase文に近いが、"=>"の左辺(ガード?)はチャネルの入出力になる。例えば、キーボードから入力があったら、tk->keyboard()を呼んで、マウスから入力があったら、tk->pointer()を呼ぶという処理を行っている。イベントループとして使うには、alt文を無限ループで囲む。
for(;;) alt { c := <-wm.kbd => tk->keyboard(Wwsh, c); m := <-wm.ptr => tk->pointer(Wwsh, *m); c := <-wm.ctl or c = <-Wwsh.wreq => tkclient->wmctl(Wwsh, c); line := <-lines => if (line == nil) break Loop; if (line[0] == '#') break; line = line[0:len line - 1]; result := tk->cmd(Wwsh, line); if (result != nil) sys->print("#%s\n", result); if (update) tk->cmd(Wwsh, "update"); sys->print("%s", ps1); menu := <-winctl => tkclient->wmctl(Wwsh, menu); s := <-output => sys->print("#<stdout>%s\n", s); sys->print("%s", ps1); }
tkcmdを実行してから、psの結果を見ると、alt状態でブロックされていることがわかる。
123 123 oraccha 0:00.0 alt 77K Tkcmd 124 2 oraccha 0:00.0 alt 13K Wmsrv 125 123 oraccha 0:00.0 release 16K Bufio[$sys]
IPWLには、alt文の注意点として、複数のスレッドから同時にチャネルに読み書きできない("channel busy"エラーが発生する)という制限があると書かれていたけど、次のプログラムはちゃんと動くようだな。emuだと動くのか、4th editionで改善されたのか?
implement Test; include "sys.m"; include "draw.m"; Test : module { init : fn(nil : ref Draw->Context, nil : list of string); }; init(nil : ref Draw->Context, nil : list of string) { channel := chan of int; spawn writer(channel); spawn writer(channel); spawn reader(channel); spawn reader(channel); } writer(channel : chan of int) { for(;;) alt { channel <-= n => } } reader(channel : chan of int) { for(;;) alt { <- channel => ; } }
altは選択可能なケースが発生するまで待機する。一方、複数のケースが選択可能な場合はいずれか一つがランダムに選択される。この挙動はNewsqueakのselectと同じ。
*1:この構文はOccamのALTが由来だと思われる。Newsqueakではselect〜case文を使っていたので。ちなみにTransputerはこれをサポートするハードウェア機能を持っていた。