Bug #4950

ほぼ同時に異なる種類のシグナルを受信すると片方のシグナルハンドラが実行されない

Added by Tomoyuki Chikanaga almost 3 years ago. Updated almost 3 years ago.

[ruby-dev:44005]
Status:Closed
Priority:Normal
Assignee:-
Category:core
Target version:1.9.3
ruby -v:- Backport:

Description

以下のようなスクリプトを実行すると [1,2] か [2,1] が表示されることが期待されますが、
実際には [1] が表示されます(稀に [2] になることもあります)。

a = []
trap(:INT) { a.push(1) }
trap(:TERM) { a.push(2) }

pid = $$
fork do
sleep 0.5
puts "send start"
Process.kill(:INT, pid)
Process.kill(:TERM, pid)
puts "send end"
end

puts "sleep start"
sleep 3
puts "sleep end"
p a

Signal.trap のブロック内で puts してみると、一方のシグナルハンドラが終了時に遅れて実行されているようです。

Associated revisions

Revision 32345
Added by Koichi Sasada almost 3 years ago

  • thread.c (rbthreadptrcheck_signal): only wake up main thread.
  • thread.c (rbthreadptrexecuteinterruptscommon): check signal deliverly if it is main thread. fixes [Ruby 1.9 - Bug #4950]
  • bootstraptest/test_fork.rb: add a test for above.
  • signal.c (rbgetnextsignal): skip if signalbuff is empty. (check signal_buff.size first)
  • vmcore.h: remove unused variable rbthreadt::execsignal.
  • thread.c (rbthreadchecktrappending): check rbsignalbuffsize() because rbthreadt::execsignal is no longer available.

History

#1 Updated by Tomoyuki Chikanaga almost 3 years ago

シグナルがメインスレッドで処理されるまで配送を遅延しているので、タイマースレッドが polling をやめてしまうと配送が滞留するという問題みたいでした。
以下のように polling する条件に追加すると期待したような動作になりました。どうでしょうか。

diff --git a/threadpthread.c b/threadpthread.c
index 4f66c4e..016c3e9 100644
--- a/threadpthread.c
+++ b/thread
pthread.c
@@ -1085,7 +1085,7 @@ threadtimer(void *p)
FD
ZERO(&rfds);
FDSET(timerthread_pipe[0], &rfds);

  • if (gvl->waiting > 0 || need_polling) {
  • if (gvl->waiting > 0 || needpolling || rbsignalbuffsize() > 0) { timeout.tvsec = 0; timeout.tvusec = TIMEQUANTUMUSEC;

#2 Updated by Koichi Sasada almost 3 years ago

  • ruby -v changed from ruby 1.9.3dev (2011-06-30 trunk 32335) [x86_64-darwin10.8.0] to -

 ささだです.

 ちょうど小崎さんに同じ問題を指摘されて,しかも解決策も提示して頂いたの
で,パッチを作っていました.これで動きます(デバッグプリント込み).

http://www.atdot.net/sp/view/w8gmnl/readonly

ただ,make test で,copy_stream のテストが刺さる,という問題が解決できず
にいます.

    at_exit { p :foo }

    megacontent = "abc" * 12345678
    #File.open("megasrc", "w") {|f| f << megacontent }

    Thread.new { sleep p(rand*0.2); Process.kill(:INT, $$) }

    r1, w1 = IO.pipe
    r2, w2 = IO.pipe
    t1 = Thread.new { w1 << megacontent; w1.close }
    t2 = Thread.new { r2.read; r2.close }
    IO.copy_stream(r1, w2) rescue nil
    w2.close
    r1.close
    t1.join
    t2.join

うーん,なぜだろう.rb_interrupt() までは呼ばれているようなのですが.

(2011/07/01 0:18), Tomoyuki Chikanaga wrote:

Issue #4950 has been updated by Tomoyuki Chikanaga.

シグナルがメインスレッドで処理されるまで配送を遅延しているので、タイマースレッドが polling をやめてしまうと配送が滞留するという問題みたいでした。
以下のように polling する条件に追加すると期待したような動作になりました。どうでしょうか。

diff --git a/threadpthread.c b/threadpthread.c
index 4f66c4e..016c3e9 100644
--- a/threadpthread.c
+++ b/thread
pthread.c
@@ -1085,7 +1085,7 @@ threadtimer(void *p)
FD
ZERO(&rfds);
FDSET(timerthread_pipe[0], &rfds);

  • if (gvl->waiting > 0 || need_polling) {
  • if (gvl->waiting > 0 || needpolling || rbsignalbuffsize() > 0) { timeout.tvsec = 0; timeout.tvusec = TIMEQUANTUMUSEC;

Bug #4950: ほぼ同時に異なる種類のシグナルを受信すると片方のシグナルハンドラが実行されない
http://redmine.ruby-lang.org/issues/4950

Author: Tomoyuki Chikanaga
Status: Open
Priority: Normal
Assignee:
Category: core
Target version: 1.9.3
ruby -v: ruby 1.9.3dev (2011-06-30 trunk 32335) [x86_64-darwin10.8.0]

以下のようなスクリプトを実行すると [1,2] か [2,1] が表示されることが期待されますが、
実際には [1] が表示されます(稀に [2] になることもあります)。

a = []
trap(:INT) { a.push(1) }
trap(:TERM) { a.push(2) }

pid = $$
fork do
sleep 0.5
puts "send start"
Process.kill(:INT, pid)
Process.kill(:TERM, pid)
puts "send end"
end

puts "sleep start"
sleep 3
puts "sleep end"
p a

Signal.trap のブロック内で puts してみると、一方のシグナルハンドラが終了時に遅れて実行されているようです。

--
// SASADA Koichi at atdot dot net

#3 Updated by Koichi Sasada almost 3 years ago

 ささだです.

 ちょうど小崎さんに同じ問題を指摘されて,しかも解決策も提示して頂いたの
で,パッチを作っていました.これで動きます(デバッグプリント込み).

http://www.atdot.net/sp/view/w8gmnl/readonly

ただ,make test で,copy_stream のテストが刺さる,という問題が解決できず
にいます.

    at_exit { p :foo }

    megacontent = "abc" * 12345678
    #File.open("megasrc", "w") {|f| f << megacontent }

    Thread.new { sleep p(rand*0.2); Process.kill(:INT, $$) }

    r1, w1 = IO.pipe
    r2, w2 = IO.pipe
    t1 = Thread.new { w1 << megacontent; w1.close }
    t2 = Thread.new { r2.read; r2.close }
    IO.copy_stream(r1, w2) rescue nil
    w2.close
    r1.close
    t1.join
    t2.join

うーん,なぜだろう.rb_interrupt() までは呼ばれているようなのですが.

(2011/07/01 0:18), Tomoyuki Chikanaga wrote:

Issue #4950 has been updated by Tomoyuki Chikanaga.

シグナルがメインスレッドで処理されるまで配送を遅延しているので、タイマースレッドが polling をやめてしまうと配送が滞留するという問題みたいでした。
以下のように polling する条件に追加すると期待したような動作になりました。どうでしょうか。

diff --git a/threadpthread.c b/threadpthread.c
index 4f66c4e..016c3e9 100644
--- a/threadpthread.c
+++ b/thread
pthread.c
@@ -1085,7 +1085,7 @@ threadtimer(void *p)
FD
ZERO(&rfds);
FDSET(timerthread_pipe[0], &rfds);

  • if (gvl->waiting > 0 || need_polling) {
  • if (gvl->waiting > 0 || needpolling || rbsignalbuffsize() > 0) { timeout.tvsec = 0; timeout.tvusec = TIMEQUANTUMUSEC;

Bug #4950: ほぼ同時に異なる種類のシグナルを受信すると片方のシグナルハンドラが実行されない
http://redmine.ruby-lang.org/issues/4950

Author: Tomoyuki Chikanaga
Status: Open
Priority: Normal
Assignee:
Category: core
Target version: 1.9.3
ruby -v: ruby 1.9.3dev (2011-06-30 trunk 32335) [x86_64-darwin10.8.0]

以下のようなスクリプトを実行すると [1,2] か [2,1] が表示されることが期待されますが、
実際には [1] が表示されます(稀に [2] になることもあります)。

a = []
trap(:INT) { a.push(1) }
trap(:TERM) { a.push(2) }

pid = $$
fork do
sleep 0.5
puts "send start"
Process.kill(:INT, pid)
Process.kill(:TERM, pid)
puts "send end"
end

puts "sleep start"
sleep 3
puts "sleep end"
p a

Signal.trap のブロック内で puts してみると、一方のシグナルハンドラが終了時に遅れて実行されているようです。

--
// SASADA Koichi at atdot dot net

#4 Updated by Koichi Sasada almost 3 years ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

This issue was solved with changeset r32345.
Tomoyuki, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


  • thread.c (rbthreadptrcheck_signal): only wake up main thread.
  • thread.c (rbthreadptrexecuteinterruptscommon): check signal deliverly if it is main thread. fixes [Ruby 1.9 - Bug #4950]
  • bootstraptest/test_fork.rb: add a test for above.
  • signal.c (rbgetnextsignal): skip if signalbuff is empty. (check signal_buff.size first)
  • vmcore.h: remove unused variable rbthreadt::execsignal.
  • thread.c (rbthreadchecktrappending): check rbsignalbuffsize() because rbthreadt::execsignal is no longer available.

Also available in: Atom PDF