コード例 #1
0
        /// <summary>
        /// Gets the translator for this configuration.
        /// </summary>
        private INodePacketTranslator GetConfigurationTranslator(TranslationDirection direction)
        {
            string cacheFile = GetCacheFile();

            try
            {
                if (direction == TranslationDirection.WriteToStream)
                {
                    Directory.CreateDirectory(Path.GetDirectoryName(cacheFile));
                    return(NodePacketTranslator.GetWriteTranslator(File.Create(cacheFile)));
                }
                else
                {
                    // Not using sharedReadBuffer because this is not a memory stream and so the buffer won't be used anyway.
                    return(NodePacketTranslator.GetReadTranslator(File.OpenRead(cacheFile), null));
                }
            }
            catch (Exception e)
            {
                if (e is DirectoryNotFoundException || e is UnauthorizedAccessException)
                {
                    ErrorUtilities.ThrowInvalidOperation("CacheFileInaccessible", cacheFile, e);
                }

                // UNREACHABLE
                throw;
            }
        }
コード例 #2
0
            /// <summary>
            /// Sends the specified packet to this node.
            /// </summary>
            /// <param name="packet">The packet to send.</param>
            public void SendData(INodePacket packet)
            {
                MemoryStream          writeStream     = new MemoryStream();
                INodePacketTranslator writeTranslator = NodePacketTranslator.GetWriteTranslator(writeStream);

                try
                {
                    writeStream.WriteByte((byte)packet.Type);

                    // Pad for the packet length
                    writeStream.Write(BitConverter.GetBytes((int)0), 0, 4);
                    packet.Translate(writeTranslator);

                    // Now plug in the real packet length
                    writeStream.Position = 1;
                    writeStream.Write(BitConverter.GetBytes((int)writeStream.Length - 5), 0, 4);

                    byte[] writeStreamBuffer = writeStream.GetBuffer();

                    for (int i = 0; i < writeStream.Length; i += MaxPacketWriteSize)
                    {
                        int lengthToWrite = Math.Min((int)writeStream.Length - i, MaxPacketWriteSize);
                        if ((int)writeStream.Length - i <= MaxPacketWriteSize)
                        {
                            // We are done, write the last bit asynchronously.  This is actually the general case for
                            // most packets in the build, and the asynchronous behavior here is desirable.
#if FEATURE_APM
                            _serverToClientStream.BeginWrite(writeStreamBuffer, i, lengthToWrite, PacketWriteComplete, null);
#else
                            _serverToClientStream.WriteAsync(writeStreamBuffer, i, lengthToWrite);
#endif
                            return;
                        }
                        else
                        {
                            // If this packet is longer that we can write in one go, then we need to break it up.  We can't
                            // return out of this function and let the rest of the system continue because another operation
                            // might want to send data immediately afterward, and that could result in overlapping writes
                            // to the pipe on different threads.
#if FEATURE_APM
                            IAsyncResult result = _serverToClientStream.BeginWrite(writeStream.GetBuffer(), i, lengthToWrite, null, null);
                            _serverToClientStream.EndWrite(result);
#else
                            _serverToClientStream.Write(writeStreamBuffer, i, lengthToWrite);
#endif
                        }
                    }
                }
                catch (IOException e)
                {
                    // Do nothing here because any exception will be caught by the async read handler
                    CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in SendData: {0}", e);
                }
                catch (ObjectDisposedException) // This happens if a child dies unexpectedly
                {
                    // Do nothing here because any exception will be caught by the async read handler
                }
            }
コード例 #3
0
        /// <summary>
        /// Determine if the event is serializable. If we are running with multiple nodes we need to make sure the logging events are serializable. If not
        /// we need to log a warning.
        /// </summary>
        internal bool IsEventSerializable(BuildEventArgs e)
        {
#if FEATURE_BINARY_SERIALIZATION
            if (!e.GetType().GetTypeInfo().IsSerializable)
#else
            if (!NodePacketTranslator.IsSerializable(e))
#endif
            {
                _taskLoggingContext.LogWarning(null, new BuildEventFileInfo(string.Empty), "ExpectedEventToBeSerializable", e.GetType().Name);
                return(false);
            }

            return(true);
        }
