Skip to content

Commit e9d373c

Browse files
committed
[perl #119893] avoid waiting on pid 0
When a filehandle is cloned into a standard handle, do_openn() copies the pid from the original handle in PL_fdpid to the standard handle and zeroes the entry for the original handle, so when the original handle was closed Perl_my_pclose() would call wait4pid() with a pid of 0. With v5.19.3-614-gd4c0274 I modified wait4pid(), perl's waitpid/wait4() wrapper, to allow a pid of zero through to the actual system call when available. These combined so that following v5.19.3-614-gd4c0274 in some circumstances closing the original handle would block by calling waitpid(0, ...) or wait4(0, ...), which waits for any child process in the same process group to terminate. This commit changes Perl_my_pclose() to wait for the child only when the stored pid is positive.
1 parent f2a7d0f commit e9d373c

File tree

3 files changed

+56
-6
lines changed

3 files changed

+56
-6
lines changed

MANIFEST

+1
Original file line numberDiff line numberDiff line change
@@ -4993,6 +4993,7 @@ time64.h 64 bit clean time.h (header)
49934993
t/io/argv.t See if ARGV stuff works
49944994
t/io/binmode.t See if binmode() works
49954995
t/io/bom.t See if scripts can start with a byte order mark
4996+
t/io/closepid.t See if close works for subprocesses
49964997
t/io/crlf.t See if :crlf works
49974998
t/io/crlf_through.t See if pipe passes data intact with :crlf
49984999
t/io/data.t See if DATA works

t/io/closepid.t

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!./perl
2+
3+
BEGIN {
4+
chdir 't' if -d 't';
5+
@INC = '../lib';
6+
require './test.pl';
7+
}
8+
9+
if ($^O eq 'dos') {
10+
skip_all("no multitasking");
11+
}
12+
13+
plan tests => 3;
14+
watchdog(10, $^O eq 'MSWin32' ? "alarm" : '');
15+
16+
use Config;
17+
$| = 1;
18+
$SIG{PIPE} = 'IGNORE';
19+
$SIG{HUP} = 'IGNORE' if $^O eq 'interix';
20+
21+
my $perl = which_perl();
22+
$perl .= qq[ "-I../lib"];
23+
24+
my $killsig = 'HUP';
25+
$killsig = 1 unless $Config{sig_name} =~ /\bHUP\b/;
26+
27+
SKIP:
28+
{
29+
skip("Not relevant to $^O", 3)
30+
if $^O eq "MSWin32" || $^O eq "VMS";
31+
skip("only matters for waitpid or wait4", 3)
32+
unless $Config{d_waitpid} || $Config{d_wait4};
33+
# [perl #119893]
34+
# close on the original of a popen handle dupped to a standard handle
35+
# would wait4pid(0, ...)
36+
open my $savein, "<&", \*STDIN;
37+
my $pid = open my $fh1, qq/$perl -e "sleep 50" |/;
38+
ok($pid, "open a pipe");
39+
# at this point PL_fdpids[fileno($fh1)] is the pid of the new process
40+
ok(open(STDIN, "<&=", $fh1), "dup the pipe");
41+
# now PL_fdpids[fileno($fh1)] is zero and PL_fdpids[0] is
42+
# the pid of the process created above, previously this would block
43+
# internally on waitpid(0, ...)
44+
ok(close($fh1), "close the original");
45+
kill $killsig, $pid;
46+
open STDIN, "<&", $savein;
47+
}

util.c

+8-6
Original file line numberDiff line numberDiff line change
@@ -2706,19 +2706,21 @@ Perl_my_pclose(pTHX_ PerlIO *ptr)
27062706
bool close_failed;
27072707
dSAVEDERRNO;
27082708
const int fd = PerlIO_fileno(ptr);
2709+
bool should_wait;
2710+
2711+
svp = av_fetch(PL_fdpid,fd,TRUE);
2712+
pid = (SvTYPE(*svp) == SVt_IV) ? SvIVX(*svp) : -1;
2713+
SvREFCNT_dec(*svp);
2714+
*svp = NULL;
27092715

27102716
#ifdef USE_PERLIO
27112717
/* Find out whether the refcount is low enough for us to wait for the
27122718
child proc without blocking. */
2713-
const bool should_wait = PerlIOUnix_refcnt(fd) == 1;
2719+
should_wait = PerlIOUnix_refcnt(fd) == 1 && pid > 0;
27142720
#else
2715-
const bool should_wait = 1;
2721+
should_wait = pid > 0;
27162722
#endif
27172723

2718-
svp = av_fetch(PL_fdpid,fd,TRUE);
2719-
pid = (SvTYPE(*svp) == SVt_IV) ? SvIVX(*svp) : -1;
2720-
SvREFCNT_dec(*svp);
2721-
*svp = NULL;
27222724
#ifdef OS2
27232725
if (pid == -1) { /* Opened by popen. */
27242726
return my_syspclose(ptr);

0 commit comments

Comments
 (0)