SIGINT not blocked by system()?

Discussion in 'Perl Misc' started by Tom, Apr 27, 2006.

  1. Tom

    Tom Guest

    I've got a multithreaded script in which I'd like to have my "worker"
    threads ignore SIGINT (i.e. the main thread traps it and calls a sub to
    cleanup nicely after all workers are done). The workers do their thing
    via system() calls, and my experience (using 5.8.8 at least) indicates
    that SIGINT is being propogated to the worker threads' system() call
    (even though it's locally ignored).

    According to perlfaq8, system() is shielded from SIGINT:

    -----------------------------------------------------------------------------
    How do I make a system() exit on control-C?

    You can't. You need to imitate the system() call (see perlipc for
    sample code) and then have a signal handler for the INT signal that
    passes the signal on to the subprocess.
    -----------------------------------------------------------------------------

    When CTRL-C is hit on the console, the following code will print
    "Cleaning up..." and exits promptly. I would have expected it to exit
    after all threads have returned.


    #!/usr/bin/perl -w
    use strict;

    use threads;

    $SIG{INT} = \&cleanup;

    foreach my $id ( 1..10 ) {
    threads->new( \&sleeper, $id );
    }

    sleep 60;

    sub cleanup {
    print "Cleaning up\n";
    foreach my $thread ( threads->list() ) {
    $thread->join();
    }
    exit();
    }

    sub sleeper {
    my $id = shift;
    my $time = int( rand(30) );
    local $SIG{INT} = 'IGNORE';
    print "Thread $id sleeping for $time...\n";
    system( "sleep $time" );

    return;
    }
    Tom, Apr 27, 2006
    #1
    1. Advertising

  2. Tom

    Ben Morrow Guest

    Quoth "Tom" <>:
    > I've got a multithreaded script in which I'd like to have my "worker"
    > threads ignore SIGINT (i.e. the main thread traps it and calls a sub to
    > cleanup nicely after all workers are done).


    In general which thread receives a signal is not well defined; and
    Perl's signal handling can be a little... funny. You may have luck with
    any or all of these:

    1. Catch the signal in the worker threads as well, and have them signal
    a condition variable (see threads::shared). The main thread should be
    sitting in a cond_wait on that variable, and if it gets signalled it
    does the cleanup and exits. You may need some playing around with
    mutexes and/or sigprocmask to avoid a race between the child starting
    and the parent going into the wait.

    2. Mask the signal in the child threads with POSIX::sigprocmask. This
    may well mask it in the parent as well, though... :(

    3. Set the environment variable PERL_SIGNALS to 'unsafe'. This will go
    back to the old immediate signal handling, which is less safe (there is
    some chance of memory corruption). You will want to heed the old advice
    about doing nothing in the signal handler beyond setting a flag you can
    check in the main program.

    4. Fake up a system() yourself, as the faq suggests, and ignore the
    signal in the (forked) child before you exec.

    I don't actually know how well Perl handles the interaction of fork and
    threads: it's not something I would try, myself. If you really do just
    need your children to run system() and return, you'd be much better off
    just forking and waiting.

    Ben

    --
    We do not stop playing because we grow old;
    we grow old because we stop playing.
    [George Bernard Shaw]
    Ben Morrow, Apr 27, 2006
    #2
    1. Advertising

  3. Tom

    Guest

    "Tom" <> wrote:
    > I've got a multithreaded script in which I'd like to have my "worker"
    > threads ignore SIGINT (i.e. the main thread traps it and calls a sub to
    > cleanup nicely after all workers are done). The workers do their thing
    > via system() calls, and my experience (using 5.8.8 at least) indicates
    > that SIGINT is being propogated to the worker threads' system() call
    > (even though it's locally ignored).


    In your shell, when you hit ^C, the shell sends an interupt signal to
    all the processes in its "process group" (or whatever they call it),
    which includes the processes spawned by the system function.

    If, instead of hitting ^C, you instead determined the script's pid and
    use linux's "kill -2 <pid>", you will probabl see the behaviour you want.

    I can also get the the behaviour you want on my system by using
    POSIX::setsid.


    >
    > According to perlfaq8, system() is shielded from SIGINT:
    >
    > -------------------------------------------------------------------------
    > ---- How do I make a system() exit on control-C?
    >
    > You can't. You need to imitate the system() call (see perlipc for
    > sample code) and then have a signal handler for the INT signal that
    > passes the signal on to the subprocess.
    > -------------------------------------------------------------------------
    > ----


    Perl doesn't pass the INT into the system. But if your shell decided to
    signal the spawned process on its own, Perl doesn't stop it from doing so.

    >
    > sub sleeper {

    ##prevent shell from clobbering system
    POSIX::setsid();
    > my $id = shift;
    > my $time = int( rand(30) );
    > local $SIG{INT} = 'IGNORE';
    > print "Thread $id sleeping for $time...\n";
    > system( "sleep $time" );
    >
    > return;
    > }


    Xho

    --
    -------------------- http://NewsReader.Com/ --------------------
    Usenet Newsgroup Service $9.95/Month 30GB
    , Apr 28, 2006
    #3
  4. Tom

    Ben Morrow Guest

    Quoth :
    > Perl doesn't pass the INT into the system. But if your shell decided to
    > signal the spawned process on its own, Perl doesn't stop it from doing so.


    My system(3) says

    | During execution of the command, SIGCHLD will be blocked, and SIGINT and
    | SIGQUIT will be ignored.

    Does Perl's system() not do this?

    Ben

    --
    All persons, living or dead, are entirely coincidental.
    Kurt Vonnegut
    Ben Morrow, Apr 28, 2006
    #4
  5. Tom

    Guest

    Ben Morrow <> wrote:
    > Quoth :
    > > Perl doesn't pass the INT into the system. But if your shell decided
    > > to signal the spawned process on its own, Perl doesn't stop it from
    > > doing so.

    >
    > My system(3) says
    >
    > | During execution of the command, SIGCHLD will be blocked, and SIGINT
    > | and SIGQUIT will be ignored.


    That seems to mean that the parent process itself process will ignore
    SIGINT while the child is running, not that the child will be arranged to
    ignore SIGINT. I'm not sure if that is how you were interpreting it or not.

    >
    > Does Perl's system() not do this?


    It seems to depend on the threads used.

    $ perl -wle 'use strict; use threads; $SIG{INT}=sub {die }; \
    print system "sleep 6"; print "Done"'
    2
    Done

    (Exits immediately upon ^c, but not because the parent itself got the
    signal, rather because the child the parent was waiting on got the signal
    and exited early.)

    $ perl -wle 'use strict; use threads; $SIG{INT}=sub {die }; \
    threads->create(sub{print system "sleep 6"}); \
    threads->list->join(); print "Done"'
    2
    Died at -e line 1.

    Here, the parent itself does apparently get the signal.

    This is perl, v5.8.3 built for x86_64-linux-thread-multi

    Xho

    --
    -------------------- http://NewsReader.Com/ --------------------
    Usenet Newsgroup Service $9.95/Month 30GB
    , Apr 28, 2006
    #5
  6. Tom

    Ben Morrow Guest

    Quoth :
    > Ben Morrow <> wrote:
    > > Quoth :
    > > > Perl doesn't pass the INT into the system. But if your shell decided
    > > > to signal the spawned process on its own, Perl doesn't stop it from
    > > > doing so.

    > >
    > > My system(3) says
    > >
    > > | During execution of the command, SIGCHLD will be blocked, and SIGINT
    > > | and SIGQUIT will be ignored.

    >
    > That seems to mean that the parent process itself process will ignore
    > SIGINT while the child is running, not that the child will be arranged to
    > ignore SIGINT. I'm not sure if that is how you were interpreting it or not.


    Ah, yes, I missed that ambiguity. I was reading it as

    fork
    if (parent) {
    waitpid
    }
    else {
    ignore SIGINT
    exec
    }

    i.e. the parent sets up the child to ignore the signals.

    So, the answer is, if you care do it all yourself and handle the signals
    as you need :).

    --
    Heracles: Vulture! Here's a titbit for you / A few dried molecules of the gall
    From the liver of a friend of yours. / Excuse the arrow but I have no spoon.
    (Ted Hughes, [ Heracles shoots Vulture with arrow. Vulture bursts into ]
    'Alcestis') [ flame, and falls out of sight. ]
    Ben Morrow, Apr 28, 2006
    #6
    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. Keith
    Replies:
    0
    Views:
    385
    Keith
    Apr 13, 2005
  2. Bram Stolk
    Replies:
    2
    Views:
    382
    Bram Stolk
    Sep 13, 2005
  3. jrpfinch
    Replies:
    2
    Views:
    512
    Fredrik Lundh
    Mar 23, 2007
  4. akineko

    multiprocessing and SIGINT

    akineko, Jan 7, 2009, in forum: Python
    Replies:
    2
    Views:
    2,200
    Gabriel Genellina
    Jan 10, 2009
  5. Christoph Scheingraber

    connect SIGINT to custom interrupt handler

    Christoph Scheingraber, May 15, 2011, in forum: Python
    Replies:
    11
    Views:
    759
    Thomas 'PointedEars' Lahn
    May 20, 2011
Loading...

Share This Page