From: "mame (Yusuke Endoh)" Date: 2012-07-05T21:23:24+09:00 Subject: [ruby-dev:45924] [ruby-trunk - Bug #6701][Assigned] once literal doesn't care escape Issue #6701 has been updated by mame (Yusuke Endoh). Status changed from Open to Assigned 遠藤です。 2012年7月5日 19:02 ko1 (Koichi Sasada) : > しかし,expr 中で例外などで大域脱出が発生し,再度評価しようとすると,まだ expr は実行中であると認識されるため,ブロックします. この問題は認識してましたが、以下の通りサボってました。 http://bugs.ruby-lang.org/issues/show/2398 > 特に何も意見がなければ,2.0 はこの方針で直します. 賛成です。直してください。 -- Yusuke Endoh ---------------------------------------- Bug #6701: once literal doesn't care escape https://bugs.ruby-lang.org/issues/6701#change-27828 Author: ko1 (Koichi Sasada) Status: Assigned Priority: Normal Assignee: ko1 (Koichi Sasada) Category: core Target version: 2.0.0 ruby -v: ruby 2.0.0dev (2012-07-05 trunk 36311) [i386-mswin32_100] =begin = 概要 /#{expr}/o は,expr はたかだか一回しか実行されない,後から評価したときは expr の評価値(を用いた正規表現)が返されるという意味になります.しかし,expr 中で例外などで大域脱出が発生し,再度評価しようとすると,まだ expr は実行中であると認識されるため,ブロックします. = 現象 次のようなコードが止まりません. 2.times{ catch(:escape){ p:before r = /#{throw :escape}/o # まだ,1度目の処理が終わってないとみなされているため, # 2回目に実行しようとすると,その1度目の終了を待つ # (もちろん,1度目は throw によって cancel されている→デッドロック) p:after } } スレッドを絡めるとこんな感じです. (1..2).map{ Thread.new{ begin r = /#{raise}/o # あるスレッドが実行しようとするが,例外でキャンセルされる # 別のスレッドは,キャンセルされた実行の終了を待つ # (もちろん,終わるわけがないのでデッドロック) rescue p :raised ensure p :exit end } }.each{|t| t.join} = 修正案 expr が例外でキャンセルされたら,ちゃんと「未実行状態」に戻して,待ってるスレッドがいればそのスレッドが expr をやり直すべきではないかと思います.具体的には,ちゃんと ensure 的な処理を入れる様に改造します. 特に何も意見がなければ,2.0 はこの方針で直します. 1.9 は,もうこのまま,でしょうか.これまで文句が来たことが無いので,誰も /#{expr}/o なんて使ってないってことですかね. 別の選択肢として,例外で抜けたら expr の評価値を nil にする,という案もありますが,なぜ nil なのか,とかあまり説得力のある理由が思いつきません. = 余談 ところで,これを考えていて,次の様な例が思い当たりました. def foo r = /#{foo}/o end foo これ,どうするべきなんだろう.こんなこと書くな,でしょうか.それとも,deadlock というか,recursive な once なのでエラー,とするのがいいでしょうか(エラーがいい気がするな). ちなみに,1.8 だと問答無用で実行しちゃうようで: t.rb:3:in `foo': stack level too deep (SystemStackError) =end -- http://bugs.ruby-lang.org/