Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Child process lingers after keyboard interrupt on Windows

by toughy (Novice)
on Oct 10, 2018 at 23:01 UTC ( #1223837=perlquestion: print w/replies, xml ) Need Help??
toughy has asked for the wisdom of the Perl Monks concerning the following question:

Hello,

I am trying to make a script to automate our project build process and I run into a surprising issue:

With either of open('-|'), IPC::Open3 or IPC::Cmd::run, the child process lingers still alive after a keyboard interrupt (^C) or a timeout alarm from IPC::Cmd. The main script receives the signal and exits as expected, but the build process is still running in the background and visible in Task Manager window.

Contrast this with a call to system() which does The Right Thing: keyboard interrupt will stop the child process, and only after that will the parent process by interrupted.

Can I fix this somehow please ?

I would like to run a couple of my build commands in parallel, and ensure they always terminate before the main script, especially in case of errors, abort, interrupt, other signals like TERM, HUP, QUIT...

Is this possible please ?

Issue not present on Linux or with perl6 Proc class

  • Comment on Child process lingers after keyboard interrupt on Windows

Replies are listed 'Best First'.
Re: Child process lingers after keyboard interrupt on Windows
by BrowserUk (Pope) on Oct 11, 2018 at 01:03 UTC

    The difference is the flags used on the CreateProcess() call underlying the different methods of creating a child process. You cannot change them from your application.

    The simplest way for you to ensure your child processes are cleaned up if your parent is interupted is to install a signal handler for each possible interupt signal and use kill to forward the interrupt before allowing the parent to die.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". The enemy of (IT) success is complexity.
    In the absence of evidence, opinion is indistinguishable from prejudice. Suck that fhit

      I believe

              kill 'INT', $pid
      
      will not send a keyboard interrupt to the other process when on Windows. It should kill the process, but it looks to me that will not work either.

      I tried the bellow code in a cmd console and neither the explicit call to kill(), nor the hardware keyboard ^C press were passed on to the child:

      perl -MIPC::Open2 -E "my($pid, $in); $pid = open2('>&' . fileno(STDOUT), $in, 'cmd', '/C', 'PowerShell', '-Command',  'Sleep', '7;', 'Echo', 'Exiting'); $SIG{'INT'} = sub { kill 'INT', $pid if $pid }; sleep 3; $SIG{'INT'}->(); waitpid $pid, 0"
      
      or just the perl code:
          my($pid, $in);
          $pid = open2('>&' . fileno(STDOUT), $in, 'cmd', '/C', 'PowerShell', '-Command',  'Sleep', '7;', 'Echo', 'Exiting');
          $SIG{'INT'} = sub { kill 'INT', $pid if $pid };
          sleep 3;
          $SIG{'INT'}->();
          waitpid $pid, 0
      

      When kill() is invoked 3 seconds after running the above script, nothing happens. When I press ^C (within 7 sec of starting the command), still nothing.

      How do you forward the interupt?can you show some code?

        The included code:

        $SIG{'INT'} = sub { kill 'INT', $pid if $pid };

        would be the Perl way to forward an interrupt. Except:

        • the INT signal is normally sent to all processes in the group anyway, so you only forward it like this if you know the child went to some other process group already. And then, it probably went away to avoid this kind of interrupt.
        • In a similar way, Windows also sends ^C (Ctrl+C) to all processes attached to that console
        • signals do not really exist on Windows, and there this call to kill directly kills the process, no signal sent whatsoever

        Or at least this is what the documentation says. Because when I try it out, nothing works as it should since I got on Windows...

Re: Child process lingers after keyboard interrupt on Windows
by davido (Cardinal) on Oct 11, 2018 at 06:18 UTC

    Proc::Simple lets you instantiate forks that are managed by objects. You can set kill_on_destroy, causing a signal to be sent to the child when the managing object falls out of scope. It's only about 330 lines of code excluding comments and POD, and deals with tricky timing, zombies, and output redirection. So if you are unable to use external modules (that's another topic), you might just study it to determine what it does that you're not doing.

    It came in handy for me on a recent project where I didn't want to invest a lot of time into reinventing the functionality that is provided on CPAN already.


    Dave

      Thank you for the hint on this module.

      I believe Proc::Simple does not yet work on Windows. With Strawberry perl, installation from CPAN hangs during 'Building and testing Proc-Simple-1.32', and with ActivePerl from ActiveState the .ppm build failed on all Windows versions: http://code.activestate.com/ppm/Proc-Simple/

      I looked at the source on CPAN and what it does is fork() and exec(). I never tried it until now because I found it unintuitive that fork() will work properly on Windows, while IPC::Run will not, but I will try it. I do not like how Proc::Simple will exit(0); after exec() fails, I think that is a silent error and it can be dangerous.

      --
      Thank you,
      Timothy Madden

        So trying to fork() I run into the this issue:

        When the pseudo-child process (really a new thread in the parent, see perl fork() emulation for Windows) reopens STDOUT, the parent STDOUT changes as well. Although perl documentation says the open handles are duplicated on emulated fork(), to prevent this issue.
Re: Child process lingers after keyboard interrupt on Windows
by toughy (Novice) on Nov 01, 2018 at 15:38 UTC

    I found that fork + exec is the way perl spawns a sub-process, including on Windows. With this the keyboard interrupt will be sent to the subprocess as expected:

    my $child = fork(); if ($child) { say 'Waiting'; waitpid $child, 0; say 'Exiting' } else { exec 'cmd', '/C', 'PowerShell', '-Command', 'sleep', '7', '&', 'Echo', 'Done' }
    I suppose I still have to disable $SIG{'INT'} while waiting for the subprocess (that is, before the waitpid call), like system() does, but somehow I still want to receive the interrupt after the child process exits.

      I found that fork + exec is the way perl spawns a sub-process, including on Windows.

      No. Windows has neither fork() nor exec(). Perl emulates both on Windows, fork() using multiple interpreters (see perlfork), and exec() by starting and supervising a process (that on Unix would replace the current process).

      I suppose I still have to disable $SIG{'INT'} while waiting for the subprocess (that is, before the waitpid call), like system() does, but somehow I still want to receive the interrupt after the child process exits.

      Windows has no signals. Perl emulates signals om Windows.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1223837]
Approved by Paladin
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (7)
As of 2018-12-11 17:39 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    How many stories does it take before you've heard them all?







    Results (56 votes). Check out past polls.

    Notices?
    • (Sep 10, 2018 at 22:53 UTC) Welcome new users!