2007-10-17 [長年日記]
λ. RubyでSTM
後で書く。
【2008-02-11 追記】 後で書こうと思いつつ、ずっと忘れていたが、ようやくまとめた。
<URL:http://www.tom.sfc.keio.ac.jp/~sakai/archives/ruby-stm-0.0.1.tar.gz>
使い方
STM.atomically{ ... } でトランザクションの実行。必要な条件が満たされていない場合などには、STM.retry で現在のトランザクションの再試行。STM.or(proc1, proc2, ...) は引数のProcを順番に試行して最初に成功した結果になる。全ての引数が再試行になったら、STM.or全体として再試行になる。
STM::Var が Haskell の TVar 相当で、トランザクションの対象となる「変数」。トランザクション中に、STM::Var.new(value) で作って、STM::Var#get, STM::Var#set(val) で読み書き。
STM::Array というのも用意してあって、こちらはトランザクションの対象となる「配列」。Rubyの配列で使える操作はほぼそのまま使える。
それから、STM::MVar が Haskell の TMVar 相当。
実装
シンプルな実装。
トランザクション中で最初に STM::Var を読み込んだときの値と、そのトランザクション中で更新した結果の値を、トランザクションのオブジェクトに記録。コミット時にはグローバルなロック*1をかけて、記録された値と現在の値に齟齬がないことを確認し、それから実際の STM::Var に対して書き込む。もし齟齬があった場合には最初からやり直し。
再試行が起こった場合には、それまでに読んだ変数に対してそのトランザクションを登録し、それらの変数に対して変更がコミットされるまで待つ。
他の実装
実装後に気づいたのだけど、<URL:http://www.atdot.net/~ko1/diary/200701.html#d25> で笹田さんも実装していた。 また、<URL:http://moonbase.rydia.net/mental/blog/programming/ruby-stm> というものもあるようだ。
*1 グローバルなロックをかけるよりは、CASやLL/SCを使いたかったけど、Rubyには無いしね。