脳ざらし紀行


2007-06-14

_ Lua の semi-coroutine っぽいのを ruby-1.9 で

ruby-dev で話題になった Lua semi-coroutine。Fiber の呼び出しがループになれない。以下は禁止。

fbr2 = nil
fbr1 = Fiber.new do
  fbr2.yield
end

fbr2 = Fiber.new do
  fbr1.yield
end
fbr1.yield

リングベンチマーク も禁止。

以下のように実装できる。

$cat g.rb
class CoLua < Fiber
  @@h = {}
  alias :__yield__ :yield
  undef :yield
  def resume(*v)
    h = @@h[Thread.current] ||= {}
    raise "can not resume parent fibers" if h[self]
    begin
      h[Fiber.current] = true
      __yield__(*v)
    ensure
      h.delete(Fiber.current)
    end
  end
end

fbr3 = nil
fbr2 = nil
fbr1 = CoLua.new do
  p 1
  fbr2.resume
  p 3
  fbr2.resume
end 

fbr2 = CoLua.new do
  p 2
  CoLua.yield
  p 4
  fbr3.resume
end

fbr3 = CoLua.new do
  p 5
  fbr1.yield  #=> エラー
end
fbr1.resume
$ ruby-1.9 g.rb
1
2
3
4
5
g.rb:10:in `resume': unhandled exception
        from g.rb:35:in `<main>'

_ Lua っぽい semi-coroutine はなかなか良い気がする

現在の Fiber の分かりにくいところは、Fiber#yield がいつ戻ってくるかが、Fiber.new に与えられたブロックだけから分からないところだと思う。

fbr = Fiber.new do
  fiber_called_in_this_method()  #=> X
  other_fiber.yield              #=> Y
  Fiber.yield 1                  #=> Z
end
fbr.yield #=> A この場所にいつ返ってくるか。

現在の ruby-1.9 の実装では

  • X でメソッドを呼んだら、その中で新たに Fiber#yield が呼ばれて、A に返ってくるかも知れない。
  • Y で Fiber#yield が呼ばれて、さらにその Fiber の中でFiber#yield が呼ばれて、A に返ってくるかも知れない。

のだけど、Lua の semi-coroutine なら必ず Z から A に戻ってくる。

と思ったけど、 fiber_called_in_this_method() の中で、Fiber.yield が呼ばれたら一緒か。でも、普通 呼ばないから大丈夫。

_ と思ったら、Fiber#prev は必ずしも親を指さないのか。

これでどうだ。

class CoLua < Fiber
  @@h = {}
  attr_accessor :parent
  def resume(*v)
    @parent = Fiber.current    
    h = @@h[Thread.current] ||= {}
    raise "can not resume parent fibers" if h[self]
    begin
      h[Fiber.current] = true
      self.yield(*v)
    ensure
      h.delete(Fiber.current)
    end
  end

  def self.yield(*v)
    Fiber.current.parent.yield(*v)
  end
end

ruby-dev:30990 の generator の入れ子の例も動いた。

お名前:
E-mail:
コメント:
本日のリンク元

最近のコメント

2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|04|05|
2011|04|
2012|03|07|
2013|01|02|07|
トップ «前の日記(2007-06-13) 最新 次の日記(2007-06-16)» 編集