How do I trap Ctrl-C so it doesn't reach spawned threads?

Hi!

I’m having trouble trapping Ctrl-C. I have a CLI that spawns processes I
would like to survive the CLI, but if I press Ctrl-C the spawns dies
along with the CLI. I have experimented with trap(INT), and have boiled
down the problem into a small example program. I run this on Linux.

First, the program:
-------- 8< --------
#!/usr/bin/jruby

require ‘readline’

keep_at_it = true

trap(“INT”) { puts “\nCtrl-C!” ; keep_at_it = false }

while (keep_at_it) do
line = Readline.readline("Enter for new xeyes, anything else to quit:
", true)
if (line.length == 0 && keep_at_it == true)
Thread.new { system(“xeyes”) }
else
keep_at_it = false
end
end
-------- 8< --------

Now, the behaviour:

When using jruby (1.6.0):

  • Quitting leaves spawned processes running
  • Ctrl-C kills all spawned processes, writes “Ctrl-C!” and quits after
    next return. I have used strace to verify that the xeyes actually gets a
    SIGINT, not a SIGHUP.

When using ruby (1.8.7):

  • Quitting leaves spawned processes running
  • First time Ctrl-C is pressed, the first started xeyes dies.
  • Second time Ctrl-C is pressed “Ctrl-C!” is written and the program
    exists after next return, leaving remaining spawned processes alive.

I don’t understand the behaviour. The jruby version feels more
consistent, but is far away from what I need. The ruby version feels
strange, but I could handle my needs by spawning a dummy process if I
didn’t need jmx4r (which as far as I understand requires jruby).

  1. How is trapping of SIGINT supposed to work? How is the passing of
    SIGINT supposed to work?
  2. Why does it differs between ruby and jruby?
  3. Anyone with a suggestion how to spawn processes not affected by a
    SIGINT?

///BR, Jens Carlberg

  1. Anyone with a suggestion how to spawn processes not affected by a
    SIGINT?

Try replacing

Thread.new { system("…") }

with:

fork { Process.setsid; exec("…") }

This also has the advantages of:

  • not using threads
  • the pid of the child is the return value from fork

Regards,

Brian.

On Fri, Apr 8, 2011 at 2:53 AM, Jens Carlberg
[email protected] wrote:

Hi!

I’m having trouble trapping Ctrl-C. I have a CLI that spawns processes I would
like to survive the CLI, but if I press Ctrl-C the spawns dies along with the CLI.
I have experimented with trap(INT), and have boiled down the problem into a small
example program. I run this on Linux.

I don’t understand the behaviour. The jruby version feels more consistent, but
is far away from what I need. The ruby version feels strange, but I could handle
my needs by spawning a dummy process if I didn’t need jmx4r (which as far as I
understand requires jruby).

  1. How is trapping of SIGINT supposed to work? How is the passing of SIGINT
    supposed to work?
  2. Why does it differs between ruby and jruby?
  3. Anyone with a suggestion how to spawn processes not affected by a SIGINT?

In MRI, I believe SIGINT is sent to the process that currently
controls the TTY, so in the case of system() it’s sent to your program
that’s running.

In JRuby, child processes don’t usually share the TTY streams (JVM
isolates child process streams from the parent using pipes), so Ctrl-C
is sent to JRuby (the JVM itself, really, since we don’t trap INT in
JRuby and allow JVM to handle it) and it shuts down taking the active
child process with it.

Perhaps you can try to use posix_spawn or 1.9’s “spawn”?

  • Charlie

At this point you might want to take this to a JRuby bug
(http://bugs.jruby.org) and we can sort out what you need and what
we’re not doing right. I will note that we’ve been making various
fixes for process management recently, so this is very timely.

  • Charlie

On Thu, Apr 14, 2011 at 3:27 AM, Jens Carlberg

I have now tried both fork and spawn, and my confusion have reached new
levels. :slight_smile:

Fork and ruby

Worked as a charm. Too bad I need to use jruby due to JMX dependencies.

Fork and jruby

Fork seems to be considered dangerous when used in jruby. I have found
info
about enabling it, but are having very limited success in doing that.

I have started jirb two ways:

  1. jirb -J-Djruby.fork.enabled=true

Results in “IRB::UnrecognizedSwitch: Unrecognized switch:
-J-Djruby.fork.enabled=true”

  1. jruby -J-Djruby.fork.enabled=true -S jirb

Results in “NotImplementedError: fork is not available on this
platform”.

The jruby.fork.enabled property isn’t listed when I do jruby
–properties
either. Has it been removed?

Spawn and jruby

  1. Process.spawn(“xeyes”)
    Do not protect the spawned process from Ctrl-C.

  2. Process.spawn(“xeyes”, :pgroup=>true)
    Gives the xeyes usage message; the option is used as a part of the
    command.
    How do jruby separates options for the command and options for
    Process.spawn?

Just to test that I read the Rdoc page for Process right:

  1. spawn({“FOO”=>“BAR”, “BAZ”=>nil}, “xeyes”)
    Errno::ENOENT: No such file or directory - {“FOO”=>“BAR”, “BAZ”=>nil}

///Best regards, Jens Carlberg