コード例 #4
0
 private bool ReadAndRoutePacket(NodePacketType packetType, byte [] packetData, int packetLength)
 {
     try
     {
         // The buffer is publicly visible so that InterningBinaryReader doesn't have to copy to an intermediate buffer.
         // Since the buffer is publicly visible dispose right away to discourage outsiders from holding a reference to it.
         using (var packetStream = new MemoryStream(packetData, 0, packetLength, /*writeable*/ false, /*bufferIsPubliclyVisible*/ true))
         {
             INodePacketTranslator readTranslator = NodePacketTranslator.GetReadTranslator(packetStream, _sharedReadBuffer);
             _packetFactory.DeserializeAndRoutePacket(_nodeId, packetType, readTranslator);
         }
     }
     catch (IOException e)
     {
         CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in ReadAndRoutPacket: {0}", e);
         _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed));
         Close();
         return(false);
     }
     return(true);
 }
コード例 #5
0
        private void RunReadLoop(Stream localReadPipe, Stream localWritePipe,
                                 ConcurrentQueue <INodePacket> localPacketQueue, AutoResetEvent localPacketAvailable, AutoResetEvent localTerminatePacketPump)
        {
            // Ordering of the wait handles is important.  The first signalled wait handle in the array
            // will be returned by WaitAny if multiple wait handles are signalled.  We prefer to have the
            // terminate event triggered so that we cannot get into a situation where packets are being
            // spammed to the endpoint and it never gets an opportunity to shutdown.
            CommunicationsUtilities.Trace("Entering read loop.");
            byte[] headerByte = new byte[5];
#if FEATURE_APM
            IAsyncResult result = localReadPipe.BeginRead(headerByte, 0, headerByte.Length, null, null);
#else
            Task <int> readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length);
