ruck

ruckChucKの軽量スレッド周りの処理をrubyに移植したもので、RubyKaigi2010でもプレゼンがあった。タイトルだけ見るとゲームの話かと思うけどゲームに関しては少しだけ触れられていてほとんどはruckとChucKの話だった。ChucKは今までにも何度かおもしろそうだなーと思ったことがあったのでruckのコードを読んでみた。ChucKの論文眺めてたのもその為です。

ruckはclock, event_clock, shred, shredulerから構成されている。shredulerに対してshredを登録するとそのshredはshredulerが持つclockに登録される。shreduler実行ループでは以下のような処理が行われている。

  • clockから現時刻のshred取り出し
  • clock時刻進める
  • 取り出したshred実行

clockがshred管理、スケジューリング、時間制御を担当し、shredulerはそのラッパーという感じ。実際に使うときはshredulerインスタンスのmake_convinientというメソッドを実行するとmodule_evalやincludeで便利なメソッドがいくつか追加され、clockを直接触らなくて済むようになってる。以下はreadmeからmake_convinient使ってるコードの抜粋

@shreduler = Ruck::Shreduler.new
@shreduler.make_convenient

spork do
  %w{ A B C D E }.each do |letter|
    puts "#{letter}"
    Ruck::Shred.yield(1)
  end
end

spork do
  %w{ 1 2 3 4 5 }.each do |number|
    puts "#{number}"
    Ruck::Shred.yield(1)
  end
end

@shreduler.run

shredの実装はruby 1.8系はcallcc, 1.9系はFiberを使ったものになっていてshreduler/clockから制御するときに実際の実装を意識しない。

Ruck::Shred.yieldメソッドはshredからshredulerに制御を戻すためのメソッドで、yieldに渡した時間とshred自身をshredulerのclockに対して再スケジュールするよう指示してshredを停止する。shred停止はfiber実装だと単にFiber.yieldするようになっていてこのあたりは面白かった。

event_clockはclockと似たようなインタフェースを持つが時間でなくイベントに対するshredスケジューリングを行う。clockが時間でソートされた配列なのに対してevent_clockはイベントをキーとした辞書といった感じ。

RubyKaigi2010でのプレゼンではstrong timing, virtual timeまわりの説明に重点が置かれている。通常のthreadとの比較、fast_forwardによるvirtual timeの制御等。threadとの比較で出てくる、初期化を先にやって各スレッドの時間経過は同時だからタイミングがずれないというような説明図の意味もコードを読んでからは理解できた。

関連:

  • ruck-ugen: ChucKでいうところのUGenを担当していて、これをあわせて使うことでChucKのような音生成を実現できる。
  • ruck-realtime: clockのfast_forwardメソッドを上書きしたモジュールで、VMのステップを進める時間を調整している。デフォルトのfast_forward実装だとVM上での時間となり実時間とズレが生じるのでfast_forward上書きでその差分を埋めよう、ということだと思う。