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>'
現在の 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 の実装では
のだけど、Lua の semi-coroutine なら必ず Z から A に戻ってくる。
と思ったけど、 fiber_called_in_this_method() の中で、Fiber.yield が呼ばれたら一緒か。でも、普通 呼ばないから大丈夫。
これでどうだ。
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 の入れ子の例も動いた。
最近のコメント