#endif

            bool exitLoop = false;
            do
            {
                // Ordering is important.  We want packetAvailable to supercede terminate otherwise we will not properly wait for all
                // packets to be sent by other threads which are shutting down, such as the logging thread.
                WaitHandle[] handles = new WaitHandle[] {
#if FEATURE_APM
                    result.AsyncWaitHandle,
#else
                    ((IAsyncResult)readTask).AsyncWaitHandle,
#endif
                    localPacketAvailable, localTerminatePacketPump
                };

                int waitId = WaitHandle.WaitAny(handles);
                switch (waitId)
                {
                case 0:
                {
                    int bytesRead = 0;
                    try
                    {
#if FEATURE_APM
                        bytesRead = localReadPipe.EndRead(result);
#else
                        bytesRead = readTask.Result;
#endif
                    }
                    catch (Exception e)
                    {
                        // Lost communications.  Abort (but allow node reuse)
                        CommunicationsUtilities.Trace("Exception reading from server.  {0}", e);
                        ExceptionHandling.DumpExceptionToFile(e);
                        ChangeLinkStatus(LinkStatus.Inactive);
                        exitLoop = true;
                        break;
                    }

                    if (bytesRead != headerByte.Length)
                    {
                        // Incomplete read.  Abort.
                        if (bytesRead == 0)
                        {
                            CommunicationsUtilities.Trace("Parent disconnected abruptly");
                        }
                        else
                        {
                            CommunicationsUtilities.Trace("Incomplete header read from server.  {0} of {1} bytes read", bytesRead, headerByte.Length);
                        }

                        ChangeLinkStatus(LinkStatus.Failed);
                        exitLoop = true;
                        break;
                    }

                    NodePacketType packetType   = (NodePacketType)Enum.ToObject(typeof(NodePacketType), headerByte[0]);
                    int            packetLength = BitConverter.ToInt32(headerByte, 1);

                    try
                    {
                        _packetFactory.DeserializeAndRoutePacket(0, packetType, NodePacketTranslator.GetReadTranslator(localReadPipe, _sharedReadBuffer));
                    }
                    catch (Exception e)
                    {
                        // Error while deserializing or handling packet.  Abort.
                        CommunicationsUtilities.Trace("Exception while deserializing packet {0}: {1}", packetType, e);
                        ExceptionHandling.DumpExceptionToFile(e);
                        ChangeLinkStatus(LinkStatus.Failed);
                        exitLoop = true;
                        break;
                    }

#if FEATURE_APM
                    result = localReadPipe.BeginRead(headerByte, 0, headerByte.Length, null, null);
#else
                    readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length);
#endif
                }

                break;

                case 1:
                case 2:
                    try
                    {
                        // Write out all the queued packets.
                        INodePacket packet;
                        while (localPacketQueue.TryDequeue(out packet))
                        {
                            MemoryStream          packetStream    = new MemoryStream();
                            INodePacketTranslator writeTranslator = NodePacketTranslator.GetWriteTranslator(packetStream);

                            packetStream.WriteByte((byte)packet.Type);

                            // Pad for packet length
                            packetStream.Write(BitConverter.GetBytes((int)0), 0, 4);

                            // Reset the position in the write buffer.
                            packet.Translate(writeTranslator);

                            // Now write in the actual packet length
                            packetStream.Position = 1;
                            packetStream.Write(BitConverter.GetBytes((int)packetStream.Length - 5), 0, 4);

                            localWritePipe.Write(packetStream.GetBuffer(), 0, (int)packetStream.Length);
                        }
                    }
                    catch (Exception e)
                    {
                        // Error while deserializing or handling packet.  Abort.
                        CommunicationsUtilities.Trace("Exception while serializing packets: {0}", e);
                        ExceptionHandling.DumpExceptionToFile(e);
                        ChangeLinkStatus(LinkStatus.Failed);
                        exitLoop = true;
                        break;
                    }

                    if (waitId == 2)
                    {
                        CommunicationsUtilities.Trace("Disconnecting voluntarily");
                        ChangeLinkStatus(LinkStatus.Failed);
                        exitLoop = true;
                    }

                    break;

                default:
                    ErrorUtilities.ThrowInternalError("waitId {0} out of range.", waitId);
                    break;
                }
            }while (!exitLoop);
        }
