Search code examples
linuxperlpipesignalsalarm

Perl: closing subprocess pipe in signal handler hangs?


I need to timeout on a script that's doing blocking io.
Surprisingly it turns out exit hangs if there is an open pipe to a subprocess:

#!/usr/bin/perl                                                                                             
(-f "foo") || die "file foo doesn't exist";
open(IN, "tail -f foo |");

$SIG{ALRM} = sub
{
    print "trying to exit...\n";
    exit 0;     # Hangs with above open() call
};
alarm 1;

while (1)
{   
    sleep 5;   # Do stuff ...
}

With no open call it works, unfortunately removing it is not an option in this case the script needs it.

Looks like exit is trying to close the filehandle and this is what is hanging:

$SIG{ALRM} = sub
{
    print "trying to close...\n";
    close(IN);            # Hangs ...
    print "ok\n";
    exit 0;
};

I guess it's not too happy about reaping the child from inside a signal handler...

Does anyone know a good way around this ?


Solution

  • The signal handler is a red herring, close will block regardless:

    open my $foo, "tail -f foo |" or die "Can't open process: $!";
    
    close $foo;     # <--- will block
    

    One way to fix this is to capture the sub process id via open and then kill that child:

    my $subprocess = open my $foo, "tail -f foo |" or die "Can't open process: $!";
    
    say "subprocess=$subprocess";
    
    kill 'KILL', $subprocess;
    
    close $foo;     # <--- happy now