Project

General

Profile

Bug #727

Signal(CLD) seems not to work on OS X

Added by pragdave (Dave Thomas) almost 11 years ago. Updated over 8 years ago.

Status:
Closed
Priority:
Normal
Target version:
ruby -v:
Backport:
[ruby-core:19744]

Description

=begin
I've noticed differences in the handling of SIGCLD in 1.9. For example, the following code:

     Signal.trap("CLD")  { puts "Child died" }
     fork && Process.wait

now reports

  t.rb:2:in `wait': Interrupted system call (Errno::EINTR)
           from t.rb:2:in `<main>'

With 1.8, it just says "Child died" (which is what I'd expect)
=end


Related issues

Related to Ruby master - Bug #947: Signal handler block is never called when the program is too shortClosed12/29/200801/20/2009Actions
Related to Ruby master - Bug #4173: TestProcess#test_wait_and_sigchild が、たまに失敗するOpenActions

History

#1

Updated by ko1 (Koichi Sasada) almost 11 years ago

  • Assignee set to nobu (Nobuyoshi Nakada)

=begin

=end

#2

Updated by pragdave (Dave Thomas) almost 11 years ago

  • Priority changed from Normal to 5

=begin
ruby 1.9.1 (2008-12-10 revision 20602) [i386-darwin9.5.0] now says nothing—the code simply fails silently.

I've raised this one to 'high', because it will cause a lot of code that monitors background processes to fail silently, which seems dangerous.

Dave
=end

#3

Updated by yugui (Yuki Sonoda) almost 11 years ago

  • Status changed from Open to Assigned
  • Assignee changed from nobu (Nobuyoshi Nakada) to yugui (Yuki Sonoda)
  • Priority changed from 5 to 6
  • Target version set to 1.9.1 Release Candidate

=begin

=end

#4

Updated by yugui (Yuki Sonoda) almost 11 years ago

=begin
Yuguiです。

[ruby-core:20448]について、いくつかのことが起こっているように思います。
まず、問題のコードを引用します。

Signal.trap(:CHLD) { puts "Child died" }
fork && Process.wait

ところで、私の環境では例外は発生しません。ただし、Trapに渡したブロックも
実行されません。
% uname -a
Darwin yugui-macbook.local 9.5.0 Darwin
Kernel Version 9.5.0: Wed Sep 3 11:29:43 PDT 2008;
root:xnu-1228.7.58~1/RELEASE_I386 i386
% ./miniruby test.rb
% ./miniruby test.rb

見てみると、timer_thread_functionに制御が渡ってきている気配がありませ
ん。そこで、

diff --git a/thread_pthread.c b/thread_pthread.c
index a23e772..b8f3d4e 100644
--- a/thread_pthread.c
+++ b/thread_pthread.c
@@ -768,6 +768,7 @@ thread_timer(void *dummy)
#endif
timer_thread_function(dummy);
}

  • timer_thread_function(dummy); native_mutex_unlock(&timer_thread_lock); return NULL; }

このようにして強制的に制御を渡してやると高い確率でChild diedが出力されま
す。しかし、ここで高い確率でErrno::EINTRも発生するようになります。

Errno::EINTRはProcess.waitが発生させています。これは結局waitpid(2)がエ
ラーを返すためのようです。そこで、添付のようなコードで実験してみると同じ
ようにwaitpidがEINTRで失敗します。これはsigaction(2)のSA_RESTARTの項に書
かれている挙動です。よって、次のようにして解決できます。

diff --git a/signal.c b/signal.c
index 91aa7c4..b393280 100644
--- a/signal.c
+++ b/signal.c
@@ -475,6 +475,9 @@ ruby_signal(int signum, sighandler_t handler)
if (signum == SIGSEGV)
sigact.sa_flags |= SA_ONSTACK;
#endif
+#ifdef SA_RESTART

  • sigact.sa_flags |= SA_RESTART; +#endif if (sigaction(signum, &sigact, &old) < 0) { if (errno != 0 && errno != EINVAL) { rb_bug("sigaction error.\n");

