@@ -5,6 +5,7 @@ namespace DotPilot.Runtime.Features.ToolchainCenter;
55internal static class ToolchainCommandProbe
66{
77 private static readonly TimeSpan CommandTimeout = TimeSpan . FromSeconds ( 2 ) ;
8+ private static readonly TimeSpan RedirectDrainTimeout = TimeSpan . FromSeconds ( 1 ) ;
89 private const string VersionSeparator = "version" ;
910 private const string EmptyOutput = "" ;
1011
@@ -87,14 +88,19 @@ private static ToolchainCommandExecution Execute(string executablePath, IReadOnl
8788
8889 using ( process )
8990 {
90- var standardOutputTask = process . StandardOutput . ReadToEndAsync ( ) ;
91- var standardErrorTask = process . StandardError . ReadToEndAsync ( ) ;
91+ var standardOutputTask = ObserveRedirectedStream ( process . StandardOutput . ReadToEndAsync ( ) ) ;
92+ var standardErrorTask = ObserveRedirectedStream ( process . StandardError . ReadToEndAsync ( ) ) ;
9293
9394 if ( ! process . WaitForExit ( ( int ) CommandTimeout . TotalMilliseconds ) )
9495 {
9596 TryTerminate ( process ) ;
96- ObserveRedirectedStreamFaults ( standardOutputTask , standardErrorTask ) ;
97- return new ( true , false , EmptyOutput , EmptyOutput ) ;
97+ WaitForTermination ( process ) ;
98+
99+ return new (
100+ true ,
101+ false ,
102+ AwaitStreamRead ( standardOutputTask ) ,
103+ AwaitStreamRead ( standardErrorTask ) ) ;
98104 }
99105
100106 return new (
@@ -105,10 +111,26 @@ private static ToolchainCommandExecution Execute(string executablePath, IReadOnl
105111 }
106112 }
107113
114+ private static Task < string > ObserveRedirectedStream ( Task < string > readTask )
115+ {
116+ _ = readTask . ContinueWith (
117+ static task => _ = task . Exception ,
118+ CancellationToken . None ,
119+ TaskContinuationOptions . OnlyOnFaulted | TaskContinuationOptions . ExecuteSynchronously ,
120+ TaskScheduler . Default ) ;
121+
122+ return readTask ;
123+ }
124+
108125 private static string AwaitStreamRead ( Task < string > readTask )
109126 {
110127 try
111128 {
129+ if ( ! readTask . Wait ( RedirectDrainTimeout ) )
130+ {
131+ return EmptyOutput ;
132+ }
133+
112134 return readTask . GetAwaiter ( ) . GetResult ( ) ;
113135 }
114136 catch
@@ -117,19 +139,28 @@ private static string AwaitStreamRead(Task<string> readTask)
117139 }
118140 }
119141
120- private static void ObserveRedirectedStreamFaults ( Task < string > standardOutputTask , Task < string > standardErrorTask )
142+ private static void TryTerminate ( Process process )
121143 {
122- _ = standardOutputTask . Exception ;
123- _ = standardErrorTask . Exception ;
144+ try
145+ {
146+ if ( ! process . HasExited )
147+ {
148+ process . Kill ( entireProcessTree : true ) ;
149+ }
150+ }
151+ catch
152+ {
153+ // Best-effort cleanup only.
154+ }
124155 }
125156
126- private static void TryTerminate ( Process process )
157+ private static void WaitForTermination ( Process process )
127158 {
128159 try
129160 {
130161 if ( ! process . HasExited )
131162 {
132- process . Kill ( entireProcessTree : true ) ;
163+ process . WaitForExit ( ( int ) RedirectDrainTimeout . TotalMilliseconds ) ;
133164 }
134165 }
135166 catch
0 commit comments