Skip to content

Commit

Permalink
Add a PowerShell script to work around those pacman.exe hangs
Browse files Browse the repository at this point in the history
For some reason, there are relatively consistent hangs on Windows/ARM64
when trying to run `pacman.exe` multiple times. Terminating the "right"
processes seems to unblock those hangs (without failure!)

The most likely explanation is a dead-lock in the (x86_64) MSYS2 runtime
where the process that handles signals for child processes is waiting
for that child process, but it is long gone, and the signal handler
process waits forever, blocking its parent process forever.

To help with that, let's implements the PowerShell script outlined in
git-for-windows/git#4883 (comment)
that identifies those hanging processes, waits for them to be idle just
to be extra certain not to disrupt any regular Pacman operation that
waits for a good reason.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
  • Loading branch information
dscho committed May 3, 2024
1 parent 36b089f commit 1da4b89
Showing 1 changed file with 71 additions and 0 deletions.
71 changes: 71 additions & 0 deletions work-around-hanging-pacman.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# This script tries to help with the frequent `pacman.exe` hangs on
# Windows/ARM64
#
# NOTE! This is just a _work-around_, no proper solution. The hope is still
# that we will eventually figure out what is going wrong in the MSYS2 runtime
# and fix it properly.
#
# The symptom is that there are processes whose `CommandLine` is identical to
# their parent process, hanging around waiting for something that is not
# happening. The working theory is that they are waiting for some child process
# that is long gone. Blocking their parent process while doing so.
#
# This script works around that by polling the list of current processes,
# finding `pacman` processes whose `CommandLine` is identical to their
# respective parent process'. Once such a child process is found, this script
# waits a little to see whether the child process consumes a non-negligible
# amount of CPU time, and if it has been idle for half a minute, terminates it.

# Set-PSDebug -Trace 1
Set-PSDebug -Off

function die {
Param(
[Parameter(Mandatory=$true,Position=0)] [String]$Message
)
[Console]::Error.WriteLine($Message)
exit 1
}

function getHangingPacmanProcess {
$p = (Get-CimInstance Win32_Process |
Where-Object {$_.CommandLine -like '*pacman*'} |
Select-Object CommandLine, ParentProcessId, ProcessId |
Group-Object CommandLine |
Where-Object {$_.Count -gt 1} |
Select-Object -ExpandProperty Group)
if ($p.Length -eq 2 -and $p[0].processId -eq $p[1].parentProcessId) {
return $p[1]
}
return $null
}

function pollForHangingPacmanProcessesAndTerminateThem {
param (
[int]$sleepSecondsBetweenPolling = 15,
[int]$idleFactor = 0.01
)
$alreadySeenPID = $null
$alreadySeenCPU = 0
while ($true) {
$hangingProcess = getHangingPacmanProcess
$hangingPID = $hangingProcess.processId
if ($hangingPID -ne $null) {
$CPU = $hangingProcess.CPU
if ($alreadySeenPID -ne $hangingPID) {
Write-Host "Looking at PID $hangingPID"
} elseif (($CPU - $alreadySeenCPU) -gt ($sleepSecondsBetweenPolling * $idleFactor)) {
Write-Host "PID $hangingPID still too busy: spent $($CPU - $alreadySeenCPU)/$sleepSecondsBetweenPolling seconds"
} else {
Write-Host "Terminating $hangingPID"
Stop-Process -Force -Id $hangingPID
$hangingPID = $null
}
$alreadySeenCPU = $CPU
}
$alreadySeenPID = $hangingPID
Start-Sleep -Seconds $sleepSecondsBetweenPolling
}
}

pollForHangingPacmanProcessesAndTerminateThem -SleepSecondsBetweenPolling 30

0 comments on commit 1da4b89

Please sign in to comment.