From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS51731 46.36.32.0/19 X-Spam-Status: No, score=-1.5 required=3.0 tests=AWL,BAYES_00, RCVD_IN_DNSWL_BLOCKED,RCVD_IN_XBL,RDNS_NONE shortcircuit=no autolearn=no version=3.3.2 X-Original-To: spew@80x24.org Received: from 80x24.org (unknown [46.36.36.127]) by dcvr.yhbt.net (Postfix) with ESMTP id 21FA21F61E for ; Fri, 17 Jul 2015 09:22:41 +0000 (UTC) From: Eric Wong To: spew@80x24.org Subject: [PATCH] ensure Process.kill(:STOP, $$) is resumable Date: Fri, 17 Jul 2015 09:22:37 +0000 Message-Id: <1437124957-4107-1-git-send-email-e@80x24.org> List-Id: Self-inflicted signals are delivered immediately. This is fine for most signals which are catchable, but SIGSTOP and SIGKILL are special and cannot be caught by a userspace process. SIGKILL is easy, the process will die immediately and we won't care for it. However, SIGSTOP is tricky because we cannot know when it is delivered. Try to improve the situation by allowing the OS to reschedule our thread by calling native_thread_yield(). * thread.c (ruby_kill): do not sleep forever on SIGSTOP --- test/ruby/test_process.rb | 23 +++++++++++++++++++++++ thread.c | 16 +++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index 00bbf9e..fa84a9f 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -1341,6 +1341,29 @@ class TestProcess < Test::Unit::TestCase end end + def test_status_stop_self_resumable + skip "kill not supported" unless Process.respond_to?(:kill) + skip "fork not supported" unless Process.respond_to?(:fork) + skip "SIGSTOP not supported" unless Signal.list.include?("STOP") + skip "WUNTRACED not defined" unless Process.const_defined?(:WUNTRACED) + pid = nil + Timeout.timeout(30) do + pid = fork do + Process.kill(:STOP, $$) + exit(42) + end + _, s = Process.waitpid2(pid, Process::WUNTRACED) + assert_predicate s, :stopped? + Process.kill(:CONT, pid) + _, s = Process.waitpid2(pid) + assert_predicate s, :exited? + assert_equal 42, s.exitstatus + end + rescue Timeout::Error + Process.kill(:KILL, pid) if pid + raise + end + def test_wait_without_arg with_tmpchdir do write_file("foo", "sleep 0.1") diff --git a/thread.c b/thread.c index 1d550ef..f0da75d 100644 --- a/thread.c +++ b/thread.c @@ -5262,6 +5262,11 @@ ruby_kill(rb_pid_t pid, int sig) { int err; rb_thread_t *th = GET_THREAD(); +#ifdef SIGSTOP + int sigstop = sig == SIGSTOP; +#else + int sigstop = 0; +#endif /* * When target pid is self, many caller assume signal will be @@ -5271,7 +5276,16 @@ ruby_kill(rb_pid_t pid, int sig) GVL_UNLOCK_BEGIN(); native_mutex_lock(&th->interrupt_lock); err = kill(pid, sig); - native_cond_wait(&th->interrupt_cond, &th->interrupt_lock); + if (sigstop) { + /* + * best effort to try to receive SIGSTOP ASAP, + * maybe we need to yield several more times. + */ + native_thread_yield(); + } + else { /* sig is SIGKILL, SIGSEGV, or SIGBUS: wait to die */ + native_cond_wait(&th->interrupt_cond, &th->interrupt_lock); + } native_mutex_unlock(&th->interrupt_lock); GVL_UNLOCK_END(); } -- EW