コード例 #6
0
        /// <summary>
        /// This method handles the asynchronous message pump.  It waits for messages to show up on the queue
        /// and calls FireDataAvailable for each such packet.  It will terminate when the terminate event is
        /// set.
        /// </summary>
        private void PacketPumpProc()
        {
            NamedPipeServerStream localPipeServer          = _pipeServer;
            AutoResetEvent        localPacketAvailable     = _packetAvailable;
            AutoResetEvent        localTerminatePacketPump = _terminatePacketPump;
            Queue <INodePacket>   localPacketQueue         = _packetQueue;

            DateTime originalWaitStartTime = DateTime.UtcNow;
            bool     gotValidConnection    = false;

            while (!gotValidConnection)
            {
                DateTime restartWaitTime = DateTime.UtcNow;

                // We only wait to wait the difference between now and the last original start time, in case we have multiple hosts attempting
                // to attach.  This prevents each attempt from resetting the timer.
                TimeSpan usedWaitTime      = restartWaitTime - originalWaitStartTime;
                int      waitTimeRemaining = Math.Max(0, CommunicationsUtilities.NodeConnectionTimeout - (int)usedWaitTime.TotalMilliseconds);

                try
                {
                    // Wait for a connection
                    IAsyncResult resultForConnection = localPipeServer.BeginWaitForConnection(null, null);
                    CommunicationsUtilities.Trace("Waiting for connection {0} ms...", waitTimeRemaining);

                    bool connected = resultForConnection.AsyncWaitHandle.WaitOne(waitTimeRemaining, false);
                    if (!connected)
                    {
                        CommunicationsUtilities.Trace("Connection timed out waiting a host to contact us.  Exiting comm thread.");
                        ChangeLinkStatus(LinkStatus.ConnectionFailed);
                        return;
                    }

                    CommunicationsUtilities.Trace("Parent started connecting. Reading handshake from parent");
                    localPipeServer.EndWaitForConnection(resultForConnection);

                    // The handshake protocol is a simple long exchange.  The host sends us a long, and we
                    // respond with another long.  Once the handshake is complete, both sides can be assured the
                    // other is ready to accept data.
                    // To avoid mixing client and server builds, the long is the MSBuild binary timestamp.

                    // Compatibility issue here.
                    // Previous builds of MSBuild 4.0 would exchange just a byte.
                    // Host would send either 0x5F or 0x60 depending on whether it was the toolset or not respectively.
                    // Client would return either 0xF5 or 0x06 respectively.
                    // Therefore an old host on a machine with new clients running will hang,
                    // sending a byte and waiting for a byte until it eventually times out;
                    // because the new client will want 7 more bytes before it returns anything.
                    // The other way around is not a problem, because the old client would immediately return the (wrong)
                    // byte on receiving the first byte of the long sent by the new host, and the new host would disconnect.
                    // To avoid the hang, special case here:
                    // Make sure our handshakes always start with 00.
                    // If we received ONLY one byte AND it's 0x5F or 0x60, return 0xFF (it doesn't matter what as long as
                    // it will cause the host to reject us; new hosts expect 00 and old hosts expect F5 or 06).
                    try
                    {
                        long            handshake       = localPipeServer.ReadLongForHandshake(/* reject these leads */ new byte[] { 0x5F, 0x60 }, 0xFF /* this will disconnect the host; it expects leading 00 or F5 or 06 */);
                        WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent();
                        string          remoteUserName  = localPipeServer.GetImpersonationUserName();

                        if (handshake != GetHostHandshake())
                        {
                            CommunicationsUtilities.Trace("Handshake failed. Received {0} from host not {1}. Probably the host is a different MSBuild build.", handshake, GetHostHandshake());
                            localPipeServer.Disconnect();
                            continue;
                        }

                        // We will only talk to a host that was started by the same user as us.  Even though the pipe access is set to only allow this user, we want to ensure they
                        // haven't attempted to change those permissions out from under us.  This ensures that the only way they can truly gain access is to be impersonating the
                        // user we were started by.
                        WindowsIdentity clientIdentity = null;
                        localPipeServer.RunAsClient(delegate() { clientIdentity = WindowsIdentity.GetCurrent(true); });

                        if (clientIdentity == null || !String.Equals(clientIdentity.Name, currentIdentity.Name, StringComparison.OrdinalIgnoreCase))
                        {
                            CommunicationsUtilities.Trace("Handshake failed. Host user is {0} but we were created by {1}.", (clientIdentity == null) ? "<unknown>" : clientIdentity.Name, currentIdentity.Name);
                            localPipeServer.Disconnect();
                            continue;
                        }
                    }
                    catch (IOException e)
                    {
                        // We will get here when:
                        // 1. The host (OOP main node) connects to us, it immediately checks for user priviledges
                        //    and if they don't match it disconnects immediately leaving us still trying to read the blank handshake
                        // 2. The host is too old sending us bits we automatically reject in the handshake
                        CommunicationsUtilities.Trace("Client connection failed but we will wait for another connection. Exception: {0}", e.Message);
                        if (localPipeServer.IsConnected)
                        {
                            localPipeServer.Disconnect();
                        }

                        continue;
                    }

                    gotValidConnection = true;
                }
                catch (Exception e)
                {
                    if (ExceptionHandling.IsCriticalException(e))
                    {
                        throw;
                    }

                    CommunicationsUtilities.Trace("Client connection failed.  Exiting comm thread. {0}", e);
                    if (localPipeServer.IsConnected)
                    {
                        localPipeServer.Disconnect();
                    }

                    ExceptionHandling.DumpExceptionToFile(e);
                    ChangeLinkStatus(LinkStatus.Failed);
                    return;
                }
            }

            CommunicationsUtilities.Trace("Writing handshake to parent");
            localPipeServer.WriteLongForHandshake(GetClientHandshake());
            ChangeLinkStatus(LinkStatus.Active);

            // Ordering of the wait handles is important.  The first signalled wait handle in the array
            // will be returned by WaitAny if multiple wait handles are signalled.  We prefer to have the
            // terminate event triggered so that we cannot get into a situation where packets are being
            // spammed to the endpoint and it never gets an opportunity to shutdown.
            CommunicationsUtilities.Trace("Entering read loop.");
            byte[]       headerByte = new byte[5];
            IAsyncResult result     = localPipeServer.BeginRead(headerByte, 0, headerByte.Length, null, null);

            bool exitLoop = false;

            do
            {
                // Ordering is important.  We want packetAvailable to supercede terminate otherwise we will not properly wait for all
                // packets to be sent by other threads which are shutting down, such as the logging thread.
                WaitHandle[] handles = new WaitHandle[] { result.AsyncWaitHandle, localPacketAvailable, localTerminatePacketPump };

                int waitId = WaitHandle.WaitAny(handles);
                switch (waitId)
                {
                case 0:
                {
                    int bytesRead = 0;
                    try
                    {
                        bytesRead = localPipeServer.EndRead(result);
                    }
                    catch (Exception e)
                    {
                        // Lost communications.  Abort (but allow node reuse)
                        CommunicationsUtilities.Trace("Exception reading from server.  {0}", e);
                        ExceptionHandling.DumpExceptionToFile(e);
                        ChangeLinkStatus(LinkStatus.Inactive);
                        exitLoop = true;
                        break;
                    }

                    if (bytesRead != headerByte.Length)
                    {
                        // Incomplete read.  Abort.
                        if (bytesRead == 0)
                        {
                            CommunicationsUtilities.Trace("Parent disconnected abruptly");
                        }
                        else
                        {
                            CommunicationsUtilities.Trace("Incomplete header read from server.  {0} of {1} bytes read", bytesRead, headerByte.Length);
                        }

                        ChangeLinkStatus(LinkStatus.Failed);
                        exitLoop = true;
                        break;
                    }

                    NodePacketType packetType   = (NodePacketType)Enum.ToObject(typeof(NodePacketType), headerByte[0]);
                    int            packetLength = BitConverter.ToInt32(headerByte, 1);

                    try
                    {
                        _packetFactory.DeserializeAndRoutePacket(0, packetType, NodePacketTranslator.GetReadTranslator(localPipeServer, _sharedReadBuffer));
                    }
                    catch (Exception e)
                    {
                        // Error while deserializing or handling packet.  Abort.
                        CommunicationsUtilities.Trace("Exception while deserializing packet {0}: {1}", packetType, e);
                        ExceptionHandling.DumpExceptionToFile(e);
                        ChangeLinkStatus(LinkStatus.Failed);
                        exitLoop = true;
                        break;
                    }

                    result = localPipeServer.BeginRead(headerByte, 0, headerByte.Length, null, null);
                }

                break;

                case 1:
                case 2:
                    try
                    {
                        int packetCount = localPacketQueue.Count;

                        // Write out all the queued packets.
                        while (packetCount > 0)
                        {
                            INodePacket packet;
                            lock (_packetQueue)
                            {
                                packet = localPacketQueue.Dequeue();
                            }

                            MemoryStream          packetStream    = new MemoryStream();
                            INodePacketTranslator writeTranslator = NodePacketTranslator.GetWriteTranslator(packetStream);

                            packetStream.WriteByte((byte)packet.Type);

                            // Pad for packet length
                            packetStream.Write(BitConverter.GetBytes((int)0), 0, 4);

                            // Reset the position in the write buffer.
                            packet.Translate(writeTranslator);

                            // Now write in the actual packet length
                            packetStream.Position = 1;
                            packetStream.Write(BitConverter.GetBytes((int)packetStream.Length - 5), 0, 4);

                            localPipeServer.Write(packetStream.GetBuffer(), 0, (int)packetStream.Length);

                            packetCount--;
                        }
                    }
                    catch (Exception e)
                    {
                        // Error while deserializing or handling packet.  Abort.
                        CommunicationsUtilities.Trace("Exception while serializing packets: {0}", e);
                        ExceptionHandling.DumpExceptionToFile(e);
                        ChangeLinkStatus(LinkStatus.Failed);
                        exitLoop = true;
                        break;
                    }

                    if (waitId == 2)
                    {
                        CommunicationsUtilities.Trace("Disconnecting voluntarily");
                        ChangeLinkStatus(LinkStatus.Failed);
                        exitLoop = true;
                    }

                    break;

                default:
                    ErrorUtilities.ThrowInternalError("waitId {0} out of range.", waitId);
                    break;
                }
            }while (!exitLoop);

            CommunicationsUtilities.Trace("Ending read loop");

            try
            {
                if (localPipeServer.IsConnected)
                {
                    localPipeServer.WaitForPipeDrain();
                    localPipeServer.Disconnect();
                }
            }
            catch (Exception)
            {
                // We don't really care if Disconnect somehow fails, but it gives us a chance to do the right thing.
            }
        }
