Skip to content

close(STDOUT) does not wait on MS-Windows #4106

Closed

Description

@p5pRT

Migrated from rt.perl.org#7142 (status was 'open')

Searchable as RT7142$

Activity

  1. p5pRT commented on Jun 19, 2001

    @p5pRT
    Author

    From @gisle

    The following program fails to work on Windows. The program
    terminates before the child finish. If another file handle than
    STDOUT is used, then it works as expected. It also works as expected
    on Linux.

    open(STDOUT, qq(| perl -pe "sleep 1;")) || die;
    print STDOUT "Hi\n" for 1..2;
    close(STDOUT);
    Perl Info
    
    Flags:
        category=core
        severity=medium
    
    Site configuration information for perl v5.6.1:
    
    Configured by gislea at Tue Jun 19 12:57:27 2001.
    
    Summary of my perl5 (revision 5 version 6 subversion 1) configuration:
      Platform:
        osname=MSWin32, osvers=4.0, archname=MSWin32-x86-multi-thread
        uname=''
        config_args='undef'
        hint=recommended, useposix=true, d_sigaction=undef
        usethreads=undef use5005threads=undef useithreads=define usemultiplicity=define
        useperlio=undef d_sfio=undef uselargefiles=undef usesocks=undef
        use64bitint=undef use64bitall=undef uselongdouble=undef
      Compiler:
        cc='cl', ccflags ='-nologo -O1 -MD -DNDEBUG -DWIN32 -D_CONSOLE -DNO_STRICT -DHAVE_DES_FCRYPT  -DPERL_IMPLICIT_CONTEXT -DPERL_IMPLICIT_SYS -DPERL_MSVCRT_READFIX',
        optimize='-O1 -MD -DNDEBUG',
        cppflags='-DWIN32'
        ccversion='', gccversion='', gccosandvers=''
        intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
        d_longlong=undef, longlongsize=8, d_longdbl=define, longdblsize=10
        ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=4
        alignbytes=8, usemymalloc=n, prototype=define
      Linker and Libraries:
        ld='', ldflags ='-nologo -nodefaultlib -release  -libpath:"C:\Perl\lib\CORE"  -machine:x86'
        libpth="C:\Program Files\Microsoft.NET\FrameworkSDK\Lib\" "C:\Perl\lib\CORE"
        libs=  oldnames.lib kernel32.lib user32.lib gdi32.lib winspool.lib  comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib  netapi32.lib uuid.lib wsock32.lib mpr.lib winmm.lib  version.lib odbc32.lib odbccp32.lib msvcrt.lib
        perllibs=  oldnames.lib kernel32.lib user32.lib gdi32.lib winspool.lib  comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib  netapi32.lib uuid.lib wsock32.lib mpr.lib winmm.lib  version.lib odbc32.lib odbccp32.lib msvcrt.lib
        libc=msvcrt.lib, so=dll, useshrplib=yes, libperl=perl56.lib
      Dynamic Linking:
        dlsrc=dl_win32.xs, dlext=dll, d_dlsymun=undef, ccdlflags=' '
        cccdlflags=' ', lddlflags='-dll -nologo -nodefaultlib -release  -libpath:"C:\Perl\lib\CORE"  -machine:x86'
    
    Locally applied patches:
        ACTIVEPERL_LOCAL_PATCHES_ENTRY
    
    
    @INC for perl v5.6.1:
        C:/Perl/lib
        C:/Perl/site/lib
        .
    
    
    Environment for perl v5.6.1:
        HOME (unset)
        LANG (unset)
        LANGUAGE (unset)
        LD_LIBRARY_PATH (unset)
        LOGDIR (unset)
        PATH=C:\PROGRA~1\MICROS~3\Common\msdev98\BIN;C:\PROGRA~1\MICROS~3\VC98\BIN;C:\PROGRA~1\MICROS~3\Common\TOOLS\WINNT;C:\PROGRA~1\MICROS~3\Common\TOOLS;C:\DevStudio\SharedIDE\BIN;C:\DevStudio\VC\BIN;C:\DevStudio\VC\BIN\WINNT;C:\Python21\;C:\t32;c:\bin;C:\Perl\bin\;C:\WINNT\system32;C:\WINNT;C:\WINNT\System32\Wbem;C:\WINNT\Microsoft.NET\Framework\v1.0.2728;C:\Program Files\Microsoft.NET\FrameworkSDK\Bin;h:\bin;C:\Program Files\Microsoft SQL Server\80\Tools\Binn\;\\rake\t32;G:\DevStudio\VC\DEVSTUDIO\VC\BIN;G:\DevStudio\VC\DEVSTUDIO\VC\BIN\WINNT
        PERLDB_OPTS=RemotePort=127.0.0.1:2000
        PERL_BADLANG (unset)
        SHELL (unset)
    
    
  2. p5pRT commented on May 25, 2016

    @p5pRT
    Author

    From horst.reiterer@fabasoft.com

    I can reproduce the issue with 5.10 and 5.22. The problem randomly breaks OpenSSL builds.

  3. p5pRT commented on May 25, 2016

    @p5pRT
    Author

    From [Unknown Contact. See original ticket]

    I can reproduce the issue with 5.10 and 5.22. The problem randomly breaks OpenSSL builds.

  4. richardleach commented on Oct 26, 2019

    @richardleach
    Contributor

    Still present in v5.30.0 on Win 10.

  5. toddr commented on Feb 4, 2020

    @toddr
    Member

    @gisle FYI your case was moved here.

  6. self-assigned this
    on Apr 23, 2025
  7. tonycoz commented on Apr 23, 2025

    @tonycoz
    Contributor

    non-Windows perl keeps process ids for piped opens in the PL_fdpid array, and for STD... handles openn_cleanup() carefully copies the process id from the original popenned fd to the STD... handle:

    perl5/doio.c

    Lines 1092 to 1107 in 6c95af2

    #if !defined(WIN32)
    /* PL_fdpid isn't used on Windows, so avoid this useless work.
    * XXX Probably the same for a lot of other places. */
    {
    Pid_t pid;
    SV *sv;
    sv = *av_fetch(PL_fdpid,fd,TRUE);
    SvUPGRADE(sv, SVt_IV);
    pid = SvIVX(sv);
    SvIV_set(sv, 0);
    sv = *av_fetch(PL_fdpid,savefd,TRUE);
    SvUPGRADE(sv, SVt_IV);
    SvIV_set(sv, pid);
    }
    #endif

    Windows being Windows, it does it's own thing, using a separate array w32_fdpid (which is a macro for PL_sys_intern.fdpid) to store piped process ids, and openn_cleanup() doesn't try to handle that.

    This means by the time we get to close(STDOUT) there's no process id associated with fd 1, so win32_pclose() fails to wait, and the parent process finishes execution without waiting for the child process to exit.

    At this point I don't know why Win32 uses its own array here.

  8. added a commit that references this issue on Apr 24, 2025
    dadf74b
  9. 3 remaining items

  10. added 4 commits that reference this issue on May 1, 2025
    f60fe53
    6a64781
    e009ed9
    eddb2de
  11. bulk88 commented on Jun 19, 2025

    @bulk88
    Contributor

    At this point I don't know why Win32 uses its own array here.

    Hysterical Raisins.

         status = spawnvp(mode, cmdname, (char * const *) argv);
    -#ifndef __BORLANDC__
    +#ifndef USE_RTL_WAIT
         /* XXX For the P_NOWAIT case, Borland RTL returns pinfo.dwProcessId
          * while VC RTL returns pinfo.hProcess. For purposes of the custom
          * implementation of win32_wait(), we assume the latter.
          */
    @@ -2121,6 +2119,13 @@ Perl_init_os_extras()
    

    4b556e6

    Borland Libc says a PID is a low 100s, low 1000s, random integer, obtained from MS public API

    DWORD
    WINAPI
    GetCurrentProcessId(VOID)
    {
        return HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess);
    }
    

    MS CRT 6 thru MS UCRT, umm, I think MS has changed the CRT docs long ago, but ancient Perl claims their popen() (???) returns CloseHandle() integers, not GetCurrentProcessId() integers like Borland.

    Current MSDN says retval of intptr_t spawnl(int Mode, const char *Filename, const char *ArgList, ...) depending on flags, is a CloseHandle() handle, not a NT PID.

    https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/spawnl-wspawnl?view=msvc-170

    more breadcrumbs: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/cwait?view=msvc-170

    my perl541.dll links to UCRT _spawnv and _execv only from perl541.dll's intptr_t win32_execv(const char *cmdname, const char *const *argv). perl541.dll's win32_execv() function is dead code inside perl541.dll. Function win32_execv() has no references from inside perl541.dll other than perl541.dll's PE export table.

    Inside miniperl.exe, which has no export table, symbol win32_execv() is referenced by static int yyl_fake_eof(pTHX_ U32 fake_eof, bool bof, char *s), at location

                if (!d
                    && *s == '#'
                    && ipathend > ipath
                    && !PL_minus_c
                    && !instr(s,"indir")
                    && instr(PL_origargv[0],"perl"))
                {
    ////// CCCUUUTTTTT
                    newargv[0] = ipath;
                    PERL_FPU_PRE_EXEC
                    PerlProc_execv(ipath, EXEC_ARGV_CAST(newargv)); <<<<<<<< HERE
                    PERL_FPU_POST_EXEC
                    croak("Can't exec %s", ipath);
                }
    

    decompiled, this isn't accurate b/c MSVC did so much variable and argument hoisting, folding, and inlining in this area, the asm code is unreadable, but it is approximately

            Perl_rsignal_save(SIGFPE, PL_sigfpe_saved, &xfpe);
            win32_execv(ipath, newargv);
            Perl_rsignal_restore(SIGFPE, &xfpe);
            Perl_die("Can't exec %s", ipath);
    

    but since the MS CRT/posix exec*() funcs are fatal calls, we dont care what the retval is. If it returns, we dont have a NT PID or NT HANDLE.

    I cant find a fuzzy match spawn or popen sym import from UCRT to miniperl.exe . My guess, GUESS !!!, is modern WinPerl always calls CreateProcess() directly and therefore DIYs its own implementations of spawn/exec/open/waitpid/cwait/pwait instead of using buggy half baked MS CRT implementations of POSIX functions.

    So WinPerl has no reason to attempt to separate proper NTnix/KERnix32 PIDs numbers from RefCounted NT Process handles for the last 10 or 20 years. WinPerl barely uses the MS CRT for anything but buffered STDIO and 0-based filenos nowadays.

    Trivia, What is a Windows NT PID integer?

    UM ClientId.UniqueProcess comes from KM UniqueProcessId. KM UniqueProcessId comes from ExCreateHandle(PspCidTable, &CidEntry);. So NT's PIDs numbers are offsets into a KM array of all live Process objects. For NT TIDs, s/Process/Thread/; So NT TIDs and PIDs use the same backend C class (lib/api) as "HANDLE"s from DuplicateHandle() and CloseHandle(). The TID/PID pool, and "HANDLE" pool, are 2 different object instances of the same "class". These 2 object instances of "class" obviously can't see each others private per object instance data (IE the array of void *s) and the numbers lives in different worlds, but both are super fast for the NT kernel to convert back and forth, and are super fast for NT Kernel to convert to a EPROCESS or KPROCESS struct ptr.

  12. added a commit that references this issue on Feb 8, 2026
    6d8e3bc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions