[ruby-trunk - Bug #5757][Open] main threadがreadやselectで待っていると、^C でなかなか死なない

Issue #5757 has been reported by Yui NARUSE.


Bug #5757: main threadがreadやselectで待っていると、^C でなかなか死なない

Author: Yui NARUSE
Status: Open
Priority: Normal
Assignee:
Category:
Target version:
ruby -v: ruby 2.0.0dev (2011-12-12 trunk 34015) [x86_64-freebsd9.0]

FreeBSD 9 にて、 ./ruby と起動して ^C を投げてもなかなか死にません。
./miniruby でも -e’$stdin.read’ でも同じです。

仕組みとしては、main thread が read や select で待つ場合、最近は blocking region で
unblock.func に ubf_select を設定するわけですが、この時にシグナルが来ると、

  1. どこかのスレッドの sighandler が呼ばれて、rb_thread_wakeup_timer_thread() が呼ばれる
  2. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  3. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  4. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる

対策はいくつかあり得ると思うのですが、例えば、ubf_select() から rb_thread_wakeup_timer_thread()
を呼ばないようにするとか

Issue #5757 has been updated by Motohiro KOSAKI.

[Bug #5343] (r33307) でのregressionですね(193ブランチだと r33310)。単純にそこを削除すると Bug
#5343が復活してしまうので、タイマー貼る必要があるんじゃないですかね。タイマースレッドを起こすためのタイマーって泥縄感が半端ないけど。

Bug #5757: main threadがreadやselectで待っていると、^C でなかなか死なない

Author: Yui NARUSE
Status: Open
Priority: Normal
Assignee:
Category:
Target version:
ruby -v: ruby 2.0.0dev (2011-12-12 trunk 34015) [x86_64-freebsd9.0]

FreeBSD 9 にて、 ./ruby と起動して ^C を投げてもなかなか死にません。
./miniruby でも -e’$stdin.read’ でも同じです。

仕組みとしては、main thread が read や select で待つ場合、最近は blocking region で
unblock.func に ubf_select を設定するわけですが、この時にシグナルが来ると、

  1. どこかのスレッドの sighandler が呼ばれて、rb_thread_wakeup_timer_thread() が呼ばれる
  2. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  3. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  4. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる

対策はいくつかあり得ると思うのですが、例えば、ubf_select() から rb_thread_wakeup_timer_thread()
を呼ばないようにするとか

Issue #5757 has been updated by Tomoyuki C…

Bug #5343 を登録したものです。
ちゃんと試さずに書いてしまいますが、 #5343 は別の Thread からの kill や raise
での割り込みの場合の問題だったと記憶していますので、ubf_select() での
rb_thread_wakeup_timer_thread()
の呼び出しはタイマースレッドで実行する場合は呼ばないようにすることで両方大丈夫にならないでしょうか。

diff --git a/thread_pthread.c b/thread_pthread.c
index afef326…16f7674 100644
— a/thread_pthread.c
+++ b/thread_pthread.c
@@ -924,6 +924,8 @@ native_sleep(rb_thread_t *th, struct timeval
*timeout_tv)
thread_debug(“native_sleep done\n”);
}

+static pthread_t timer_thread_id;
+
#ifdef USE_SIGNAL_THREAD_LIST
struct signal_thread_list {
rb_thread_t *th;
@@ -1018,7 +1020,8 @@ ubf_select(void *ptr)
{
rb_thread_t *th = (rb_thread_t *)ptr;
add_signal_thread_list(th);

  • rb_thread_wakeup_timer_thread(); /* activate timer thread */
  • if ( pthread_self() != timer_thread_id )
  •   rb_thread_wakeup_timer_thread(); /* activate timer thread */
    
    ubf_select_each(th);
    }

@@ -1047,7 +1050,6 @@ ping_signal_thread_list(void) {
static int ping_signal_thread_list(void) { return 0; }
#endif /* USE_SIGNAL_THREAD_LIST */

-static pthread_t timer_thread_id;
static int timer_thread_pipe[2] = {-1, -1};
static int timer_thread_pipe_owner_process;


Bug #5757: main threadがreadやselectで待っていると、^C でなかなか死なない

Author: Yui NARUSE
Status: Open
Priority: Normal
Assignee:
Category:
Target version:
ruby -v: ruby 2.0.0dev (2011-12-12 trunk 34015) [x86_64-freebsd9.0]

FreeBSD 9 にて、 ./ruby と起動して ^C を投げてもなかなか死にません。
./miniruby でも -e’$stdin.read’ でも同じです。

仕組みとしては、main thread が read や select で待つ場合、最近は blocking region で
unblock.func に ubf_select を設定するわけですが、この時にシグナルが来ると、

  1. どこかのスレッドの sighandler が呼ばれて、rb_thread_wakeup_timer_thread() が呼ばれる
  2. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  3. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  4. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる

対策はいくつかあり得ると思うのですが、例えば、ubf_select() から rb_thread_wakeup_timer_thread()
を呼ばないようにするとか

2011$BG/(B12$B7n(B13$BF|(B19:45 Tomoyuki C.
[email protected]:

Issue #5757 has been updated by Tomoyuki C…

Bug #5343 $B$rEPO?$7$?$b$N$G$9!#(B
$B$A$c$s$H;n$5$:$K=q$$$F$7$^$$$^$9$,!“(B #5343 $B$OJL$N(B Thread $B$+$i$N(B kill
$B$d(B raise $B$G$N3d$j9~$_$N>l9g$NLdBj$@$C$?$H5-21$7$F$$$^$9$N$G!”(Bubf_select()
$B$G$N(B rb_thread_wakeup_timer_thread()
$B$N8F$S=P$7$O%?%$%^!<%9%l%C%I$G<B9T$9$k>l9g$O8F$P$J$$$h$&$K$9$k$3$H$GN>J}Bg>fIW$K$J$i$J$$$G$7$g$&$+!#(B

$B$$$d!“%9%l%C%I$,(BGVL$B6%9g$7$F$J$/$F!”(Bubf_select$B$rEPO?$7$?$1$I$^$@(Bselect$B$rFI$s$G$J$$$H$-(B
ubf_select$B$N(Bkill(VTALRM)
$B$O%9%+$k$N$G!"$b$&0l2s(Bubf_select_each$B$,8F$P$l$k$h$&$K$9$k(B
$B%H%j%C%/$,I,MW$@$H$$$&G’<1$G$9!#(B

$B$b$&%3!<%I$,$&$m3P$($J$N$G$J$K$+8+Mn$H$7$F$k2DG=@-$b$"$j$^$9$,!#(B

(2011/12/14 15:00), Yui NARUSE wrote:

そのもう一回も外しちゃう可能性って無いんでしょうか。
どちらかというと、rb_thread_kill 側で確実に止めを刺す用にするべきな気もします。

もう一回外す可能性があるため,必要が無くなるまで何度もやります.

Issue #5757 has been updated by Yui NARUSE.

ruby -v changed from - to ruby 2.0.0dev (2011-12-12 trunk 34015)
[x86_64-freebsd9.0]

レビューありがとうございます。

いや、スレッドがGVL競合してなくて、ubf_selectを登録したけどまだselectを読んでないとき
ubf_selectのkill(VTALRM) はスカるので、もう一回ubf_select_eachが呼ばれるようにする
トリックが必要だという認識です。

そのもう一回も外しちゃう可能性って無いんでしょうか。
どちらかというと、rb_thread_kill 側で確実に止めを刺す用にするべきな気もします。

rb_thread_kill で rb_thread_wakeup_timer_thread() というのも考えていたんですが、
とりあえず [ruby-dev:44992] で動いてるっぽかったので、各プラットフォームでの確認の利便性を鑑み、
r34038 にてコミットしてます。

Bug #5757: main threadがreadやselectで待っていると、^C でなかなか死なない

Author: Yui NARUSE
Status: Open
Priority: Normal
Assignee:
Category:
Target version:
ruby -v: ruby 2.0.0dev (2011-12-12 trunk 34015) [x86_64-freebsd9.0]

FreeBSD 9 にて、 ./ruby と起動して ^C を投げてもなかなか死にません。
./miniruby でも -e’$stdin.read’ でも同じです。

仕組みとしては、main thread が read や select で待つ場合、最近は blocking region で
unblock.func に ubf_select を設定するわけですが、この時にシグナルが来ると、

  1. どこかのスレッドの sighandler が呼ばれて、rb_thread_wakeup_timer_thread() が呼ばれる
  2. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  3. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  4. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる

対策はいくつかあり得ると思うのですが、例えば、ubf_select() から rb_thread_wakeup_timer_thread()
を呼ばないようにするとか

Issue #5757 has been updated by Tomoyuki C…

File bug5757.patch added

Motohiro KOSAKI wrote:

いや、スレッドがGVL競合してなくて、ubf_selectを登録したけどまだselectを読んでないとき
ubf_selectのkill(VTALRM) はスカるので、もう一回ubf_select_eachが呼ばれるようにする
トリックが必要だという認識です。
少しゆっくり考えてみましたところ、ubf_select() を呼ぶのがタイマースレッドでなければこれまで通り
rb_thread_wakeup_timer_thread() を呼ぶので、 signal_thread_list
に入れたものをタイマースレッドが確実に blocking region
を抜けるまで面倒をみてくれる(定期的にSIGVTALRM送信)ので安泰です。
しかしタイマースレッドがシグナルを処理させるために rb_threadptr_check_signal() からメインスレッドに割り込みする時の
ubf_select() で rb_thread_wakeup_timer_thread() を呼ばなくなるため、シグナル受信処理時に
SIGVTALRM の喪失してメインスレッドが止まったままになる可能性があるのではないかと思います。

thread_timer() の ping_signal_thread_list() で signal_thread_list
をチェックした(そして空だった)後に timer_thread_function() で新たに signal_thread_list
にメインスレッドが追加された場合が問題なわけなので、この後に signal_thread_list
のチェックを入れればいいのではないかと思います。というわけで追加のパッチを添付します。


Bug #5757: main threadがreadやselectで待っていると、^C でなかなか死なない

Author: Yui NARUSE
Status: Open
Priority: Normal
Assignee:
Category:
Target version:
ruby -v: -

FreeBSD 9 にて、 ./ruby と起動して ^C を投げてもなかなか死にません。
./miniruby でも -e’$stdin.read’ でも同じです。

仕組みとしては、main thread が read や select で待つ場合、最近は blocking region で
unblock.func に ubf_select を設定するわけですが、この時にシグナルが来ると、

  1. どこかのスレッドの sighandler が呼ばれて、rb_thread_wakeup_timer_thread() が呼ばれる
  2. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  3. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  4. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる

対策はいくつかあり得ると思うのですが、例えば、ubf_select() から rb_thread_wakeup_timer_thread()
を呼ばないようにするとか

Issue #5757 has been updated by Motohiro KOSAKI.

Status changed from Open to Assigned
Assignee set to Motohiro KOSAKI

すいません。なるべく早く時間取ってレビューします

Bug #5757: main threadがreadやselectで待っていると、^C でなかなか死なない

Author: Yui NARUSE
Status: Assigned
Priority: Normal
Assignee: Motohiro KOSAKI
Category:
Target version:
ruby -v: -

FreeBSD 9 にて、 ./ruby と起動して ^C を投げてもなかなか死にません。
./miniruby でも -e’$stdin.read’ でも同じです。

仕組みとしては、main thread が read や select で待つ場合、最近は blocking region で
unblock.func に ubf_select を設定するわけですが、この時にシグナルが来ると、

  1. どこかのスレッドの sighandler が呼ばれて、rb_thread_wakeup_timer_thread() が呼ばれる
  2. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  3. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  4. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる

対策はいくつかあり得ると思うのですが、例えば、ubf_select() から rb_thread_wakeup_timer_thread()
を呼ばないようにするとか

Issue #5757 has been updated by Motohiro KOSAKI.

Assignee changed from Motohiro KOSAKI to Tomoyuki C.

しばらく考えた結果、[ruby-dev:44999] のパッチで問題ないという結論に至りました。コミットお願い出来ますでしょうか


Bug #5757: main threadがreadやselectで待っていると、^C でなかなか死なない

Author: Yui NARUSE
Status: Assigned
Priority: Normal
Assignee: Tomoyuki C.
Category:
Target version:
ruby -v: -

FreeBSD 9 にて、 ./ruby と起動して ^C を投げてもなかなか死にません。
./miniruby でも -e’$stdin.read’ でも同じです。

仕組みとしては、main thread が read や select で待つ場合、最近は blocking region で
unblock.func に ubf_select を設定するわけですが、この時にシグナルが来ると、

  1. どこかのスレッドの sighandler が呼ばれて、rb_thread_wakeup_timer_thread() が呼ばれる
  2. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  3. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  4. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる

対策はいくつかあり得ると思うのですが、例えば、ubf_select() から rb_thread_wakeup_timer_thread()
を呼ばないようにするとか

Issue #5757 has been updated by Tomoyuki C…

Status changed from Closed to Open

[ruby-dev:44999] に書いたように今度はシグナル処理の漏れの懸念があるので再 open させて頂きます。

Bug #5757: main threadがreadやselectで待っていると、^C でなかなか死なない

Author: Yui NARUSE
Status: Open
Priority: Normal
Assignee:
Category:
Target version:
ruby -v: -

FreeBSD 9 にて、 ./ruby と起動して ^C を投げてもなかなか死にません。
./miniruby でも -e’$stdin.read’ でも同じです。

仕組みとしては、main thread が read や select で待つ場合、最近は blocking region で
unblock.func に ubf_select を設定するわけですが、この時にシグナルが来ると、

  1. どこかのスレッドの sighandler が呼ばれて、rb_thread_wakeup_timer_thread() が呼ばれる
  2. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  3. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  4. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる

対策はいくつかあり得ると思うのですが、例えば、ubf_select() から rb_thread_wakeup_timer_thread()
を呼ばないようにするとか

Issue #5757 has been updated by Tomoyuki C…

Status changed from Assigned to Closed


Bug #5757: main threadがreadやselectで待っていると、^C でなかなか死なない

Author: Yui NARUSE
Status: Closed
Priority: Normal
Assignee: Tomoyuki C.
Category:
Target version:
ruby -v: -

FreeBSD 9 にて、 ./ruby と起動して ^C を投げてもなかなか死にません。
./miniruby でも -e’$stdin.read’ でも同じです。

仕組みとしては、main thread が read や select で待つ場合、最近は blocking region で
unblock.func に ubf_select を設定するわけですが、この時にシグナルが来ると、

  1. どこかのスレッドの sighandler が呼ばれて、rb_thread_wakeup_timer_thread() が呼ばれる
  2. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  3. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  4. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる

対策はいくつかあり得ると思うのですが、例えば、ubf_select() から rb_thread_wakeup_timer_thread()
を呼ばないようにするとか

Issue #5757 has been updated by Tomoyuki C…

確認ありがとうございます。 r34099 でコミットしました。

Bug #5757: main threadがreadやselectで待っていると、^C でなかなか死なない

Author: Yui NARUSE
Status: Assigned
Priority: Normal
Assignee: Tomoyuki C.
Category:
Target version:
ruby -v: -

FreeBSD 9 にて、 ./ruby と起動して ^C を投げてもなかなか死にません。
./miniruby でも -e’$stdin.read’ でも同じです。

仕組みとしては、main thread が read や select で待つ場合、最近は blocking region で
unblock.func に ubf_select を設定するわけですが、この時にシグナルが来ると、

  1. どこかのスレッドの sighandler が呼ばれて、rb_thread_wakeup_timer_thread() が呼ばれる
  2. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  3. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる
  4. タイマースレッドが起きて、thread_timer() → timer_thread_function() →
    rb_threadptr_check_signal() → rb_threadptr_interrupt() →
    (th->unblock.func)(th->unblock.arg) → ubf_select() →
    rb_thread_wakeup_timer_thread() が呼ばれる

対策はいくつかあり得ると思うのですが、例えば、ubf_select() から rb_thread_wakeup_timer_thread()
を呼ばないようにするとか