ignores SIGPIPE, why ruby

Discussion in 'Ruby' started by Zsban Ambrus, Apr 26, 2004.

  1. Zsban Ambrus

    Zsban Ambrus Guest

    Ruby ignores SIGPIPE. In this mail, I'll argue against the usefulness of
    this decision.

    When you type something like

    ruby -we 'n= 1; puts n+=1 while 1;' | head

    then after the 10th line the puts function raises Errno::EPIPE (cause that's
    what syscalls return when they raise an EPIPE signal) and ruby dies with an
    ugly annoying message like

    -e:1:in `write': Broken pipe (Errno::EPIPE)
    from -e:1:in `puts'
    from -e:1

    I think that ruby should either silently die from the EPIPE signal (or else
    raise an Errno::EPIPE exception that you can catch but if you don't catch it
    ruby should just exit silently just like when you call Kernel.exit())

    I belive that one of the biggest advantage of the UN*X-like systems is the
    well-thought job-control scheme. It works quite automatically: you can
    redirect, suspend, resume, background etc a procaess, and everything works
    fine even if the processes itself do not have any special support for it.
    This is especially true for simple processes that don't do anything special
    with stdin and stdout just use them in cooked mode.

    It would take nothing to implement this behaviour, as the OS kills ruby, so
    ruby has to do nothing. In the above example, you don't need an error
    message, as you asked for "|head", so the os killing the process would be right
    (and the shell not printing a message about what signal killed the process
    on the left side on the pipeline is also right, note that when a process
    dies from a signal, new shells usually report this with a short line).

    What makes the situation even worse is that when I try to change tshi
    behaiviour like this:

    ruby -we 'trap "PIPE" do exit end; n= 1; puts n+=1 while 1;' | head

    the trap function does not do anything, it does not catch the SIGPIPE. The
    trap function should at least raise an error because it does not catch the
    signal. The only way to silence the error message is to wrap the whole
    script in a begin .... rescue Errno::EPIPE; end;

    If ruby would just leave the signal as is, you could probabyl still have the
    old behaiviour by adding a trap for SIGPIPE that raises an Errno::EPIPE
    exception, or just by ignoring the signal and supposing that the syscall
    that raised SIGPIPE will get an EPIPE error anyway thus raise the exception.

    What do you think of this idea?

    ambrus
     
    Zsban Ambrus, Apr 26, 2004
    #1
    1. Advertising

  2. Zsban Ambrus

    Zsban Ambrus Guest

    On Tue, Apr 27, 2004 at 08:48:25AM -0700, Eric Hodel wrote:
    > Zsban Ambrus () wrote:
    >
    >...
    > No, Ruby does not ignore SIGPIPE. It traps it just fine.


    It traps SIGPIE, but unless you define your own signal handler, it does
    nothing with it. See the sigpipe function in signal.c.

    >
    > > When you type something like
    > >
    > > ruby -we 'n= 1; puts n+=1 while 1;' | head
    > >
    > > then after the 10th line the puts function raises Errno::EPIPE (cause that's
    > > what syscalls return when they raise an EPIPE signal) and ruby dies with an
    > > ugly annoying message like
    > >
    > > -e:1:in `write': Broken pipe (Errno::EPIPE)
    > > from -e:1:in `puts'
    > > from -e:1
    > >
    > > I think that ruby should either silently die from the EPIPE signal (or else
    > > raise an Errno::EPIPE exception that you can catch but if you don't catch it
    > > ruby should just exit silently just like when you call Kernel.exit())

    >


    After what you've said I've investigated further what happened.

    > Ruby did not recive a SIGPIPE, puts raised an Errno::EPIPE. See
    > errno(2) vs signal(3).


    Ruby did get a SIGPIPE. I thought it should get one as the os always sends a
    SIGPIPE when any operation (either on a socket or a pipe or a named fifo or
    whatever) fails with EPIPE.

    So it got SIGPIPE, but as I sad the handler function does nothing, so puts
    raises the EPIPE exception it gets from the os.

    Read further why I'm so sure

    >...
    > > What makes the situation even worse is that when I try to change tshi
    > > behaiviour like this:
    > >
    > > ruby -we 'trap "PIPE" do exit end; n= 1; puts n+=1 while 1;' | head
    > >
    > > the trap function does not do anything, it does not catch the SIGPIPE. The
    > > trap function should at least raise an error because it does not catch the
    > > signal.


    This last statement of mine was incorrect. Ruby indeed catches the SIGPIPE,
    but it has "safe signals", so it does not immediately start my trap block,
    but schedules it later. But then puts raises an exception, and as the
    exception is not caught, ruby exits and it does not get around to run the
    trap-block.

    To prove this, look:

    am ~/a> ruby -we 'trap "PIPE" do $stderr.puts "trapped SIGPIPE"; end; x=0;
    begin loop do puts(x+=1); end; rescue Errno::EPIPE; $stderr.puts "rescued
    EPIPE"; end;' | head
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    trapped SIGPIPE
    rescued EPIPE
    am ~/a> ruby -v
    ruby 1.8.1 (2003-12-25) [i686-linux]

    What I think happens here starts the same as I've described above.
    First, ruby gets the SIGPIPE from the os when the head has exited,
    but it cannot execute my block now, so it just puts it aside.
    Then, the write operation returns with EPIPE, so puts raises an Errno::EPIPE
    execption. The begin block captures this exception, but then, before ruby
    would start evaling the commands in the rescue clause, it runs my handler
    for SIGPIPE. The SIGPIPE handler returns, and then the rescue clause runs
    too. Then the program ends normally.

    I still be wrong here, so correct me if I'm wrong.

    >
    > That code never recieves a SIGPIPE signal,

    False, as I've said above
    > it raises an Errno::EPIPE.
    > trap "PIPE" works just fine:
    >
    > $ cat x.rb
    > pid = fork do
    > trap 'PIPE' do puts "caught SIGPIPE"; exit end
    > File.open '/dev/null', 'a' do |fp|
    > loop do fp.puts end
    > end
    > end
    >
    > sleep 1
    > Process.kill 'PIPE', pid
    > $ ruby x.rb
    > caught SIGPIPE
    >

    Thanks for this example, this is what made me think about why trap does not
    seem to work.
    >
    > > The only way to silence the error message is to wrap the whole script
    > > in a begin .... rescue Errno::EPIPE; end;

    >
    > def main
    > # ...
    > rescue Errno::EPIPE
    > exit
    > end
    >
    > main if $0 == __FILE__
    >

    That's almost the same isn't it?

    > > If ruby would just leave the signal as is, you could probabyl still
    > > have the old behaiviour by adding a trap for SIGPIPE that raises an
    > > Errno::EPIPE exception, or just by ignoring the signal and supposing
    > > that the syscall that raised SIGPIPE will get an EPIPE error anyway
    > > thus raise the exception.

    This doesn't hold because of the above.

    I am now not quite about what would be the correct behaviour.
    Maybe ruby should try to check for pending signals before dieing of an
    exception?

    >
    > --
    > Eric Hodel - - http://segment7.net
    > All messages signed with fingerprint:
    > FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04
    >
     
    Zsban Ambrus, Apr 27, 2004
    #2
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Andrey Tarasevich

    IE ignores table cell height propery - why?

    Andrey Tarasevich, Aug 8, 2005, in forum: HTML
    Replies:
    6
    Views:
    30,050
    mbstevens
    Aug 9, 2005
  2. Dan Stromberg

    ignoring SIGPIPE in a python script?

    Dan Stromberg, Jun 1, 2005, in forum: Python
    Replies:
    1
    Views:
    771
    Donn Cave
    Jun 1, 2005
  3. Mr. SweatyFinger

    why why why why why

    Mr. SweatyFinger, Nov 28, 2006, in forum: ASP .Net
    Replies:
    4
    Views:
    906
    Mark Rae
    Dec 21, 2006
  4. Mr. SweatyFinger
    Replies:
    2
    Views:
    1,978
    Smokey Grindel
    Dec 2, 2006
  5. George Glynn

    SIGPIPE delay on open2 exec failure

    George Glynn, Apr 15, 2005, in forum: Perl Misc
    Replies:
    1
    Views:
    191
Loading...

Share This Page