Bug #3674
closeddRuby サーバプロセスを停止する時に時間がかかることがある
Description
=begin
CentOS release 5.4 Linux のマシンで dRuby のサーバプロセスを停止させる時に CPU 利用した状態で
数秒から十数秒程度時間がかかることがあります。
Redhat Enterprise Linux, Ubuntu 8.10, openSUSE 11.1 等の他の環境で同じものを動かしていますが
今のところこの現象がみられるのは CentOS のところだけのようです。
(CPU の種類/速度や libpthread のバージョンも異なるのでその影響もあるかもしれません)
たとえば以下のようなサンプルでも稀に発生します。
- server.rb
require "drb"
class S
def m1
puts "S#m1 called"
sleep 10
end
end
begin
front = S.new
uri = ARGV[0] || "druby://localhost:0"
puts DRb.start_service(uri, front).uri
DRb.thread.join
rescue Interrupt
ensure
DRb.stop_service
end
- client.rb
require "drb"
obj = DRbObject.new_with_uri ARGV[0]
obj.m1
- 実行例
$ ruby server.rb druby://localhost:10000
druby://localhost:10000
S#m1 called
<- Ctrl-C で中断
(別端末で実行)
$ ruby client.rb druby://localhost:10000
発生した時に gdb attach してみると DRbServer#kill_sub_thread で dRuby 要求を処理している
Thread を kill して回っているところで止まっているようです。
以下のように Thread.pass を挿入すると発生しなくなりました。
Index: lib/drb/drb.rb¶
--- lib/drb/drb.rb (revision 28880)
+++ lib/drb/drb.rb (working copy)
@@ -1421,6 +1421,7 @@
list.each do |th|
th.kill if th.alive?
end
-
Thread.pass list = @grp.list end end
=end
Updated by nagachika (Tomoyuki Chikanaga) over 14 years ago
=begin
[ruby-dev:42035] の咳さんの返信にフォローします。
えっと、Rubyのスレッドかなにか問題の報告と考えてよいのでしょうか?
ここにThread.passが必要な理由がわかりません。
私がなにかしたほうがよいのでしょうか?
なにを指摘しているのかあいまいな報告ですみませんでした。
Thread#kill のソース(rb_thread_kill)をみると、特に明示的なスレッドの切り替えを
発生させるようなところがなかったため、
DRbServer#kill_sub_thread の以下の while ループはタイマーで強制的に切り替えが発生するまで
ぐるぐる回り続けるのではないかと考えました。
list = @grp.list
while list.size > 0
list.each do |th|
th.kill if th.alive?
end
list = @grp.list
end
先刻 irb でちょっと実験してみましたが kill で停止した Thread は
実際に終了するまで(?) ThreadGroup に残っているようです。
tg = ThreadGroup.new
=> #ThreadGroup:0x8e7cc20
thr = Thread.new{ tg.add Thread.current; sleep }
=> #<Thread:0x8e96008 sleep>
thr.kill; p tg.list
[#<Thread:0x8e96008 aborting>]
=> [#<Thread:0x8e96008 aborting>]
tg.list
=> []
ということで Thread#kill で停止させた Thread が本当に停止して ThreadGroup から消えるには
明示的なスレッド切り替えが必要なのではないか(充分とは限りませんが)と考えた結果が先のパッチです。
しかし Thread#kill または ThreadGroup の挙動が意図したものでないという可能性もあります。
これはよくわかりません。どうなのでしょうか?
個人的には Thread#kill するたびに切り替えするのは重そうなので現状のほうがいいような気がします。
=end
Updated by mame (Yusuke Endoh) over 14 years ago
=begin
遠藤です。
2010年8月16日9:46 Tomoyuki Chikanaga redmine@ruby-lang.org:
えっと、Rubyのスレッドかなにか問題の報告と考えてよいのでしょうか?
ここにThread.passが必要な理由がわかりません。
私がなにかしたほうがよいのでしょうか?なにを指摘しているのかあいまいな報告ですみませんでした。
Thread#kill のソース(rb_thread_kill)をみると、特に明示的なスレッドの切り替えを
発生させるようなところがなかったため、
DRbServer#kill_sub_thread の以下の while ループはタイマーで強制的に切り替えが発生するまで
ぐるぐる回り続けるのではないかと考えました。
そうだと思います。[ruby-dev:40440] と同じような問題ですね。
ということで Thread#kill で停止させた Thread が本当に停止して ThreadGroup から消えるには
明示的なスレッド切り替えが必要なのではないか(充分とは限りませんが)と考えた結果が先のパッチです。
1.9 では Thread.pass はアーキテクチャによってはあてにならないらしい
([ruby-dev:40060] の最後参照) ので、[ruby-dev:40440] のように join
した方がいいかと思いました。
--
Yusuke Endoh mame@tsg.ne.jp
=end
Updated by mame (Yusuke Endoh) over 14 years ago
=begin
遠藤です。
2010年8月17日0:00 Masatoshi SEKI m_seki@mva.biglobe.ne.jp:
Thread#joinって最後の値を取り出しますよね。たしか例外で終了した
場合は例外が再びあがるんじゃなかったけ。
はい。
begin
thread.kill.join
rescue なんとかえらー
endみたいにしたほうがよいものですか?
kill_sub_thread の文脈を理解していないのですが、例外が上がる
可能性があるならそうしたほうがよさそうですね。
あと、join で死ぬのを待つなら while list.size > 0 は要らない
かも。
diff --git a/lib/drb/drb.rb b/lib/drb/drb.rb
index cacca14..cfed15c 100644
--- a/lib/drb/drb.rb
+++ b/lib/drb/drb.rb
@@ -1417,11 +1417,11 @@ module DRb
grp = ThreadGroup.new
grp.add(Thread.current)
list = @grp.list
- while list.size > 0
- list.each do |th|
-
th.kill if th.alive?
- list.each do |th|
- begin
-
th.kill.join if th.alive?
- rescue Exception
end
- list = @grp.list
end
end
end
--
Yusuke Endoh mame@tsg.ne.jp
=end
Updated by nagachika (Tomoyuki Chikanaga) over 14 years ago
=begin
近永です。
ありがとうございます。
この差分でしばらく試してみます。
2010年8月18日4:13 Masatoshi SEKI m_seki@mva.biglobe.ne.jp:
咳といいます。
On 2010/08/16, at 9:46, Tomoyuki Chikanaga wrote:
チケット #3674 が更新されました。 (by Tomoyuki Chikanaga)
[ruby-dev:42035] の咳さんの返信にフォローします。
えっと、Rubyのスレッドかなにか問題の報告と考えてよいのでしょうか?
ここにThread.passが必要な理由がわかりません。
私がなにかしたほうがよいのでしょうか?なにを指摘しているのかあいまいな報告ですみませんでした。
Thread#kill のソース(rb_thread_kill)をみると、特に明示的なスレッドの切り替えを
発生させるようなところがなかったため、
DRbServer#kill_sub_thread の以下の while ループはタイマーで強制的に切り替えが発生するまで
ぐるぐる回り続けるのではないかと考えました。遠藤さんの指摘をもとにこんな風に変更したらどうかと思いました。
もしよかったら、試していただけませんか?Index: drb.rb¶
--- drb.rb (revision 29026)
+++ drb.rb (working copy)
@@ -1419,7 +1419,10 @@
list = @grp.list
while list.size > 0
list.each do |th|
th.kill if th.alive?
begin
th.kill.join if th.alive?
rescue Exception
end end list = @grp.list end
=end
Updated by nagachika (Tomoyuki Chikanaga) over 14 years ago
=begin
近永と申します。
確認が遅くなってすみません。
頂いた修正を加えて、現象が発生していた環境で再現スクリプトを2000回くらい回して起きなくなったことを確認しました。
ただ差分を見ていてちょっと気がついたのですが、kill した Thread に ensure 節の処理があって、
それがブロックする可能性があるものだと、その Thread が終了するまで時間がかかることがあり、
その間は他の Thread が kill されず動き続けると思います。
別に手元のプログラムで問題が発生したというわけではないのですが、
元の動作に近づけるにはまず全体に kill を呼んでおいてから join したほうがいいかなと思いました。
Index: lib/drb/drb.rb¶
--- lib/drb/drb.rb (revision 29026)
+++ lib/drb/drb.rb (working copy)
@@ -1418,8 +1418,14 @@
grp.add(Thread.current)
list = @grp.list
while list.size > 0
-
list = list.map do |th|
-
th.kill if th.alive?
-
end.compact list.each do |th|
-
th.kill if th.alive?
-
begin
-
th.join
-
rescue Exception
-
end end list = @grp.list end
=end
Updated by shyouhei (Shyouhei Urabe) about 14 years ago
- Status changed from Open to Assigned
- Assignee set to seki (Masatoshi Seki)
=begin
=end
Updated by nagachika (Tomoyuki Chikanaga) about 14 years ago
=begin
えっと、わたしとしてはローカルで上記のパッチをあててずっと運用していて、うちの用途では問題もないので適用していただきたいと思います。
=end
Updated by mame (Yusuke Endoh) about 14 years ago
=begin
遠藤です。
2010年11月15日22:55 Tomoyuki Chikanaga redmine@ruby-lang.org:
えっと、わたしとしてはローカルで上記のパッチをあててずっと運用していて、うちの用途では問題もないので適用していただきたいと思います。
なんか途中投げにした格好ですみません。私も近永さんのパッチに賛成です。
--
Yusuke Endoh mame@tsg.ne.jp
=end
Updated by ko1 (Koichi Sasada) over 13 years ago
これは,咳さん待ちということになるんでしょうか.
Updated by kosaki (Motohiro KOSAKI) over 13 years ago
http://d.hatena.ne.jp/nagachika/20110613/ruby_trunk_changes_32028_32070 の r32028 の解説を読むと、すでに直っているように読めますが、咳さんの認識はいかがでしょうか?
Updated by nagachika (Tomoyuki Chikanaga) over 13 years ago
- Category set to lib
- Status changed from Assigned to Closed
kill_sub_thread はなくなったので閉じます。
Updated by nagachika (Tomoyuki Chikanaga) over 13 years ago
- ruby -v changed from ruby 1.9.3dev (2010-08-05 trunk 28840) [i686-linux] to -
近永と申します。
失礼しました、チケットは閉じておきました。
そういえば
ruby 1.9.3dev (2011-06-12 trunk 32027) [x86_64-darwin10.7.0]
ころのRubyではdRuby+forkするスクリプトを書いてたら、終了時に
暴走することが多かったです。
再現させられなかったので不確かですが、r32064 で kosaki さんが
GVL の変更をしてスレッド間の切り替えのしかたが変化したので
もしかすると最新の trunk だと起きなくなっているかもしれません。
もしよろしかったら最新版でも試してみていただけますか?