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

3. 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.

···

--
Posted via http://www.ruby-forum.com/\.

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

···

On Fri, Apr 8, 2011 at 2:53 AM, Jens Carlberg <jens.carlberg@ericsson.com> wrote:

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"

2. 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:

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

///Best regards, Jens Carlberg

-----Original Message-----
From: Charles Oliver Nutter [mailto:headius@headius.com]
Sent: den 11 april 2011 01:32
To: ruby-talk@ruby-lang.org
Cc: Jens Carlberg
Subject: Re: How do I trap Ctrl-C so it doesn't reach spawned threads?

On Fri, Apr 8, 2011 at 2:53 AM, Jens Carlberg <jens.carlberg@ericsson.com> 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 <jens.carlberg@ericsson.com> wrote:

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"

2. 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:

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

///Best regards, Jens Carlberg

-----Original Message-----
From: Charles Oliver Nutter [mailto:headius@headius.com]
Sent: den 11 april 2011 01:32
To: ruby-talk@ruby-lang.org
Cc: Jens Carlberg
Subject: Re: How do I trap Ctrl-C so it doesn't reach spawned threads?

On Fri, Apr 8, 2011 at 2:53 AM, Jens Carlberg <jens.carlberg@ericsson.com> > 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