Improve Windows pipe (FIFO) support

Use `WaitForPipeDrain` to deterministically wait for the pipe to drain instead of using `Start-Sleep`.

Use `Dispose` instead of `Close` to properly flush and close the pipe stream.

Add error handling for connection timeout and I/O errors.

Handle `ERROR_BROKEN_PIPE` separately when peeking at pipe, as this happens when the pipe is disconnected immediately after connecting it or after reading the previous message.

Don't ignore `ERROR_BAD_PIPE` anymore, as the pipe should never be in a disconnected (i.e. bad) state at this point of the function.
This commit is contained in:
Robert Müller 2023-09-05 13:50:03 +02:00
parent 3ff799770c
commit 1604784669
2 changed files with 28 additions and 14 deletions

View file

@ -4,7 +4,7 @@
# The second argument is the message to send.
if ($args.length -lt 2) {
Write-Output "Usage: ./send_named_pipe.ps1 <pipename> <message> [message] ... [message]"
return
exit -1
}
$Wrapper = [pscustomobject]@{
@ -18,16 +18,24 @@ $Wrapper = [pscustomobject]@{
Reader = $null
Writer = $null
}
$Wrapper.Pipe.Connect(5000)
if (!$?) {
return
try {
$Wrapper.Pipe.Connect(5000)
$Wrapper.Reader = New-Object System.IO.StreamReader($Wrapper.Pipe)
$Wrapper.Writer = New-Object System.IO.StreamWriter($Wrapper.Pipe)
$Wrapper.Writer.AutoFlush = $true
for ($i = 1; $i -lt $args.length; $i++) {
$Wrapper.Writer.WriteLine($args[$i])
}
# Wait for pipe contents to be read.
$Wrapper.Pipe.WaitForPipeDrain()
# Dispose the pipe, which also calls Flush and Close.
$Wrapper.Pipe.Dispose()
# Explicity set error level 0 for success, as otherwise the current error level is kept.
exit 0
} catch [TimeoutException] {
Write-Output "Timeout connecting to pipe"
exit 1
} catch [System.IO.IOException] {
Write-Output "Broken pipe"
exit 2
}
$Wrapper.Reader = New-Object System.IO.StreamReader($Wrapper.Pipe)
$Wrapper.Writer = New-Object System.IO.StreamWriter($Wrapper.Pipe)
$Wrapper.Writer.AutoFlush = $true
for ($i = 1; $i -lt $args.length; $i++) {
$Wrapper.Writer.WriteLine($args[$i])
}
# We need to wait because the lines will not be written if we close the pipe immediately
Start-Sleep -Seconds 1.5
$Wrapper.Pipe.Close()

View file

@ -154,7 +154,13 @@ void CFifo::Update()
if(!PeekNamedPipe(m_pPipe, NULL, 0, NULL, &BytesAvailable, NULL))
{
const DWORD LastError = GetLastError();
if(LastError != ERROR_BAD_PIPE) // pipe not connected, not an error
if(LastError == ERROR_BROKEN_PIPE)
{
// Pipe was disconnected from the other side, either immediately
// after connecting or after reading the previous message.
DisconnectNamedPipe(m_pPipe);
}
else
{
const std::string ErrorMsg = windows_format_system_message(LastError);
dbg_msg("fifo", "failed to peek at pipe '%s' (%ld %s)", m_aFilename, LastError, ErrorMsg.c_str());