コード例 #7
0
            /// <summary>
            /// Method called when the body of a packet has been read.
            /// </summary>
            private void BodyReadComplete(IAsyncResult result)
            {
                NodePacketType packetType = (NodePacketType)_headerByte[0];
                var            state      = (Tuple <byte[], int>)result.AsyncState;

                byte[] packetData   = state.Item1;
                int    packetLength = state.Item2;
                int    bytesRead;

                try
                {
                    try
                    {
                        bytesRead = _nodePipe.EndRead(result);
                    }

                    // Workaround for CLR stress bug; it sporadically calls us twice on the same async
                    // result, and EndRead will throw on the second one. Pretend the second one never happened.
                    catch (ArgumentException)
                    {
                        CommunicationsUtilities.Trace(_nodeId, "Hit CLR bug #825607: called back twice on same async result; ignoring");
                        return;
                    }

                    if (bytesRead != packetLength)
                    {
                        CommunicationsUtilities.Trace(_nodeId, "Bad packet read for packet {0} - Expected {1} bytes, got {2}", packetType, packetLength, bytesRead);
                        _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed));
                        Close();
                        return;
                    }
                }
                catch (IOException e)
                {
                    CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in BodyReadComplete (Reading): {0}", e);
                    _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed));
                    Close();
                    return;
                }

                // Read and route the packet.
                try
                {
                    // The buffer is publicly visible so that InterningBinaryReader doesn't have to copy to an intermediate buffer.
                    // Since the buffer is publicly visible dispose right away to discourage outsiders from holding a reference to it.
                    using (var packetStream = new MemoryStream(packetData, 0, packetLength, /*writeable*/ false, /*bufferIsPubliclyVisible*/ true))
                    {
                        INodePacketTranslator readTranslator = NodePacketTranslator.GetReadTranslator(packetStream, _sharedReadBuffer);
                        _packetFactory.DeserializeAndRoutePacket(_nodeId, packetType, readTranslator);
                    }
                }
                catch (IOException e)
                {
                    CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in BodyReadComplete (Routing): {0}", e);
                    _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed));
                    Close();
                    return;
                }

                if (packetType != NodePacketType.NodeShutdown)
                {
                    // Read the next packet.
                    BeginAsyncPacketRead();
                }
                else
                {
                    Close();
                }
            }