22// The .NET Foundation licenses this file to you under the MIT license.
33
44using System . ComponentModel ;
5- using System . IO ;
65using System . Runtime . InteropServices ;
76using Microsoft . Win32 . SafeHandles ;
87
@@ -11,7 +10,8 @@ namespace System.Diagnostics
1110 public partial class Process
1211 {
1312 /// <summary>
14- /// Reads from both standard output and standard error pipes using Unix poll-based multiplexing.
13+ /// Reads from both standard output and standard error pipes using Unix poll-based multiplexing
14+ /// with non-blocking reads.
1515 /// </summary>
1616 private static void ReadPipes (
1717 SafeFileHandle outputHandle ,
@@ -25,6 +25,11 @@ private static void ReadPipes(
2525 int outputFd = outputHandle . DangerousGetHandle ( ) . ToInt32 ( ) ;
2626 int errorFd = errorHandle . DangerousGetHandle ( ) . ToInt32 ( ) ;
2727
28+ if ( Interop . Sys . Fcntl . DangerousSetIsNonBlocking ( ( IntPtr ) outputFd , 1 ) != 0 || Interop . Sys . Fcntl . DangerousSetIsNonBlocking ( ( IntPtr ) errorFd , 1 ) != 0 )
29+ {
30+ throw new Win32Exception ( ) ;
31+ }
32+
2833 Span < Interop . PollEvent > pollFds = stackalloc Interop . PollEvent [ 2 ] ;
2934
3035 long deadline = timeoutMs >= 0
@@ -101,7 +106,7 @@ private static void ReadPipes(
101106 ref int currentBytesRead = ref ( isError ? ref errorBytesRead : ref outputBytesRead ) ;
102107 ref bool currentDone = ref ( isError ? ref errorDone : ref outputDone ) ;
103108
104- int bytesRead = RandomAccess . Read ( currentHandle , currentBuffer . AsSpan ( currentBytesRead ) , fileOffset : 0 ) ;
109+ int bytesRead = ReadNonBlocking ( currentHandle , currentBuffer , currentBytesRead ) ;
105110 if ( bytesRead > 0 )
106111 {
107112 currentBytesRead += bytesRead ;
@@ -111,11 +116,37 @@ private static void ReadPipes(
111116 RentLargerBuffer ( ref currentBuffer , currentBytesRead ) ;
112117 }
113118 }
114- else
119+ else if ( bytesRead == 0 )
115120 {
121+ // EOF: pipe write end was closed.
116122 currentDone = true ;
117123 }
124+ // bytesRead < 0 means EAGAIN — nothing available yet, let poll retry.
125+ }
126+ }
127+ }
128+
129+ /// <summary>
130+ /// Performs a non-blocking read from the given handle into the buffer starting at the specified offset.
131+ /// Returns the number of bytes read, 0 for EOF, or -1 for EAGAIN (nothing available yet).
132+ /// </summary>
133+ private static unsafe int ReadNonBlocking ( SafeFileHandle handle , byte [ ] buffer , int offset )
134+ {
135+ fixed ( byte * pBuffer = buffer )
136+ {
137+ int bytesRead = Interop . Sys . Read ( handle , pBuffer + offset , buffer . Length - offset ) ;
138+ if ( bytesRead < 0 )
139+ {
140+ Interop . ErrorInfo errorInfo = Interop . Sys . GetLastErrorInfo ( ) ;
141+ if ( errorInfo . Error == Interop . Error . EAGAIN )
142+ {
143+ return - 1 ;
144+ }
145+
146+ throw new Win32Exception ( errorInfo . RawErrno ) ;
118147 }
148+
149+ return bytesRead ;
119150 }
120151 }
121152 }
0 commit comments