Issue #8531 has been reported by ktsj (Kazuki Tsujimoto).

----------------------------------------
Bug #8531: ifuncに渡したブロックが共有される
https://bugs.ruby-lang.org/issues/8531

Author: ktsj (Kazuki Tsujimoto)
Status: Assigned
Priority: Normal
Assignee: ko1 (Koichi Sasada)
Category: 
Target version: current: 2.1.0
ruby -v: ruby 2.1.0dev (2013-06-15 trunk 41311) [x86_64-linux]
Backport: 1.9.3: UNKNOWN, 2.0.0: UNKNOWN


=begin
ifunc(rb_iterateでbl_procとして渡したもの)をブロック付きで呼び出すと、
渡したブロックがifuncのフレーム内に保存されるようになっていますが(r29335)、

 2072 vm_yield_with_cfunc(rb_thread_t *th, const rb_block_t *block,
 ....
 2107     if (blockargptr) {
 2108         VM_CF_LEP(cfp)[0] = VM_ENVVAL_BLOCK_PTR(blockargptr);
 2109     }

これによりメソッドに渡したブロックが次回のメソッド呼び出し時にもそのままフレームに残り続けて
共有されてしまうことがあります。#8341でMethod#to_procの件が報告されていますが、
Symbol#to_procなどでも同様です。

 c = Class.new do
   def foo
     if block_given?
       yield
     else
       puts "No block given."
     end
   end
 end
 
 o = c.new
 f = :foo.to_proc
 f.(o) { puts "Block given." } 
 # => Block given.
 f.(o)
 # => Block given.

特に明文化されていないですが、ifuncには渡されたブロックを参照するのにLEPが利用できず(PASS_PASSED_BLOCKなどが使えない)、
引数として渡されるblockargを使わなければならないという制約があるものと思っています。
(正確に言えば利用できないわけではなくて、RubyレベルでいうProc内でのblock_given?などと同等の動きになる)

現在フレームにブロックを保存するようしているのはこの制約を回避してifuncからrb_method_callなどを期待通りに呼び出すためですが、
ブロックを共有してしまうという弊害がある以上ブロックは引数で渡すという形に修正するのが妥当ではないでしょうか。
この考え方で作ったSymbol#to_procの修正パッチを添付します。(Method#to_procについては#8341に添付しています)

なお、今の公開APIには任意のProcオブジェクトをpassed_blockとしてメソッドを呼び出すための関数がなさそうなので
利便性のために追加したりしていますが、この辺りは議論が必要そうな気がします。
=end



-- 
http://bugs.ruby-lang.org/