File: AssemblyDependency\Node\OutOfProcRarNodeEndpoint.cs
Web Access
Project: ..\..\..\src\Tasks\Microsoft.Build.Tasks.csproj (Microsoft.Build.Tasks.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Build.BackEnd;
using Microsoft.Build.Internal;
 
namespace Microsoft.Build.Tasks.AssemblyDependency
{
    /// <summary>
    /// Implements a single instance of a pipe server which executes the ResolveAssemblyReference task.
    /// </summary>
    internal sealed class OutOfProcRarNodeEndpoint : IDisposable
    {
        private readonly int _endpointId;
 
        private readonly NodePipeServer _pipeServer;
 
        internal OutOfProcRarNodeEndpoint(
            int endpointId,
            string pipeName,
            ServerNodeHandshake handshake,
            int maxNumberOfServerInstances,
            NodePacketFactory packetFactory)
        {
            _endpointId = endpointId;
            _pipeServer = new NodePipeServer(pipeName, handshake, maxNumberOfServerInstances);
            _pipeServer.RegisterPacketFactory(packetFactory);
        }
 
        public void Dispose() => _pipeServer.Dispose();
 
        internal async Task RunAsync(CancellationToken cancellationToken = default)
        {
            CommunicationsUtilities.Trace("({0}) Starting RAR endpoint.", _endpointId);
 
            try
            {
                await RunInternalAsync(cancellationToken);
            }
            catch (OperationCanceledException)
            {
                // Swallow cancellation excpetions for now. We're using this as a simple way to gracefully shutdown the
                // endpoint, instead of having to implement separate Start / Stop methods and deferring to the caller.
                // Can reevaluate if we need more granular control over cancellation vs shutdown.
                CommunicationsUtilities.Trace("({0}) RAR endpoint stopped due to cancellation.", _endpointId);
            }
        }
 
        private async Task RunInternalAsync(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                while (!_pipeServer.IsConnected)
                {
                    _ = _pipeServer.WaitForConnection();
                }
 
                CommunicationsUtilities.Trace("({0}) Received RAR request.", _endpointId);
 
                try
                {
                    INodePacket packet = await _pipeServer.ReadPacketAsync(cancellationToken);
 
                    if (packet.Type == NodePacketType.NodeShutdown)
                    {
                        // Although the client has already disconnected, it is still necessary to Diconnect() so the
                        // pipe can transition into PipeState.Disonnected, which is treated as an intentional pipe break.
                        // Otherwise, all future operations on the pipe will throw an exception.
                        CommunicationsUtilities.Trace("({0}) RAR client disconnected.", _endpointId);
                        _pipeServer.Disconnect();
                        continue;
                    }
 
                    RarNodeExecuteRequest request = (RarNodeExecuteRequest)packet;
 
                    // TODO: Use request packet to set inputs on the RAR task.
                    ResolveAssemblyReference rarTask = new();
 
                    // TODO: bool success = rarTask.ExecuteInProcess();
                    // TODO: Use RAR task outputs to create response packet.
                    await _pipeServer.WritePacketAsync(new RarNodeExecuteResponse(), cancellationToken);
 
                    CommunicationsUtilities.Trace("({0}) Completed RAR request.", _endpointId);
                }
                catch (Exception e) when (e is not OperationCanceledException)
                {
                    CommunicationsUtilities.Trace("({0}) Exception while executing RAR request: {1}", _endpointId, e);
                }
            }
 
            _pipeServer.Disconnect();
        }
    }
}