This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eryksun
Recipients eryksun, jimbo1qaz_
Date 2019-01-17.01:57:18
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1547690238.43.0.491477605043.issue35754@roundup.psfhosted.org>
In-reply-to
Content
If I'm right, we can reduce your example down as follows:

    import os
    import subprocess
    import time
    import ctypes

    RtlGetLastNtStatus = ctypes.WinDLL('ntdll').RtlGetLastNtStatus
    RtlGetLastNtStatus.restype = ctypes.c_ulong

    msys = os.path.normpath("C:/msys64/usr/bin")
    head = os.path.join(msys, "head.exe")

    p = subprocess.Popen(head, stdin=subprocess.PIPE,
            stdout=subprocess.PIPE, bufsize=0)

    # head.exe reads 1 KiB. It closes stdin if it finds 10 lines.
    p.stdin.write(b'\n' * 1024)

    # If we immediately fill up the pipe again plus 1 extra byte, 
    # i.e. 4097 bytes for the default queue size, then NPFS will 
    # internally queue a pending IRP. We're synchronous, so the 
    # I/O manager will wait for I/O completion. Meanwhile the child
    # has requested to close its end of the pipe. In this case,
    # NPFS will complete the pending IRP with STATUS_PIPE_BROKEN,
    # which maps to WinAPI ERROR_PIPE_BROKEN and C errno EPIPE.
    #
    # On the other hand, if we wait to give the child's close request
    # time to complete, then NPFS will fail our 4097 byte write 
    # immediately with STATUS_PIPE_CLOSING, which maps to WinAPI
    # ERROR_NO_DATA and C errno EINVAL.

    time.sleep(0.0) # STATUS_PIPE_BROKEN / ERROR_PIPE_BROKEN / EPIPE
    #time.sleep(0.5) # STATUS_PIPE_CLOSING / ERROR_NO_DATA / EINVAL

    try:
        p.stdin.write(b'\n' * 4097)
    except OSError:
        ntstatus = RtlGetLastNtStatus()
        if ntstatus == 0xC000_00B1:
           print('NT Status: STATUS_PIPE_CLOSING\n')
        elif ntstatus == 0xC000_014B:
            print('NT Status: STATUS_PIPE_BROKEN\n')
        else:
            print('NT Status: {}\n'.format(ntstatus, '#010x'))
        raise

This could be addressed by improving our exception handling to look at the C runtime's _doserrno [1] value for EINVAL errors, in order to map ERROR_NO_DATA to EPIPE instead of EINVAL. Only two NT status codes are mapped to ERROR_NO_DATA, and both are pipe related (STATUS_PIPE_CLOSING and STATUS_PIPE_EMPTY), so using EPIPE should be fine.

[1]: https://docs.microsoft.com/en-us/cpp/c-runtime-library/errno-doserrno-sys-errlist-and-sys-nerr?view=vs-2017
History
Date User Action Args
2019-01-17 01:57:20eryksunsetrecipients: + eryksun, jimbo1qaz_
2019-01-17 01:57:18eryksunsetmessageid: <1547690238.43.0.491477605043.issue35754@roundup.psfhosted.org>
2019-01-17 01:57:18eryksunlinkissue35754 messages
2019-01-17 01:57:18eryksuncreate