Project

General

Profile

Bug #6701

Updated by nobu (Nobuyoshi Nakada) about 7 years ago

# =begin 

 = 概要 

 `/#{expr}/o` は,`expr` /#{expr}/o は,expr はたかだか一回しか実行されない,後から評価したときは `expr` の評価値(を用いた正規表現)が返されるという意味になります.しかし,`expr` expr の評価値(を用いた正規表現)が返されるという意味になります.しかし,expr 中で例外などで大域脱出が発生し,再度評価しようとすると,まだ `expr` expr は実行中であると認識されるため,ブロックします. 

 # = 現象 

 次のようなコードが止まりません. 

 ~~~ruby 
   

   2.times{ 
     catch(:escape){ 
       p:before 
       r = /#{throw :escape}/o 
       # まだ,1度目の処理が終わってないとみなされているため, 
       # 2回目に実行しようとすると,その1度目の終了を待つ 
       # (もちろん,1度目は throw によって cancel されている→デッドロック) 
       p:after 
     } 
   } 
 ~~~ 

 スレッドを絡めるとこんな感じです. 

 ~~~ruby 
   

   (1..2).map{ 
     Thread.new{ 
       begin 
         r = /#{raise}/o 
         # あるスレッドが実行しようとするが,例外でキャンセルされる 
         # 別のスレッドは,キャンセルされた実行の終了を待つ 
         # (もちろん,終わるわけがないのでデッドロック) 
       rescue 
         p :raised 
       ensure 
         p :exit 
       end 
     } 
   }.each{|t| t.join} 
 ~~~ 

 # = 修正案 

 `expr` expr が例外でキャンセルされたら,ちゃんと「未実行状態」に戻して,待ってるスレッドがいればそのスレッドが `expr` expr をやり直すべきではないかと思います.具体的には,ちゃんと `ensure` ensure 的な処理を入れる様に改造します. 

 特に何も意見がなければ,2.0 はこの方針で直します. 

 1.9 は,もうこのまま,でしょうか.これまで文句が来たことが無いので,誰も `/#{expr}/o` /#{expr}/o なんて使ってないってことですかね. 

 別の選択肢として,例外で抜けたら `expr` expr の評価値を `nil` nil にする,という案もありますが,なぜ `nil` nil なのか,とかあまり説得力のある理由が思いつきません. 

 # = 余談 

 ところで,これを考えていて,次の様な例が思い当たりました. 

 ~~~ruby 
   

   def foo 
     r = /#{foo}/o 
   end 
  
   foo 
 ~~~ 

 これ,どうするべきなんだろう.こんなこと書くな,でしょうか.それとも,deadlock というか,recursive な once なのでエラー,とするのがいいでしょうか(エラーがいい気がするな). 

 ちなみに,1.8 だと問答無用で実行しちゃうようで: 

 ~~~ 
   

   t.rb:3:in `foo': stack level too deep (SystemStackError) 
 ~~~ 


 =end 

Back