@@ -15,7 +15,7 @@ public partial class Process
1515 /// Reads from both standard output and standard error pipes using Windows overlapped IO
1616 /// with wait handles for single-threaded synchronous multiplexing.
1717 /// </summary>
18- private static void ReadPipes (
18+ private static unsafe void ReadPipes (
1919 SafeFileHandle outputHandle ,
2020 SafeFileHandle errorHandle ,
2121 int timeoutMs ,
@@ -24,131 +24,132 @@ private static void ReadPipes(
2424 ref byte [ ] errorBuffer ,
2525 ref int errorBytesRead )
2626 {
27- MemoryHandle outputPin = outputBuffer . AsMemory ( ) . Pin ( ) ;
28- MemoryHandle errorPin = errorBuffer . AsMemory ( ) . Pin ( ) ;
29-
30- unsafe
27+ MemoryHandle outputPin = default ;
28+ MemoryHandle errorPin = default ;
29+ NativeOverlapped * outputOverlapped = null ;
30+ NativeOverlapped * errorOverlapped = null ;
31+ EventWaitHandle ? outputEvent = null ;
32+ EventWaitHandle ? errorEvent = null ;
33+
34+ try
3135 {
32- NativeOverlapped * outputOverlapped = null ;
33- NativeOverlapped * errorOverlapped = null ;
36+ outputPin = outputBuffer . AsMemory ( ) . Pin ( ) ;
37+ errorPin = errorBuffer . AsMemory ( ) . Pin ( ) ;
3438
35- EventWaitHandle outputEvent = new ( initialState : false , EventResetMode . ManualReset ) ;
36- EventWaitHandle errorEvent = new ( initialState : false , EventResetMode . ManualReset ) ;
39+ outputEvent = new EventWaitHandle ( initialState : false , EventResetMode . ManualReset ) ;
40+ errorEvent = new EventWaitHandle ( initialState : false , EventResetMode . ManualReset ) ;
3741
38- try
39- {
40- outputOverlapped = AllocateOverlapped ( outputEvent ) ;
41- errorOverlapped = AllocateOverlapped ( errorEvent ) ;
42+ outputOverlapped = AllocateOverlapped ( outputEvent ) ;
43+ errorOverlapped = AllocateOverlapped ( errorEvent ) ;
4244
43- bool outputDone = false ;
44- bool errorDone = false ;
45+ bool outputDone = false ;
46+ bool errorDone = false ;
4547
46- WaitHandle [ ] waitHandles = [ outputEvent , errorEvent ] ;
48+ WaitHandle [ ] waitHandles = [ outputEvent , errorEvent ] ;
4749
48- // Issue initial reads.
49- Interop . Kernel32 . ReadFile ( outputHandle , ( byte * ) outputPin . Pointer + outputBytesRead ,
50- outputBuffer . Length - outputBytesRead , IntPtr . Zero , outputOverlapped ) ;
50+ // Issue initial reads.
51+ Interop . Kernel32 . ReadFile ( outputHandle , ( byte * ) outputPin . Pointer + outputBytesRead ,
52+ outputBuffer . Length - outputBytesRead , IntPtr . Zero , outputOverlapped ) ;
5153
52- Interop . Kernel32 . ReadFile ( errorHandle , ( byte * ) errorPin . Pointer + errorBytesRead ,
53- errorBuffer . Length - errorBytesRead , IntPtr . Zero , errorOverlapped ) ;
54+ Interop . Kernel32 . ReadFile ( errorHandle , ( byte * ) errorPin . Pointer + errorBytesRead ,
55+ errorBuffer . Length - errorBytesRead , IntPtr . Zero , errorOverlapped ) ;
5456
55- long deadline = timeoutMs >= 0
56- ? Environment . TickCount64 + timeoutMs
57- : long . MaxValue ;
57+ long deadline = timeoutMs >= 0
58+ ? Environment . TickCount64 + timeoutMs
59+ : long . MaxValue ;
5860
59- while ( ! outputDone || ! errorDone )
61+ while ( ! outputDone || ! errorDone )
62+ {
63+ int waitTimeout ;
64+ if ( timeoutMs >= 0 )
6065 {
61- int waitTimeout ;
62- if ( timeoutMs >= 0 )
63- {
64- long remaining = deadline - Environment . TickCount64 ;
65- if ( remaining <= 0 )
66- {
67- CancelPendingIOIfNeeded ( outputHandle , outputDone , outputOverlapped ) ;
68- CancelPendingIOIfNeeded ( errorHandle , errorDone , errorOverlapped ) ;
69- throw new TimeoutException ( ) ;
70- }
71-
72- waitTimeout = ( int ) Math . Min ( remaining , int . MaxValue ) ;
73- }
74- else
75- {
76- waitTimeout = Timeout . Infinite ;
77- }
78-
79- int waitResult = WaitHandle . WaitAny ( waitHandles , waitTimeout ) ;
80-
81- if ( waitResult == WaitHandle . WaitTimeout )
66+ long remaining = deadline - Environment . TickCount64 ;
67+ if ( remaining <= 0 )
8268 {
8369 CancelPendingIOIfNeeded ( outputHandle , outputDone , outputOverlapped ) ;
8470 CancelPendingIOIfNeeded ( errorHandle , errorDone , errorOverlapped ) ;
8571 throw new TimeoutException ( ) ;
8672 }
8773
88- bool isError = waitResult == 1 ;
89- NativeOverlapped * currentOverlapped = isError ? errorOverlapped : outputOverlapped ;
90- SafeFileHandle currentHandle = isError ? errorHandle : outputHandle ;
91- ref int totalBytesRead = ref ( isError ? ref errorBytesRead : ref outputBytesRead ) ;
92- ref byte [ ] currentBuffer = ref ( isError ? ref errorBuffer : ref outputBuffer ) ;
93- EventWaitHandle currentEvent = isError ? errorEvent : outputEvent ;
74+ waitTimeout = ( int ) Math . Min ( remaining , int . MaxValue ) ;
75+ }
76+ else
77+ {
78+ waitTimeout = Timeout . Infinite ;
79+ }
9480
95- int bytesRead = GetOverlappedResultForPipe ( currentHandle , currentOverlapped ) ;
81+ int waitResult = WaitHandle . WaitAny ( waitHandles , waitTimeout ) ;
9682
97- if ( bytesRead > 0 )
98- {
99- totalBytesRead += bytesRead ;
83+ if ( waitResult == WaitHandle . WaitTimeout )
84+ {
85+ CancelPendingIOIfNeeded ( outputHandle , outputDone , outputOverlapped ) ;
86+ CancelPendingIOIfNeeded ( errorHandle , errorDone , errorOverlapped ) ;
87+ throw new TimeoutException ( ) ;
88+ }
89+
90+ bool isError = waitResult == 1 ;
91+ NativeOverlapped * currentOverlapped = isError ? errorOverlapped : outputOverlapped ;
92+ SafeFileHandle currentHandle = isError ? errorHandle : outputHandle ;
93+ ref int totalBytesRead = ref ( isError ? ref errorBytesRead : ref outputBytesRead ) ;
94+ ref byte [ ] currentBuffer = ref ( isError ? ref errorBuffer : ref outputBuffer ) ;
95+ EventWaitHandle currentEvent = isError ? errorEvent : outputEvent ;
96+
97+ int bytesRead = GetOverlappedResultForPipe ( currentHandle , currentOverlapped ) ;
10098
101- if ( totalBytesRead == currentBuffer . Length )
102- {
103- ref MemoryHandle currentPin = ref ( isError ? ref errorPin : ref outputPin ) ;
104- currentPin . Dispose ( ) ;
99+ if ( bytesRead > 0 )
100+ {
101+ totalBytesRead += bytesRead ;
105102
106- RentLargerBuffer ( ref currentBuffer , totalBytesRead ) ;
103+ if ( totalBytesRead == currentBuffer . Length )
104+ {
105+ ref MemoryHandle currentPin = ref ( isError ? ref errorPin : ref outputPin ) ;
106+ currentPin . Dispose ( ) ;
107107
108- currentPin = currentBuffer . AsMemory ( ) . Pin ( ) ;
109- }
108+ RentLargerBuffer ( ref currentBuffer , totalBytesRead ) ;
110109
111- // Reset the event and overlapped for next read.
112- ResetOverlapped ( currentEvent , currentOverlapped ) ;
110+ currentPin = currentBuffer . AsMemory ( ) . Pin ( ) ;
111+ }
113112
114- byte * pinPointer = isError ? ( byte * ) errorPin . Pointer : ( byte * ) outputPin . Pointer ;
115- Interop . Kernel32 . ReadFile ( currentHandle , pinPointer + totalBytesRead ,
116- currentBuffer . Length - totalBytesRead , IntPtr . Zero , currentOverlapped ) ;
113+ // Reset the event and overlapped for next read.
114+ ResetOverlapped ( currentEvent , currentOverlapped ) ;
115+
116+ byte * pinPointer = isError ? ( byte * ) errorPin . Pointer : ( byte * ) outputPin . Pointer ;
117+ Interop . Kernel32 . ReadFile ( currentHandle , pinPointer + totalBytesRead ,
118+ currentBuffer . Length - totalBytesRead , IntPtr . Zero , currentOverlapped ) ;
119+ }
120+ else
121+ {
122+ // EOF: pipe write end was closed.
123+ if ( isError )
124+ {
125+ errorDone = true ;
117126 }
118127 else
119128 {
120- // EOF: pipe write end was closed.
121- if ( isError )
122- {
123- errorDone = true ;
124- }
125- else
126- {
127- outputDone = true ;
128- }
129-
130- // Reset the event so WaitAny won't trigger on this stale handle.
131- currentEvent . Reset ( ) ;
129+ outputDone = true ;
132130 }
131+
132+ // Reset the event so WaitAny won't trigger on this stale handle.
133+ currentEvent . Reset ( ) ;
133134 }
134135 }
135- finally
136+ }
137+ finally
138+ {
139+ if ( outputOverlapped is not null )
136140 {
137- if ( outputOverlapped != null )
138- {
139- NativeMemory . Free ( outputOverlapped ) ;
140- }
141-
142- if ( errorOverlapped != null )
143- {
144- NativeMemory . Free ( errorOverlapped ) ;
145- }
141+ NativeMemory . Free ( outputOverlapped ) ;
142+ }
146143
147- outputEvent . Dispose ( ) ;
148- errorEvent . Dispose ( ) ;
149- outputPin . Dispose ( ) ;
150- errorPin . Dispose ( ) ;
144+ if ( errorOverlapped is not null )
145+ {
146+ NativeMemory . Free ( errorOverlapped ) ;
151147 }
148+
149+ outputEvent ? . Dispose ( ) ;
150+ errorEvent ? . Dispose ( ) ;
151+ outputPin . Dispose ( ) ;
152+ errorPin . Dispose ( ) ;
152153 }
153154 }
154155
0 commit comments