public ulong Reserved; //always 0

            public static bool IsMemoryAvailable()
            {
                MemoryHelper status = new MemoryHelper();
                GlobalMemoryStatusEx(status);
                ulong max = status.MaxVirtual;
                ulong free = status.AvailableVirtual;

                int shift = 20;
                string unit = "MB";
                if (free >> shift == 0)
                {
                    shift = 10;
                    unit = "KB";
                }

                CompilerServerLogger.Log("Free memory: {1}{0} of {2}{0}.", unit, free >> shift, max >> shift);

                return free >= 800 << 20; // Value (500MB) is arbitrary; feel free to improve.
            }
Esempio n. 2
0
        internal async Task <CompletionData> ProcessAsync(
            Task <IClientConnection> clientConnectionTask,
            bool allowCompilationRequests       = true,
            CancellationToken cancellationToken = default)
        {
            try
            {
                return(await ProcessCore().ConfigureAwait(false));
            }
            catch (Exception ex)
            {
                CompilerServerLogger.LogException(ex, $"Error processing request for client");
                return(CompletionData.RequestError);
            }

            async Task <CompletionData> ProcessCore()
            {
                using var clientConnection = await clientConnectionTask.ConfigureAwait(false);

                var request = await clientConnection.ReadBuildRequestAsync(cancellationToken).ConfigureAwait(false);

                if (request.ProtocolVersion != BuildProtocolConstants.ProtocolVersion)
                {
                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               new MismatchedVersionBuildResponse(),
                               CompletionData.RequestError,
                               cancellationToken).ConfigureAwait(false));
                }

                if (!string.Equals(request.CompilerHash, BuildProtocolConstants.GetCommitHash(), StringComparison.OrdinalIgnoreCase))
                {
                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               new IncorrectHashBuildResponse(),
                               CompletionData.RequestError,
                               cancellationToken).ConfigureAwait(false));
                }

                if (request.Arguments.Count == 1 && request.Arguments[0].ArgumentId == BuildProtocolConstants.ArgumentId.Shutdown)
                {
                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               new ShutdownBuildResponse(Process.GetCurrentProcess().Id),
                               new CompletionData(CompletionReason.RequestCompleted, shutdownRequested : true),
                               cancellationToken).ConfigureAwait(false));
                }

                if (!allowCompilationRequests)
                {
                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               new RejectedBuildResponse("Compilation not allowed at this time"),
                               CompletionData.RequestCompleted,
                               cancellationToken).ConfigureAwait(false));
                }

                if (!Environment.Is64BitProcess && !MemoryHelper.IsMemoryAvailable())
                {
                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               new RejectedBuildResponse("Not enough resources to accept connection"),
                               CompletionData.RequestError,
                               cancellationToken).ConfigureAwait(false));
                }

                return(await ProcessCompilationRequestAsync(clientConnection, request, cancellationToken).ConfigureAwait(false));
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Creates a Task that waits for a client connection to occur and returns the connected
        /// <see cref="NamedPipeServerStream"/> object.  Throws on any connection error.
        /// </summary>
        /// <param name="pipeName">Name of the pipe on which the instance will listen for requests.</param>
        /// <param name="cancellationToken">Used to cancel the connection sequence.</param>
        private async Task <NamedPipeServerStream> CreateListenTask(string pipeName, CancellationToken cancellationToken)
        {
            // Create the pipe and begin waiting for a connection. This
            // doesn't block, but could fail in certain circumstances, such
            // as Windows refusing to create the pipe for some reason
            // (out of handles?), or the pipe was disconnected before we
            // starting listening.
            NamedPipeServerStream pipeStream = ConstructPipe(pipeName);

            // Unfortunately the version of .Net we are using doesn't support the WaitForConnectionAsync
            // method.  When it is available it should absolutely be used here.  In the meantime we
            // have to deal with the idea that this WaitForConnection call will block a thread
            // for a significant period of time.  It is unadvisable to do this to a thread pool thread
            // hence we will use an explicit thread here.
            var listenSource = new TaskCompletionSource <NamedPipeServerStream>();
            var listenTask   = listenSource.Task;
            var listenThread = new Thread(() =>
            {
                try
                {
                    CompilerServerLogger.Log("Waiting for new connection");
                    pipeStream.WaitForConnection();
                    CompilerServerLogger.Log("Pipe connection detected.");

                    if (Environment.Is64BitProcess || MemoryHelper.IsMemoryAvailable())
                    {
                        CompilerServerLogger.Log("Memory available - accepting connection");
                        listenSource.SetResult(pipeStream);
                        return;
                    }

                    try
                    {
                        pipeStream.Close();
                    }
                    catch
                    {
                        // Okay for Close failure here.
                    }

                    listenSource.SetException(new Exception("Insufficient resources to process new connection."));
                }
                catch (Exception ex)
                {
                    listenSource.SetException(ex);
                }
            });

            listenThread.Start();

            // Create a tasks that waits indefinitely (-1) and completes only when cancelled.
            var waitCancellationTokenSource = new CancellationTokenSource();
            var waitTask = Task.Delay(
                Timeout.Infinite,
                CancellationTokenSource.CreateLinkedTokenSource(waitCancellationTokenSource.Token, cancellationToken).Token);
            await Task.WhenAny(listenTask, waitTask).ConfigureAwait(false);

            if (listenTask.IsCompleted)
            {
                waitCancellationTokenSource.Cancel();
                return(await listenTask.ConfigureAwait(false));
            }

            // The listen operation was cancelled.  Close the pipe stream throw a cancellation exception to
            // simulate the cancel operation.
            waitCancellationTokenSource.Cancel();
            try
            {
                pipeStream.Close();
            }
            catch
            {
                // Okay for Close failure here.
            }

            throw new OperationCanceledException();
        }
Esempio n. 4
0
 private static extern bool GlobalMemoryStatusEx([In, Out] MemoryHelper buffer);