From 1604784669fb88255f80d981d61fbe132cd1446e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Tue, 5 Sep 2023 13:50:03 +0200 Subject: [PATCH] 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. --- scripts/send_named_pipe.ps1 | 34 +++++++++++++++++++++------------- src/engine/shared/fifo.cpp | 8 +++++++- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/scripts/send_named_pipe.ps1 b/scripts/send_named_pipe.ps1 index fa659deb3..f3dcfe86c 100644 --- a/scripts/send_named_pipe.ps1 +++ b/scripts/send_named_pipe.ps1 @@ -4,7 +4,7 @@ # The second argument is the message to send. if ($args.length -lt 2) { Write-Output "Usage: ./send_named_pipe.ps1 [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() diff --git a/src/engine/shared/fifo.cpp b/src/engine/shared/fifo.cpp index 7ed0b296b..af2b49f09 100644 --- a/src/engine/shared/fifo.cpp +++ b/src/engine/shared/fifo.cpp @@ -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());