Repository navigation
Pipeline hangs forever: external command blocks in fhandler_pipe_fifo::raw_write waiting on pipe_mtx after the reader exits #6502
Description
Setup
- Which version of Git for Windows are you using?
git version 2.54.0.windows.1, 64-bit - msys2-runtime:
3.6.7-4(from/etc/package-versions.txt), commitfb42d71358dd896ab324c52970f7d03f9ab0dfe5 msys-2.0.dllSHA-256:13f0b0dc94588766ecfa1867f1a00061508ba1dc62f5b8e858ac59f01e358aa0(.gnu_debuglinkmsys-2.0.dbg, CRC320x23a6d10d; matching.dbgfrommsys2-runtime-devel-3.6.7-4-x86_64.pkg.tar.xz)- Windows: Windows 11 Home 10.0.26200, 64-bit, 4 cores
- Options: defaults. Git Bash (
C:\Program Files\Git\usr\bin\bash.exe), no terminal involved (the commands ran non-interactively).
Details
Minimal reproducer (run it a few times):
timeout 20 bash -c '{ echo a; /usr/bin/echo x; echo a; /usr/bin/echo x; } | true'; echo "rc=$?"Observed:
- Hangs 10/10 runs (killed by
timeout, rc 124). Withouttimeoutit never returns. - Each run prints
bash: line 1: echo: write error: Permission denied: the builtinechogets EACCES, not EPIPE. - With a delay first, so that the reader is gone before any write, it hangs 0/10:
{ sleep 1; echo a; /usr/bin/echo x; echo a; /usr/bin/echo x; } | true - Control:
{ sleep 1; /usr/bin/echo x; } | true→ the writer dies with SIGPIPE (rc 141), 3/3.
Expected: each writer gets EPIPE/SIGPIPE once the reader (true) has exited, and the pipeline finishes, as on Linux.
Real-world trigger: for f in dir/*/file.json; do echo "$f"; grep -c … "$f"; file "$f"; done | head -20 never finished once head exited (more than 20 lines of output). The loop mixes builtin echo with external commands, like the reproducer.
Where it hangs
Full memory dump of the hung /usr/bin/echo x (taken with ProcDump), symbolized with the matching msys-2.0.dbg. Main thread:
WaitForMultipleObjects
cygwait
fhandler_pipe_fifo::raw_write+0x6d
write
_write_r
__sflush_r
_fclose_r
_sigbe / pthread_mutex_unlock
__call_exitprocs
exit
cygwin_exit
The signal thread (wait_sig) is in ReadFile, as normal.
winsup/cygwin/fhandler/pipe.cc at fb42d713, lines 464–467:
if (pipe_mtx) /* pipe_mtx is NULL in the fifo case */
{
DWORD timeout = is_nonblocking () ? 0 : INFINITE;
DWORD waitret = cygwait (pipe_mtx, timeout);So the process blocks with no time limit while it acquires pipe_mtx during the exit-time flush of stdout. The process never gets to the NtWriteFile that would return STATUS_PIPE_CLOSED and raise SIGPIPE.
What I could not establish: which thread owns pipe_mtx. The holder must be alive, because an abandoned mutex would make cygwait return an error instead of blocking. The candidates are the other live processes of the pipeline (the bash subshell that ran the builtin echo a, and the parent bash). I could not read the mutex owner from user mode. One possibly relevant detail: raise (SIGPIPE) at lines 538 and 703 runs while pipe_mtx is still held (it is released later at 721/730). I have not confirmed that this is the cause.
The dump (~22 MB) is available on request.
Possibly related: msys2/msys2-runtime#202, https://inbox.sourceware.org/cygwin-patches/87edcqgfwc.fsf@/T/
Did you check the latest release?
Yes. After upgrading to git version 2.55.0.windows.5 (msys2-runtime 3.6.10-1, msys-2.0.dll SHA-256 e55be2d89f2756f540a8f2acf18cd14bf275d1187a32d5189946ee5c896bcf03), the reproducer still hangs 10/10 and still prints echo: write error: Permission denied. winsup/cygwin/fhandler/pipe.cc on main of git-for-windows/msys2-runtime is identical to the fb42d713 version quoted above. The dump and stack above are from 2.54.0.windows.1.
Activity