pjf / ipc-system-simple

Perl module to make running system commands and capturing errors as simple as possible.
http://search.cpan.org/perldoc?IPC::System::Simple
Other
19 stars 20 forks source link

Avoid calling END{} blocks too many times #16

Open duelafn opened 9 years ago

duelafn commented 9 years ago

capturex will, if execution fails, call END{} blocks one too many times. Suggest adding this fix from http://stackoverflow.com/questions/4307482/how-do-i-disable-end-blocks-in-child-processes

jkeenan commented 4 years ago

capturex will, if execution fails, call END{} blocks one too many times. Suggest adding this fix from http://stackoverflow.com/questions/4307482/how-do-i-disable-end-blocks-in-child-processes

Can you provide a code example that demonstrates the problem which your p.r. is intended to address?

Thank you very much. Jim Keenan (co-maintainer)

duelafn commented 4 years ago

I think that my original problem was an END{} block used by a GUI library being called prematurely. However, this example shows what can happen:

use strict;
use warnings;
use IPC::System::Simple qw/ capturex /;
use File::Path qw/ remove_tree /;

my $dir = "/tmp/t6LupMzfsF";
mkdir $dir;
END {
    print STDERR "FINISHED!\n";
    remove_tree $dir;
    # Log out of a website (invalidate LWP cookie)
    # Delete temporary directory
    # Use a GUI module that does cleanup in an END block
}

open my $F1, ">", "$dir/test.1" or die "Error writing $dir/test.1: $!";
print $F1 "Hello\n";
close $F1;

eval {
    capturex "/usr/bin/NO-SUCH-PROGRAM";
};

open my $F2, ">", "$dir/test.2" or die "Error writing $dir/test.2: $!";
print $F2 "Hello\n";
close $F2;

The END block is called in the forked child of capturex when in an eval. In this case, that child END block removes a temporary directory that the original process is still using. The result is that the open $F2 call fails since the directory is now gone.

Script output:

FINISHED!
Error writing /tmp/t6LupMzfsF/test.2: No such file or directory at /tmp/test.pl line 25.
FINISHED!
duelafn commented 4 years ago

Also, eval { capturex } with a failure is the only situation that causes an extra evaluation of END blocks. Other functions (capture, system, systemx) and successful capturex all behave correctly and do not evaluate END blocks multiple times. Therefore, there should be little concern of breaking existing code. It is unlikely that anyone is relying on calling END in the capturex child.