Project

General

Profile

Bug #6701

Updated by nobu (Nobuyoshi Nakada) almost 4 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