22// The .NET Foundation licenses this file to you under the MIT license.
33
44using System ;
5+ using System . Buffers . Binary ;
56using System . Collections . Concurrent ;
7+ using System . IO ;
68using System . Threading ;
9+
10+ #if NET
711using System . Threading . Tasks ;
12+ #endif
13+
814using Microsoft . Build . Internal ;
915using Microsoft . Build . Shared ;
1016
@@ -42,15 +48,25 @@ internal sealed class MSBuildClientPacketPump : INodePacketHandler, INodePacketF
4248 /// </summary>
4349 private readonly NodePacketFactory _packetFactory ;
4450
51+ /// <summary>
52+ /// The memory stream for a read buffer.
53+ /// </summary>
54+ private readonly MemoryStream _readBufferMemoryStream ;
55+
4556 /// <summary>
4657 /// The thread which runs the asynchronous packet pump
4758 /// </summary>
4859 private Thread ? _packetPumpThread ;
4960
5061 /// <summary>
51- /// The pipe client from where to read packets.
62+ /// The stream from where to read packets.
5263 /// </summary>
53- private readonly NodePipeClient _pipeClient ;
64+ private readonly Stream _stream ;
65+
66+ /// <summary>
67+ /// The binary translator for reading packets.
68+ /// </summary>
69+ private readonly ITranslator _binaryReadTranslator ;
5470
5571 /// <summary>
5672 /// True if this side is gracefully disconnecting.
@@ -59,19 +75,21 @@ internal sealed class MSBuildClientPacketPump : INodePacketHandler, INodePacketF
5975 /// </summary>
6076 private bool _isServerDisconnecting ;
6177
62- public MSBuildClientPacketPump ( NodePipeClient pipeClient )
78+ public MSBuildClientPacketPump ( Stream stream )
6379 {
64- ErrorUtilities . VerifyThrowArgumentNull ( pipeClient ) ;
80+ ErrorUtilities . VerifyThrowArgumentNull ( stream ) ;
6581
66- _pipeClient = pipeClient ;
67- _pipeClient . RegisterPacketFactory ( this ) ;
82+ _stream = stream ;
6883 _isServerDisconnecting = false ;
6984 _packetFactory = new NodePacketFactory ( ) ;
7085
7186 ReceivedPacketsQueue = new ConcurrentQueue < INodePacket > ( ) ;
7287 PacketReceivedEvent = new AutoResetEvent ( false ) ;
7388 PacketPumpCompleted = new ManualResetEvent ( false ) ;
7489 _packetPumpShutdownEvent = new ManualResetEvent ( false ) ;
90+
91+ _readBufferMemoryStream = new MemoryStream ( ) ;
92+ _binaryReadTranslator = BinaryTranslator . GetReadTranslator ( _readBufferMemoryStream , InterningBinaryReader . CreateSharedBuffer ( ) ) ;
7593 }
7694
7795 #region INodePacketFactory Members
@@ -97,13 +115,14 @@ public void UnregisterPacketHandler(NodePacketType packetType)
97115 }
98116
99117 /// <summary>
100- /// Deserializes a packet .
118+ /// Deserializes and routes a packer to the appropriate handler .
101119 /// </summary>
120+ /// <param name="nodeId">The node from which the packet was received.</param>
102121 /// <param name="packetType">The packet type.</param>
103122 /// <param name="translator">The translator to use as a source for packet data.</param>
104- public INodePacket DeserializePacket ( NodePacketType packetType , ITranslator translator )
123+ public void DeserializeAndRoutePacket ( int nodeId , NodePacketType packetType , ITranslator translator )
105124 {
106- return _packetFactory . DeserializePacket ( packetType , translator ) ;
125+ _packetFactory . DeserializeAndRoutePacket ( nodeId , packetType , translator ) ;
107126 }
108127
109128 /// <summary>
@@ -165,16 +184,21 @@ public void Stop()
165184 /// </remarks>
166185 private void PacketPumpProc ( )
167186 {
168- RunReadLoop ( _pipeClient , _packetPumpShutdownEvent ) ;
187+ RunReadLoop ( _stream , _packetPumpShutdownEvent ) ;
169188 }
170189
171- private void RunReadLoop ( NodePipeClient pipeClient , ManualResetEvent localPacketPumpShutdownEvent )
190+ private void RunReadLoop ( Stream localStream , ManualResetEvent localPacketPumpShutdownEvent )
172191 {
173192 CommunicationsUtilities . Trace ( "Entering read loop." ) ;
174193
175194 try
176195 {
177- Task < INodePacket > readTask = pipeClient . ReadPacketAsync ( ) ;
196+ byte [ ] headerByte = new byte [ 5 ] ;
197+ #if FEATURE_APM
198+ IAsyncResult result = localStream . BeginRead ( headerByte , 0 , headerByte . Length , null , null ) ;
199+ #else
200+ Task < int > readTask = CommunicationsUtilities . ReadAsync ( localStream , headerByte , headerByte . Length ) . AsTask ( ) ;
201+ #endif
178202
179203 bool continueReading = true ;
180204 do
@@ -186,7 +210,11 @@ private void RunReadLoop(NodePipeClient pipeClient, ManualResetEvent localPacket
186210 WaitHandle [ ] handles =
187211 [
188212 localPacketPumpShutdownEvent ,
213+ #if FEATURE_APM
214+ result . AsyncWaitHandle
215+ #else
189216 ( ( IAsyncResult ) readTask ) . AsyncWaitHandle
217+ #endif
190218 ] ;
191219 int waitId = WaitHandle . WaitAny ( handles ) ;
192220 switch ( waitId )
@@ -198,27 +226,80 @@ private void RunReadLoop(NodePipeClient pipeClient, ManualResetEvent localPacket
198226 break ;
199227
200228 case 1 :
201- INodePacket packet = readTask . GetAwaiter ( ) . GetResult ( ) ;
202-
203- if ( packet . Type == NodePacketType . NodeShutdown )
204229 {
205- if ( ! _isServerDisconnecting )
230+ // Client recieved a packet header. Read the rest of it.
231+ int headerBytesRead = 0 ;
232+ #if FEATURE_APM
233+ headerBytesRead = localStream . EndRead ( result ) ;
234+ #else
235+ headerBytesRead = readTask . Result ;
236+ #endif
237+
238+ if ( ( headerBytesRead != headerByte . Length ) && ! localPacketPumpShutdownEvent . WaitOne ( 0 ) )
206239 {
207- ErrorUtilities . ThrowInternalError ( "Server disconnected abruptly." ) ;
240+ // Incomplete read. Abort.
241+ if ( headerBytesRead == 0 )
242+ {
243+ if ( _isServerDisconnecting )
244+ {
245+ continueReading = false ;
246+ break ;
247+ }
248+
249+ ErrorUtilities . ThrowInternalError ( "Server disconnected abruptly" ) ;
250+ }
251+ else
252+ {
253+ ErrorUtilities . ThrowInternalError ( "Incomplete header read. {0} of {1} bytes read" , headerBytesRead , headerByte . Length ) ;
254+ }
208255 }
209256
210- continueReading = false ;
211- break ;
212- }
257+ NodePacketType packetType = ( NodePacketType ) Enum . ToObject ( typeof ( NodePacketType ) , headerByte [ 0 ] ) ;
213258
214- _packetFactory . RoutePacket ( 0 , packet ) ;
259+ int packetLength = BinaryPrimitives . ReadInt32LittleEndian ( new Span < byte > ( headerByte , 1 , 4 ) ) ;
260+ int packetBytesRead = 0 ;
215261
216- continueReading = packet . Type != NodePacketType . ServerNodeBuildResult ;
217- if ( continueReading )
218- {
219- readTask = pipeClient . ReadPacketAsync ( ) ;
220- }
262+ _readBufferMemoryStream . Position = 0 ;
263+ _readBufferMemoryStream . SetLength ( packetLength ) ;
264+ byte [ ] packetData = _readBufferMemoryStream . GetBuffer ( ) ;
221265
266+ while ( packetBytesRead < packetLength )
267+ {
268+ int bytesRead = localStream . Read ( packetData , packetBytesRead , packetLength - packetBytesRead ) ;
269+ if ( bytesRead == 0 )
270+ {
271+ // Incomplete read. Abort.
272+ ErrorUtilities . ThrowInternalError ( "Incomplete packet read. {0} of {1} bytes read" , packetBytesRead , packetLength ) ;
273+ }
274+
275+ packetBytesRead += bytesRead ;
276+ }
277+
278+ try
279+ {
280+ _packetFactory . DeserializeAndRoutePacket ( 0 , packetType , _binaryReadTranslator ) ;
281+ }
282+ catch
283+ {
284+ // Error while deserializing or handling packet. Logging additional info.
285+ CommunicationsUtilities . Trace ( "Packet factory failed to receive package. Exception while deserializing packet {0}." , packetType ) ;
286+ throw ;
287+ }
288+
289+ if ( packetType == NodePacketType . ServerNodeBuildResult )
290+ {
291+ continueReading = false ;
292+ }
293+ else
294+ {
295+ // Start reading the next package header.
296+ #if FEATURE_APM
297+ result = localStream . BeginRead ( headerByte , 0 , headerByte . Length , null , null ) ;
298+ #else
299+ readTask = CommunicationsUtilities . ReadAsync ( localStream , headerByte , headerByte . Length ) . AsTask ( ) ;
300+ #endif
301+ }
302+ }
222303 break ;
223304
224305 default :
0 commit comments