diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | signal.c | 66 | ||||
-rw-r--r-- | test/ruby/test_process.rb | 25 | ||||
-rw-r--r-- | test/ruby/test_signal.rb | 11 | ||||
-rw-r--r-- | thread.c | 9 | ||||
-rw-r--r-- | version.h | 8 |
6 files changed, 106 insertions, 18 deletions
@@ -1,3 +1,8 @@ +Tue Sep 2 02:08:12 2014 Nobuyoshi Nakada <nobu@ruby-lang.org> + + * signal.c (rb_f_kill): directly enqueue an ignored signal to self, + except for SIGSEGV and SIGBUS. [ruby-dev:48203] [Bug #9820] + Sun Aug 31 01:13:21 2014 Koichi Sasada <ko1@atdot.net> * gc.c: change full GC timing to keep lower memory usage. @@ -343,6 +343,9 @@ ruby_default_signal(int sig) raise(sig); } +static int signal_ignored(int sig); +static void signal_enque(int sig); + /* * call-seq: * Process.kill(signal, pid, ...) -> fixnum @@ -429,6 +432,8 @@ rb_f_kill(int argc, VALUE *argv) break; } + if (argc <= 1) return INT2FIX(0); + if (sig < 0) { sig = -sig; for (i=1; i<argc; i++) { @@ -437,8 +442,42 @@ rb_f_kill(int argc, VALUE *argv) } } else { + const rb_pid_t self = (GET_THREAD() == GET_VM()->main_thread) ? getpid() : -1; + int wakeup = 0; + for (i=1; i<argc; i++) { - ruby_kill(NUM2PIDT(argv[i]), sig); + rb_pid_t pid = NUM2PIDT(argv[i]); + + if ((sig != 0) && (self != -1) && (pid == self)) { + /* + * When target pid is self, many caller assume signal will be + * delivered immediately and synchronously. + */ + switch (sig) { + case SIGSEGV: +#ifdef SIGBUS + case SIGBUS: +#endif +#ifdef SIGKILL + case SIGKILL: +#endif +#ifdef SIGSTOP + case SIGSTOP: +#endif + ruby_kill(pid, sig); + break; + default: + if (signal_ignored(sig)) break; + signal_enque(sig); + wakeup = 1; + } + } + else if (kill(pid, sig) < 0) { + rb_sys_fail(0); + } + } + if (wakeup) { + rb_threadptr_check_signal(GET_VM()->main_thread); } } rb_thread_execute_interrupts(rb_thread_current()); @@ -570,11 +609,32 @@ ruby_nativethread_signal(int signum, sighandler_t handler) #endif #endif -static RETSIGTYPE -sighandler(int sig) +static int +signal_ignored(int sig) +{ +#ifdef POSIX_SIGNAL + struct sigaction old; + (void)VALGRIND_MAKE_MEM_DEFINED(&old, sizeof(old)); + if (sigaction(sig, NULL, &old) < 0) return FALSE; + return old.sa_handler == SIG_IGN; +#else + sighandler_t old = signal(sig, SIG_DFL); + signal(sig, old); + return old == SIG_IGN; +#endif +} + +static void +signal_enque(int sig) { ATOMIC_INC(signal_buff.cnt[sig]); ATOMIC_INC(signal_buff.size); +} + +static RETSIGTYPE +sighandler(int sig) +{ + signal_enque(sig); rb_thread_wakeup_timer_thread(); #if !defined(BSD_SIGNAL) && !defined(POSIX_SIGNAL) ruby_signal(sig, sighandler); diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index f6e44fadad..a71a2d6dce 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -1193,6 +1193,23 @@ class TestProcess < Test::Unit::TestCase def test_status_kill return unless Process.respond_to?(:kill) + return unless Signal.list.include?("KILL") + + # assume the system supports signal if SIGQUIT is available + expected = Signal.list.include?("QUIT") ? [false, true, false, nil] : [true, false, false, true] + + with_tmpchdir do + write_file("foo", "Process.kill(:KILL, $$); exit(42)") + system(RUBY, "foo") + s = $? + assert_equal(expected, + [s.exited?, s.signaled?, s.stopped?, s.success?], + "[s.exited?, s.signaled?, s.stopped?, s.success?]") + end + end + + def test_status_quit + return unless Process.respond_to?(:kill) return unless Signal.list.include?("QUIT") with_tmpchdir do @@ -1206,16 +1223,14 @@ class TestProcess < Test::Unit::TestCase end t = Time.now s = $? - assert_equal([false, true, false], - [s.exited?, s.signaled?, s.stopped?], - "[s.exited?, s.signaled?, s.stopped?]") + assert_equal([false, true, false, nil], + [s.exited?, s.signaled?, s.stopped?, s.success?], + "[s.exited?, s.signaled?, s.stopped?, s.success?]") assert_send( [["#<Process::Status: pid #{ s.pid } SIGQUIT (signal #{ s.termsig })>", "#<Process::Status: pid #{ s.pid } SIGQUIT (signal #{ s.termsig }) (core dumped)>"], :include?, s.inspect]) - assert_equal(false, s.exited?) - assert_equal(nil, s.success?) EnvUtil.diagnostic_reports("QUIT", RUBY, pid, t) end end diff --git a/test/ruby/test_signal.rb b/test/ruby/test_signal.rb index 60b886cec9..e329df9a34 100644 --- a/test/ruby/test_signal.rb +++ b/test/ruby/test_signal.rb @@ -268,4 +268,15 @@ EOS } } end if Process.respond_to?(:kill) and Signal.list.key?('HUP') + + def test_ignored_interrupt + bug9820 = '[ruby-dev:48203] [Bug #9820]' + assert_separately(['-', bug9820], <<-'end;') # begin + bug = ARGV.shift + trap(:INT, "IGNORE") + assert_nothing_raised(SignalException, bug) do + Process.kill(:INT, $$) + end + end; + end if Process.respond_to?(:kill) end @@ -5320,13 +5320,12 @@ ruby_kill(rb_pid_t pid, int sig) { int err; rb_thread_t *th = GET_THREAD(); - rb_vm_t *vm = GET_VM(); /* * When target pid is self, many caller assume signal will be * delivered immediately and synchronously. */ - if ((sig != 0) && (th == vm->main_thread) && (pid == getpid())) { + { GVL_UNLOCK_BEGIN(); native_mutex_lock(&th->interrupt_lock); err = kill(pid, sig); @@ -5334,9 +5333,7 @@ ruby_kill(rb_pid_t pid, int sig) native_mutex_unlock(&th->interrupt_lock); GVL_UNLOCK_END(); } - else { - err = kill(pid, sig); - } - if (err < 0) + if (err < 0) { rb_sys_fail(0); + } } @@ -1,10 +1,10 @@ #define RUBY_VERSION "2.1.2" -#define RUBY_RELEASE_DATE "2014-08-31" -#define RUBY_PATCHLEVEL 216 +#define RUBY_RELEASE_DATE "2014-09-02" +#define RUBY_PATCHLEVEL 217 #define RUBY_RELEASE_YEAR 2014 -#define RUBY_RELEASE_MONTH 8 -#define RUBY_RELEASE_DAY 31 +#define RUBY_RELEASE_MONTH 9 +#define RUBY_RELEASE_DAY 2 #include "ruby/version.h" |