Project

General

Profile

Actions

Bug #3674

closed

dRuby サーバプロセスを停止する時に時間がかかることがある

Added by nagachika (Tomoyuki Chikanaga) over 13 years ago. Updated almost 13 years ago.

Status:
Closed
Target version:
ruby -v:
-
Backport:

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

Actions #1

Updated by nagachika (Tomoyuki Chikanaga) over 13 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

Actions #2

Updated by mame (Yusuke Endoh) over 13 years ago

=begin
遠藤です。

2010年8月16日9:46 Tomoyuki Chikanaga :

えっと、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

=end

Actions #3

Updated by mame (Yusuke Endoh) over 13 years ago

=begin
遠藤です。

2010年8月17日0:00 Masatoshi SEKI :

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

=end

Actions #4

Updated by nagachika (Tomoyuki Chikanaga) over 13 years ago

=begin
近永です。

ありがとうございます。
この差分でしばらく試してみます。

2010年8月18日4:13 Masatoshi SEKI :

咳といいます。

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

Actions #5

Updated by nagachika (Tomoyuki Chikanaga) over 13 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

Actions #6

Updated by shyouhei (Shyouhei Urabe) over 13 years ago

  • Status changed from Open to Assigned
  • Assignee set to seki (Masatoshi Seki)

=begin

=end

Actions #7

Updated by naruse (Yui NARUSE) over 13 years ago

=begin
これってどうなってますか?
=end

Actions #8

Updated by nagachika (Tomoyuki Chikanaga) over 13 years ago

=begin
えっと、わたしとしてはローカルで上記のパッチをあててずっと運用していて、うちの用途では問題もないので適用していただきたいと思います。

=end

Actions #9

Updated by naruse (Yui NARUSE) over 13 years ago

  • Target version set to 1.9.3

=begin

=end

Actions #10

Updated by mame (Yusuke Endoh) over 13 years ago

=begin
遠藤です。

2010年11月15日22:55 Tomoyuki Chikanaga :

えっと、わたしとしてはローカルで上記のパッチをあててずっと運用していて、うちの用途では問題もないので適用していただきたいと思います。

なんか途中投げにした格好ですみません。私も近永さんのパッチに賛成です。

--
Yusuke Endoh

=end

Updated by ko1 (Koichi Sasada) almost 13 years ago

これは,咳さん待ちということになるんでしょうか.

Updated by kosaki (Motohiro KOSAKI) almost 13 years ago

http://d.hatena.ne.jp/nagachika/20110613/ruby_trunk_changes_32028_32070 の r32028 の解説を読むと、すでに直っているように読めますが、咳さんの認識はいかがでしょうか?

Updated by nagachika (Tomoyuki Chikanaga) almost 13 years ago

  • Category set to lib
  • Status changed from Assigned to Closed

kill_sub_thread はなくなったので閉じます。

Updated by nagachika (Tomoyuki Chikanaga) almost 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 だと起きなくなっているかもしれません。

もしよろしかったら最新版でも試してみていただけますか?

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0