Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions btest-bg-run-helper
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@ esac
# taskkill /T terminates the process and all its descendants.
# MSYS_NO_PATHCONV prevents MSYS from interpreting /F, /T, /PID as paths.
win_kill_tree() {
local wpid
if [ $is_windows -eq 1 ] && [ -f .winpid ]; then
wpid=$(cat .winpid)
if [ $is_windows -eq 1 ]; then
local wpid=""
# Prefer the live /proc lookup: after exec the Windows PID may
# differ from the value captured at startup in .winpid.
if [ -n "$pid" ] && [ -f /proc/"$pid"/winpid ]; then
wpid=$(cat /proc/"$pid"/winpid)
elif [ -f .winpid ]; then
wpid=$(cat .winpid)
fi
if [ -n "$wpid" ]; then
MSYS_NO_PATHCONV=1 taskkill /F /T /PID "$wpid" &>/dev/null
fi
Expand Down Expand Up @@ -63,8 +69,14 @@ echo $$ >.pid

# Record the child's Windows PID so that btest-bg-wait can
# reliably terminate it even if MSYS PIDs become stale.
# Also record the child's MSYS PID (.winchildpid): btest-bg-wait
# can use it to look up the *current* Windows PID via /proc at kill
# time, which is more reliable than .winpid because exec may change
# the Windows PID.
# Guard: /proc entry disappears if the child exits before we get here.
if [ $is_windows -eq 1 ] && [ -f /proc/$pid/winpid ]; then
cat /proc/$pid/winpid >.winpid
echo $pid >.winchildpid
fi

wait $pid
Expand Down
17 changes: 17 additions & 0 deletions btest-bg-wait
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,23 @@ function kill_procs {
MSYS_NO_PATHCONV=1 taskkill /F /T /PID "$wpid" &>/dev/null
fi
fi

# The .winpid captured at startup may hold a stale
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a "may or may not" for unknown reasons (or perhaps a race condition), or does the /proc/$cpid/winpid case always hold true for Cygwin but not elsewhere and so this is a actually a Cygwin-specific check?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Unix, exec() replaces the process image but preserves the PID — that's a fundamental guarantee. Cygwin/MSYS2 can't do that because Windows has no native exec().
Instead, the runtime spawns a new Windows process and terminates the old one. So after exec():

  • MSYS/Cygwin PID → stays the same (virtual, tracked by the runtime)
  • Windows PID → changes (it's a brand-new native process)

This is exactly why the live /proc/$pid/winpid lookup is needed — .winpid captured the pre-exec Windows PID, which is now dead

# pre-exec Windows PID. Look up the *current* Windows
# PID from /proc using the child's MSYS PID, which
# remains valid and tracks the native process after exec.
if [ -f "$p/.winchildpid" ]; then
local cpid
cpid=$(cat "$p/.winchildpid")
if [ -n "$cpid" ] && [ -f /proc/"$cpid"/winpid ]; then
local live_wpid
live_wpid=$(cat /proc/"$cpid"/winpid)
if [ -n "$live_wpid" ]; then
MSYS_NO_PATHCONV=1 taskkill /F /T /PID "$live_wpid" &>/dev/null
fi
fi
fi

if [ -f "$p/.pid" ]; then
msys_pids="$msys_pids $(cat "$p/.pid")"
fi
Expand Down
Loading