Skip to content

Commit 06d1cb4

Browse files
[REVERT] 11546 refactor common pipe code (#11648)
1 parent 37eb2be commit 06d1cb4

21 files changed

Lines changed: 1219 additions & 192 deletions

src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public void UnregisterPacketHandler(NodePacketType packetType)
100100
throw new NotImplementedException();
101101
}
102102

103-
public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator)
103+
public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator)
104104
{
105105
throw new NotImplementedException();
106106
}

src/Build/BackEnd/Client/MSBuildClient.cs

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Diagnostics;
88
using System.Globalization;
99
using System.IO;
10+
using System.IO.Pipes;
1011
using System.Threading;
1112
using Microsoft.Build.BackEnd;
1213
using Microsoft.Build.BackEnd.Client;
@@ -74,9 +75,19 @@ public sealed class MSBuildClient
7475
private readonly string _pipeName;
7576

7677
/// <summary>
77-
/// The named pipe client for client-server communication.
78+
/// The named pipe stream for client-server communication.
7879
/// </summary>
79-
private NodePipeClient _pipeClient = null!;
80+
private NamedPipeClientStream _nodeStream = null!;
81+
82+
/// <summary>
83+
/// A way to cache a byte array when writing out packets
84+
/// </summary>
85+
private readonly MemoryStream _packetMemoryStream;
86+
87+
/// <summary>
88+
/// A binary writer to help write into <see cref="_packetMemoryStream"/>
89+
/// </summary>
90+
private readonly BinaryWriter _binaryWriter;
8091

8192
/// <summary>
8293
/// Used to estimate the size of the build with an ETW trace.
@@ -119,14 +130,26 @@ public MSBuildClient(
119130
// Client <-> Server communication stream
120131
_handshake = GetHandshake();
121132
_pipeName = OutOfProcServerNode.GetPipeName(_handshake);
133+
_packetMemoryStream = new MemoryStream();
134+
_binaryWriter = new BinaryWriter(_packetMemoryStream);
122135

123136
CreateNodePipeStream();
124137
}
125138

126139
private void CreateNodePipeStream()
127140
{
128-
_pipeClient = new NodePipeClient(_pipeName, _handshake);
129-
_packetPump = new MSBuildClientPacketPump(_pipeClient);
141+
#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter
142+
_nodeStream = new NamedPipeClientStream(
143+
serverName: ".",
144+
_pipeName,
145+
PipeDirection.InOut,
146+
PipeOptions.Asynchronous
147+
#if FEATURE_PIPEOPTIONS_CURRENTUSERONLY
148+
| PipeOptions.CurrentUserOnly
149+
#endif
150+
);
151+
#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter
152+
_packetPump = new MSBuildClientPacketPump(_nodeStream);
130153
}
131154

132155
/// <summary>
@@ -400,7 +423,7 @@ private bool TrySendPacket(Func<INodePacket> packetResolver)
400423
try
401424
{
402425
packet = packetResolver();
403-
_pipeClient.WritePacket(packet);
426+
WritePacket(_nodeStream, packet);
404427
CommunicationsUtilities.Trace("Command packet of type '{0}' sent...", packet.Type);
405428
}
406429
catch (Exception ex)
@@ -598,7 +621,7 @@ private bool TryConnectToServer(int timeoutMilliseconds)
598621
tryAgain = false;
599622
try
600623
{
601-
_pipeClient.ConnectToServer(Math.Max(1, timeoutMilliseconds - (int)sw.ElapsedMilliseconds));
624+
NodeProviderOutOfProcBase.ConnectToPipeStream(_nodeStream, _pipeName, _handshake, Math.Max(1, timeoutMilliseconds - (int)sw.ElapsedMilliseconds));
602625
}
603626
catch (Exception ex)
604627
{
@@ -621,5 +644,30 @@ private bool TryConnectToServer(int timeoutMilliseconds)
621644

622645
return true;
623646
}
647+
648+
private void WritePacket(Stream nodeStream, INodePacket packet)
649+
{
650+
MemoryStream memoryStream = _packetMemoryStream;
651+
memoryStream.SetLength(0);
652+
653+
ITranslator writeTranslator = BinaryTranslator.GetWriteTranslator(memoryStream);
654+
655+
// Write header
656+
memoryStream.WriteByte((byte)packet.Type);
657+
658+
// Pad for packet length
659+
_binaryWriter.Write(0);
660+
661+
// Reset the position in the write buffer.
662+
packet.Translate(writeTranslator);
663+
664+
int packetStreamLength = (int)memoryStream.Position;
665+
666+
// Now write in the actual packet length
667+
memoryStream.Position = 1;
668+
_binaryWriter.Write(packetStreamLength - 5);
669+
670+
nodeStream.Write(memoryStream.GetBuffer(), 0, packetStreamLength);
671+
}
624672
}
625673
}

src/Build/BackEnd/Client/MSBuildClientPacketPump.cs

Lines changed: 107 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Buffers.Binary;
56
using System.Collections.Concurrent;
7+
using System.IO;
68
using System.Threading;
9+
10+
#if NET
711
using System.Threading.Tasks;
12+
#endif
13+
814
using Microsoft.Build.Internal;
915
using 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:

src/Build/BackEnd/Components/Communications/NodeManager.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,13 +240,19 @@ public void UnregisterPacketHandler(NodePacketType packetType)
240240
}
241241

242242
/// <summary>
243-
/// Takes a serializer and deserializes the packet.
243+
/// Takes a serializer, deserializes the packet and routes it to the appropriate handler.
244244
/// </summary>
245+
/// <param name="nodeId">The node from which the packet was received.</param>
245246
/// <param name="packetType">The packet type.</param>
246247
/// <param name="translator">The translator containing the data from which the packet should be reconstructed.</param>
247-
public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator)
248+
public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator)
248249
{
249-
return _packetFactory.DeserializePacket(packetType, translator);
250+
if (packetType == NodePacketType.NodeShutdown)
251+
{
252+
RemoveNodeFromMapping(nodeId);
253+
}
254+
255+
_packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator);
250256
}
251257

252258
/// <summary>

src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,10 @@ public void UnregisterPacketHandler(NodePacketType packetType)
286286
/// <summary>
287287
/// Deserializes and routes a packet. Not used in the in-proc node.
288288
/// </summary>
289-
public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator)
289+
public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator)
290290
{
291291
// Not used
292292
ErrorUtilities.ThrowInternalErrorUnreachable();
293-
return null;
294293
}
295294

296295
/// <summary>

0 commit comments

Comments
 (0)