問題は2つに分かれるように思います。
(1) OSのタイミング的にはRubyプロセスに届いたシグナルがSignal.trapで処理
されないことは許されるであろうか? 許されないとすれば、スレッドの同期をど
うとる?
(VMの設計)

(2) 上の修正ではシグナルの種類によらずシステムコールを再開しているが、こ
れは望ましいか。SIGCHLDに限定した方がよい?
sleepなどはEINTRを無視して再開するように実装されているが、Rubyの
Process.waitにおいて、シグナル受信で目覚める(そしてErrno::EINTR)ことは期
待された挙動であろうか。
(言語の設計と各プラットフォームの振る舞い)

--
Yugui yugui@yugui.jp
http://yugui.jp
私は私をDumpする

=end

#5

Updated by yugui (Yuki Sonoda) almost 11 years ago

=begin

Errno::EINTRはProcess.waitが発生させています。これは結局waitpid(2)がエ
ラーを返すためのようです。そこで、添付のようなコードで実験してみると同じ
ようにwaitpidがEINTRで失敗します。これはsigaction(2)のSA_RESTARTの項に書
かれている挙動です。よって、次のようにして解決できます。

添付し忘れました。

--
Yugui yugui@yugui.jp
http://yugui.jp
私は私をDumpする

#include
#include
#include
#include

void sighandler(int signum) {}
int main(void)
{
int status;
pid_t pid;
struct sigaction sigact, old;

 sigact.sa_sigaction = sighandler;
 sigact.sa_flags = SA_SIGINFO;
 if (sigaction(SIGCHLD, &sigact, &old)) {
     fprintf(stderr, "sigaction failed: %s\n", strerror(errno));
 }

 if (pid = fork()) {
     if (waitpid(pid, &status, 0) < 0) {
         fprintf(stderr, "waitpid failed: %s\n", strerror(errno));
     }
 }
 else {
 }
 return 0;

}
=end

#6

Updated by yugui (Yuki Sonoda) almost 11 years ago

=begin
Yuguiです。

Tanaka Akira さんは書きました:

SA_RESTART でシステムコールを再開した場合、システムコールで
ブロックしているスレッドを殺すのはどうやって実現するんですか?

確かにそうですね。これは、SA_RESTARTをSIGCHLDに限定することで解決する問
題でしょうか? それとも、他の手段(たとえば直近のシグナルの種類を見てシス
テムコールを再呼び出し?)で適切なものがあるでしょうか。
適当な解決策が分からないのが[ruby-dev:20448]を投稿した理由です。

--
Yugui yugui@yugui.jp
http://yugui.jp
私は私をDumpする

=end

#7

Updated by yugui (Yuki Sonoda) almost 11 years ago

  • Assignee deleted (yugui (Yuki Sonoda))

=begin

=end

#8

Updated by yugui (Yuki Sonoda) over 10 years ago

  • Due date set to 12/24/2008

=begin

=end

#9

Updated by ko1 (Koichi Sasada) over 10 years ago

  • Assignee set to ko1 (Koichi Sasada)

=begin

=end

#10

Updated by yugui (Yuki Sonoda) over 10 years ago

  • Due date changed from 12/24/2008 to 01/20/2009
  • Priority changed from 6 to Normal
  • Target version changed from 1.9.1 Release Candidate to 1.9.1 RC2

=begin
The reported bug consists of two parts.

The first one is that SIGCHLD causes Errno::EINTR. This problem was fixed by r21181.

The latter one is that the block passed to Signal.trap is never called. This is because both parent and child process are too short and the main thread of the parent process is terminated before it receives SIGCHLD. This is a bug of YARV and it is difficult to fix by 1.9.1 RC.

I think the block is called correctly when the program is enouogh complex as:
Signal.trap(:CHLD) { puts "Child died" }
fork {
sleep 10
exit
}
Thread.start {
raise
}
Process.wait
sleep 3

=end

#11

Updated by yugui (Yuki Sonoda) over 10 years ago

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

=begin
Applied in changeset r21181.
=end

Also available in: Atom PDF