From: Yusuke Endoh Date: 2010-04-21T20:54:16+09:00 Subject: [ruby-dev:41031] [Bug #3182] multi-irb may run parallelly Bug #3182: multi-irb may run parallelly http://redmine.ruby-lang.org/issues/show/3182 起票者: Yusuke Endoh ステータス: Open, 優先度: Normal 担当者: Keiju Ishitsuka, カテゴリ: lib, Target version: 1.9.2 ruby -v: ruby 1.9.2dev (2010-04-21 trunk 27432) [i686-linux] いしつかさん 遠藤です。 #3139 で、irb が SEGV するという報告が上がっています。 こんな感じで再現するらしいです。 irb String irb Array kill String jobs jobs jobs #=> SEGV 私の環境で再現はしないのですが、同じ手順を実行すると、kill したあとで、 キー入力が不思議な挙動になりました。 irb(main):001:0> irb String irb#1(String):001:0> irb Array irb#2(Array):001:0> kill String => #, @signal_status=:IN_EVAL, @scanner=#> irb(main):002:0> => [String] irb#2(Array):002:0> j NameError: undefined local variable or method `j' for main:Object from (irb):2 from ../ruby-trunk-local/bin/irb:12:in `
' irb(main):003:0> obs NameError: undefined local variable or method `obs' for main:Object from (irb):3 from ../ruby-trunk-local/bin/irb:12:in `
' 調べてみたところ、multi-irb で複数の irb のスレッドが同時に走り出して いて、複数のスレッドから Readline.readline を呼ぶため入力の奪い合いに なっているようです。 たぶん、これが報告者の環境では SEGV になったのではないかと予想します (再現しないのでわからないですが) 。 SEGV はおいといて、複数の irb スレッドが走りだすのは multi-irb のバグ だと思います。 irb が終了したときに、親の irb スレッドを run するコードがありますが、 kill された時は kill した irb スレッドがすでに走っているので、親を run させるべきでないと思います。 以下のように、kill された irb 一覧を保持しておいて、kill された irb の 終了時には親を起こさないようにしたところ、不思議な挙動はなくなりました。 diff --git a/lib/irb/ext/multi-irb.rb b/lib/irb/ext/multi-irb.rb index 7bb1a7c..df9c789 100644 --- a/lib/irb/ext/multi-irb.rb +++ b/lib/irb/ext/multi-irb.rb @@ -19,6 +19,7 @@ module IRB def initialize # @jobs = [[thread, irb],...] @jobs = [] + @killed_irbs = {} @current_job = nil end @@ -64,6 +65,7 @@ module IRB for key in keys th, irb = search(key) IRB.fail IrbAlreadyDead unless th.alive? + @killed_irbs[irb] = true th.exit end end @@ -104,6 +106,7 @@ module IRB end until assoc = @jobs.pop; end unless @jobs.empty? @jobs.push assoc + @killed_irbs.delete(key) end def inspect @@ -171,7 +174,7 @@ module IRB #fail ensure unless system_exit - @JobManager.delete(irb) + unless @JobManager.delete(irb) if parent_thread.alive? @JobManager.current_job = @JobManager.irb(parent_thread) parent_thread.run @@ -179,6 +182,7 @@ module IRB @JobManager.current_job = @JobManager.main_irb @JobManager.main_thread.run end + end end end end -- Yusuke Endoh ---------------------------------------- http://redmine.ruby-lang.org