From af8b09cfa2bbb05cd9ec390c151f002a6423ea58 Mon Sep 17 00:00:00 2001 From: Dave Friedel Date: Wed, 7 Jan 2026 11:57:13 -0500 Subject: [PATCH] Fix DDC/CI timeout using Task.WhenAny MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous timeout using CancellationToken didn't work because ReadToEndAsync blocks waiting for process output. Now using Task.WhenAny with Task.Delay to properly timeout and kill hung processes after 5 seconds. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- DellMonitorControl/DellMonitorControl.csproj | 6 +-- Library/Helpers/ConsoleHelper.cs | 42 +++++++++++--------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/DellMonitorControl/DellMonitorControl.csproj b/DellMonitorControl/DellMonitorControl.csproj index 811b3bd..dcb574b 100644 --- a/DellMonitorControl/DellMonitorControl.csproj +++ b/DellMonitorControl/DellMonitorControl.csproj @@ -13,9 +13,9 @@ - 1.0.13 - 1.0.13.0 - 1.0.13.0 + 1.0.14 + 1.0.14.0 + 1.0.14.0 diff --git a/Library/Helpers/ConsoleHelper.cs b/Library/Helpers/ConsoleHelper.cs index c8fefc1..e490303 100644 --- a/Library/Helpers/ConsoleHelper.cs +++ b/Library/Helpers/ConsoleHelper.cs @@ -28,18 +28,17 @@ internal class ConsoleHelper p.StartInfo.FileName = command; p.Start(); - using var cts = new CancellationTokenSource(timeoutMs); - try + var readTask = p.StandardOutput.ReadToEndAsync(); + var completedTask = await Task.WhenAny(readTask, Task.Delay(timeoutMs)); + + if (completedTask != readTask) { - var output = await p.StandardOutput.ReadToEndAsync(cts.Token); - await p.WaitForExitAsync(cts.Token); - return output; - } - catch (OperationCanceledException) - { - try { p.Kill(); } catch { } + // Timeout - kill the process + try { p.Kill(true); } catch { } return string.Empty; } + + return await readTask; } public static async Task CmdCommandAsync(params string[] cmds) => @@ -63,23 +62,28 @@ internal class ConsoleHelper } p.StandardInput.WriteLine("exit"); - using var cts = new CancellationTokenSource(timeoutMs); - try + var readTask = Task.Run(async () => { - var result = await p.StandardOutput.ReadToEndAsync(cts.Token); - var error = await p.StandardError.ReadToEndAsync(cts.Token); + var result = await p.StandardOutput.ReadToEndAsync(); + var error = await p.StandardError.ReadToEndAsync(); if (!string.IsNullOrWhiteSpace(error)) result = result + "\r\n:\r\n" + error; - await p.WaitForExitAsync(cts.Token); - p.Close(); - Debug.WriteLine(result); return result; - } - catch (OperationCanceledException) + }); + + var completedTask = await Task.WhenAny(readTask, Task.Delay(timeoutMs)); + + if (completedTask != readTask) { - try { p.Kill(); } catch { } + // Timeout - kill the process + try { p.Kill(true); } catch { } return string.Empty; } + + var output = await readTask; + p.Close(); + Debug.WriteLine(output); + return output; } public static string Command(string fileName, params string[] cmds)