fwashbur has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,

I'm having difficutly porting some CTRL-C acceptance testing from Perl 5.6.1 to Perl 5.8.8. I guess there were issues with:
kill "INT", $pid;
not working on 5.6.1, so we chose to get a Console handle from Win32::Console, and then call:
$console->GenerateCtrlEvent();
after having overriden $SIG{INT} to be ignored in the process that launched the process we want to CTRL-C. This worked great on 5.6.1, but does not work at all on 5.8.8. We never enter the $SIG{INT} handler on 5.8.8 with either the kill function or the GenerateCtrlEvent() call.

The perl process we are trying to signal is launched through on open() call.
open TEST, "perl $d $dir\\ControlC\\ControlC.pl $block $job \$re +slist=$reslist \$timeout=10 $retry |"; # capture stdout from client under test my $output = ''; while (<TEST>) { $output .= $_; if ( /waiting for control c/i ) { $SIG{INT} = 'IGNORE'; #kill "INT", $pid; $console->GenerateCtrlEvent(); Win32::Sleep(100); $SIG{INT} = 'DEFAULT'; } } close TEST;

Is there a reliable way to send a CTRL-C signal to another process using windows XPpro and Perl 5.8.8?

Thanks for your time!

Replies are listed 'Best First'.
Re: Need a reliable way to send SIGINT to a perl process on 5.8.8/windows XPpro
by ikegami (Patriarch) on Mar 09, 2007 at 03:04 UTC

    Need a reliable way to send SIGINT to a perl process on 5.8.8/windows XPpro

    Maybe you're looking at the wrong place. Windows doesn't have signals, so you're dealing with an emulation. If you want reliability, maybe you should use the underlying mechanism (SetConsoleCtrlHandler) directly.

    Better yet, just send the CTRL_C_EVENT to the right process. Perl sets the CREATE_NEW_PROCESS_GROUP flag when spawning an asynchronous child (according to a shallow look at relevant Perl guts), so you should be able to affect only the child you wish to kill without doing anything special in the parent.

    use Win32::Console qw( CTRL_C_EVENT ); my $pid = open(my $fh_from_child, "$cmd |"); my $output = ''; while (<$fh_from_child>) { $output .= $_; if ( /waiting for control c/i ) { Win32::Console->GenerateCtrlEvent(CTRL_C_EVENT, $pid); } } close($fh_from_child);

    By the way, here's a version where you don't have to quote and escape your arguments anymore.

      Yep, I tried specifying CTRL_C_EVENT (which is default) as well as the pid in the GenerateCtrlEvent call and saw no change in behavior.
Re: Need a reliable way to send SIGINT to a perl process on 5.8.8/windows XPpro
by chromatic (Archbishop) on Mar 09, 2007 at 00:14 UTC

    If you can modify the child process, does using Perl::Unsafe::Signals clear up the issue? If so, it's the change in when Perl checks for signals that's tripping you.

      So I see the change in when Perl checks for signals that was made on 5.8.8 when I do CTRL-C manually, but manually it still works.
      I'll have a look at Perl::Unsafe::Signals.
      Thanks
      So I tried setting $ENV{PERL_SIGNALS} = "unsafe"; and saw no change. It doesn't seem to be a timing issue as far as I can tell, it appears that the signal never makes it to the process in the first place.

        I believe you have to set it before you start Perl, but I'm not positive. However, if Perl isn't performing a single long-running operation, but the signal never arrives, this isn't the solution. I don't know anything about signal handling on Windows.

Re: Need a reliable way to send SIGINT to a perl process on 5.8.8/windows XPpro
by BrowserUk (Patriarch) on Mar 09, 2007 at 01:54 UTC

    I'm not surprised that $console->GenerateCtrlEvent(); doesn't affect the child process. Any console handle you have in your parent process is unlikely to have much effect upon your child process. If you were creating the child yourself and setting all the right flags and permissions on the CreateProcess() you might be able to affect the child using a handle in the parent, but not otherwise.

    Try kill 21, $pid;. If that gets through to the child process, I may have an explanation for you.


    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".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      I'm not surprised that $console->GenerateCtrlEvent(); doesn't affect the child process.

      According to the documentation for the underlying function (GenerateConsoleCtrlEvent), the signal is generated in all processes that share the console of the calling process when the process group id parameter is 0 like it is here. (If the parameter is not zero, all the processes in the specified process group are affected.) The child should be affected, meaning its event handler should be called if one was set.

      By the way, this function can be called as a static method: Win32::Console->GenerateCtrlEvent();. It's not really a console method at all.

        The behavior we see on 5.6.1 matches the documentation, but on 5.8.8 it does not appear that the CTRL-C get through to the child process where we have the $SIG{INT} handler defined. I have tried using defaults which works on 5.6.1, and specifying CTRL_C_EVENT along with the pid directly and that doesn't seem to help either.
      Interesting, so using the Break instead, my current acceptance test case seems to interrupt correctly in many cases although I get wild exit codes some times.
      However, trying just a simple test, this still does not work. For instance I have 2 scripts running in parallel, using one to attempt to send the signal to the other:
      WaitForCtrlC.pl:
      use strict; use warnings; $SIG{INT} = sub {print "got ctrl-C in waitforctrlc\n"; exit 1;}; print "\nmy pid is $$\n"; while (1) { print "waiting for ctrl-C\n"; sleep 2 } # should never get here print "made it past ctrl-C\n"; exit 0;

      And then send the signal to the pid with SendCtrlC.pl:
      use strict; use warnings; while (1) { print "Enter pid to signal:"; my $pid = <STDIN>; chomp $pid; print "\nSending signal to $pid:..."; kill 21, $pid; print "\n"; }
      Kudos to you!
      kill 21, $pid;
      works!, I just assigned $SIG{BREAK} to the same handler as $SIG{INT}. I know that manual CTRL-C gets me into the interrupt handler for $SIG{INT}. So now I can just use BREAK to test the handler.
      Thank you!!
Re: Need a reliable way to send SIGINT to a perl process on 5.8.8/windows XPpro
by bart (Canon) on Mar 09, 2007 at 11:38 UTC
    Perhaps you're doing this the wrong way, and you should rather try to kill it with Win32::Process, using KillProcess?
    Win32::Process::KillProcess($pid, $exitcode)
    Terminates any process identified by $pid. $exitcode will be set to the exit code of the process.

    If I read this right, $pid should just be a number.

      We definitely don't want to kill the process, we want to run the interrupt handler first before we kill it. Manually doing CTRL-C works great, now we need to simulate that programmatically to do acceptance testing of our interrupt handler.