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. }
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)); } }
/// <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(); }
private static extern bool GlobalMemoryStatusEx([In, Out] MemoryHelper buffer);