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: X-Spam-Status: No, score=-4.1 required=3.0 tests=ALL_TRUSTED,BAYES_00, RP_MATCHES_RCVD shortcircuit=no autolearn=unavailable version=3.3.2 X-Original-To: spew@80x24.org Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 18F991F461 for ; Fri, 17 Jul 2015 17:51:15 +0000 (UTC) From: Eric Wong To: spew@80x24.org Subject: [PATCH] make Process.kill(:STOP, $$) resumable Date: Fri, 17 Jul 2015 17:51:15 +0000 Message-Id: <1437155475-8676-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. Thus, we must rely on sighandler->timer_thread to signal th->interrupt_cond when SIGCONT. * signal.c (Init_signal): install sighandler for SIGCONT * test/ruby/test_process.rb (test_stop_self_resumable): new test --- signal.c | 3 +++ test/ruby/test_process.rb | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/signal.c b/signal.c index 40caaa6..bc3ab4d 100644 --- a/signal.c +++ b/signal.c @@ -1456,6 +1456,9 @@ Init_signal(void) #ifdef SIGUSR2 install_sighandler(SIGUSR2, sighandler); #endif +#ifdef SIGCONT + install_sighandler(SIGCONT, sighandler); +#endif if (!ruby_enable_coredump) { #ifdef SIGBUS diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index 00bbf9e..d42a096 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -1341,6 +1341,43 @@ class TestProcess < Test::Unit::TestCase end end + def test_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) + + (0..1).each do |n| + begin + pid = nil + Timeout.timeout(30) do + pid = fork do + # 2nd time we run this test, try with an explicit SIGCONT handler + case n + when 1 + trap(:CONT) { n = 0 } + when 2 + n = 0 + trap(:CONT, 'IGNORE') + end + + Process.kill(:STOP, $$) + exit(42 + n) + 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 + end + end + def test_wait_without_arg with_tmpchdir do write_file("foo", "sleep 0.1") -- EW