public void ReadWriteRequest() { Task.Run(async () => { var request = new BuildRequest( BuildProtocolConstants.ProtocolVersion, RequestLanguage.VisualBasicCompile, ImmutableArray.Create( new BuildRequest.Argument(BuildProtocolConstants.ArgumentId.CurrentDirectory, argumentIndex: 0, value: "directory"), new BuildRequest.Argument(BuildProtocolConstants.ArgumentId.CommandLineArgument, argumentIndex: 1, value: "file"))); var memoryStream = new MemoryStream(); await request.WriteAsync(memoryStream, default(CancellationToken)).ConfigureAwait(false); Assert.True(memoryStream.Position > 0); memoryStream.Position = 0; var read = await BuildRequest.ReadAsync(memoryStream, default(CancellationToken)).ConfigureAwait(false); Assert.Equal(BuildProtocolConstants.ProtocolVersion, read.ProtocolVersion); Assert.Equal(RequestLanguage.VisualBasicCompile, read.Language); Assert.Equal(2, read.Arguments.Length); Assert.Equal(BuildProtocolConstants.ArgumentId.CurrentDirectory, read.Arguments[0].ArgumentId); Assert.Equal(0, read.Arguments[0].ArgumentIndex); Assert.Equal("directory", read.Arguments[0].Value); Assert.Equal(BuildProtocolConstants.ArgumentId.CommandLineArgument, read.Arguments[1].ArgumentId); Assert.Equal(1, read.Arguments[1].ArgumentIndex); Assert.Equal("file", read.Arguments[1].Value); }).Wait(); }
protected override Task<BuildResponse> ServeBuildRequest(BuildRequest request, CancellationToken cancellationToken) { if (ServeBuildRequestFunc != null) { return ServeBuildRequestFunc(request, cancellationToken); } return base.ServeBuildRequest(request, cancellationToken); }
private void VerifyShutdownRequest(BuildRequest request) { Assert.Equal(1, request.Arguments.Length); var argument = request.Arguments[0]; Assert.Equal(BuildProtocolConstants.ArgumentId.Shutdown, argument.ArgumentId); Assert.Equal(0, argument.ArgumentIndex); Assert.Equal("", argument.Value); }
internal static async Task <BuildResponse> RunServerCompilationCore( RequestLanguage language, List <string> arguments, BuildPathsAlt buildPaths, string pipeName, string keepAlive, string libEnvVariable, int?timeoutOverride, Func <string, string, bool> tryCreateServerFunc, CancellationToken cancellationToken) { if (pipeName == null) { return(new RejectedBuildResponse()); } if (buildPaths.TempDirectory == null) { return(new RejectedBuildResponse()); } var clientDir = buildPaths.ClientDirectory; var timeoutNewProcess = timeoutOverride ?? TimeOutMsNewProcess; var timeoutExistingProcess = timeoutOverride ?? TimeOutMsExistingProcess; Task <NamedPipeClientStream> pipeTask = null; Mutex clientMutex = null; var holdsMutex = false; try { try { var clientMutexName = GetClientMutexName(pipeName); clientMutex = new Mutex(initiallyOwned: true, name: clientMutexName, out holdsMutex); } catch { // The Mutex constructor can throw in certain cases. One specific example is docker containers // where the /tmp directory is restricted. In those cases there is no reliable way to execute // the server and we need to fall back to the command line. // // Example: https://github.com/dotnet/roslyn/issues/24124 return(new RejectedBuildResponse()); } if (!holdsMutex) { try { holdsMutex = clientMutex.WaitOne(timeoutNewProcess); if (!holdsMutex) { return(new RejectedBuildResponse()); } } catch (AbandonedMutexException) { holdsMutex = true; } } // Check for an already running server var serverMutexName = GetServerMutexName(pipeName); bool wasServerRunning = WasServerMutexOpen(serverMutexName); var timeout = wasServerRunning ? timeoutExistingProcess : timeoutNewProcess; if (wasServerRunning || tryCreateServerFunc(clientDir, pipeName)) { pipeTask = TryConnectToServerAsync(pipeName, timeout, cancellationToken); } } finally { if (clientMutex != null) { if (holdsMutex) { clientMutex.ReleaseMutex(); } clientMutex.Dispose(); } } if (pipeTask != null) { var pipe = await pipeTask.ConfigureAwait(false); if (pipe != null) { var request = BuildRequest.Create(language, buildPaths.WorkingDirectory, buildPaths.TempDirectory, arguments, keepAlive, libEnvVariable); return(await TryCompile(pipe, request, cancellationToken).ConfigureAwait(false)); } } return(new RejectedBuildResponse()); }
internal static Task <BuildResponse> RunServerCompilationCore( RequestLanguage language, List <string> arguments, BuildPathsAlt buildPaths, string pipeName, string keepAlive, string libEnvVariable, int?timeoutOverride, Func <string, string, bool> tryCreateServerFunc, CancellationToken cancellationToken) { if (pipeName == null) { return(Task.FromResult <BuildResponse>(new RejectedBuildResponse())); } if (buildPaths.TempDirectory == null) { return(Task.FromResult <BuildResponse>(new RejectedBuildResponse())); } var clientDir = buildPaths.ClientDirectory; var timeoutNewProcess = timeoutOverride ?? TimeOutMsNewProcess; var timeoutExistingProcess = timeoutOverride ?? TimeOutMsExistingProcess; var clientMutexName = GetClientMutexName(pipeName); bool holdsMutex; using (var clientMutex = new Mutex(initiallyOwned: true, name: clientMutexName, createdNew: out holdsMutex)) { try { if (!holdsMutex) { try { holdsMutex = clientMutex.WaitOne(timeoutNewProcess); if (!holdsMutex) { return(Task.FromResult <BuildResponse>(new RejectedBuildResponse())); } } catch (AbandonedMutexException) { holdsMutex = true; } } // Check for an already running server var serverMutexName = GetServerMutexName(pipeName); bool wasServerRunning = WasServerMutexOpen(serverMutexName); var timeout = wasServerRunning ? timeoutExistingProcess : timeoutNewProcess; NamedPipeClientStream pipe = null; if (wasServerRunning || tryCreateServerFunc(clientDir, pipeName)) { pipe = TryConnectToServer(pipeName, timeout, cancellationToken); } if (pipe != null) { var request = BuildRequest.Create(language, buildPaths.WorkingDirectory, buildPaths.TempDirectory, arguments, keepAlive, libEnvVariable); return(TryCompile(pipe, request, cancellationToken)); } } finally { if (holdsMutex) { clientMutex.ReleaseMutex(); } } } return(Task.FromResult <BuildResponse>(new RejectedBuildResponse())); }
/// <summary> /// Try to compile using the server. Returns a null-containing Task if a response /// from the server cannot be retrieved. /// </summary> private static async Task<BuildResponse> TryCompile(NamedPipeClientStream pipeStream, BuildRequest request, CancellationToken cancellationToken) { BuildResponse response; using (pipeStream) { // Write the request try { Log("Begin writing request"); await request.WriteAsync(pipeStream, cancellationToken).ConfigureAwait(false); Log("End writing request"); } catch (Exception e) { LogException(e, "Error writing build request."); return new RejectedBuildResponse(); } // Wait for the compilation and a monitor to detect if the server disconnects var serverCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); Log("Begin reading response"); var responseTask = BuildResponse.ReadAsync(pipeStream, serverCts.Token); var monitorTask = CreateMonitorDisconnectTask(pipeStream, serverCts.Token); await Task.WhenAny(responseTask, monitorTask).ConfigureAwait(false); Log("End reading response"); if (responseTask.IsCompleted) { // await the task to log any exceptions try { response = await responseTask.ConfigureAwait(false); } catch (Exception e) { LogException(e, "Error reading response"); response = new RejectedBuildResponse(); } } else { Log("Server disconnect"); response = new RejectedBuildResponse(); } // Cancel whatever task is still around serverCts.Cancel(); Debug.Assert(response != null); return response; } }
internal static async Task <BuildResponse> RunServerCompilationCore( RequestLanguage language, List <string> arguments, BuildPathsAlt buildPaths, string pipeName, string keepAlive, string libEnvVariable, int?timeoutOverride, CreateServerFunc createServerFunc, CancellationToken cancellationToken) { if (pipeName == null) { return(new RejectedBuildResponse()); } if (buildPaths.TempDirectory == null) { return(new RejectedBuildResponse()); } // early check for the build hash. If we can't find it something is wrong; no point even trying to go to the server if (string.IsNullOrWhiteSpace(BuildProtocolConstants.GetCommitHash())) { return(new IncorrectHashBuildResponse()); } var clientDir = buildPaths.ClientDirectory; var timeoutNewProcess = timeoutOverride ?? TimeOutMsNewProcess; var timeoutExistingProcess = timeoutOverride ?? TimeOutMsExistingProcess; Task <NamedPipeClientStream> pipeTask = null; IServerMutex clientMutex = null; try { var holdsMutex = false; try { var clientMutexName = GetClientMutexName(pipeName); clientMutex = OpenOrCreateMutex(clientMutexName, out holdsMutex); } catch { // The Mutex constructor can throw in certain cases. One specific example is docker containers // where the /tmp directory is restricted. In those cases there is no reliable way to execute // the server and we need to fall back to the command line. // // Example: https://github.com/dotnet/roslyn/issues/24124 return(new RejectedBuildResponse()); } if (!holdsMutex) { try { holdsMutex = clientMutex.TryLock(timeoutNewProcess); if (!holdsMutex) { return(new RejectedBuildResponse()); } } catch (AbandonedMutexException) { holdsMutex = true; } } // Check for an already running server var serverMutexName = GetServerMutexName(pipeName); bool wasServerRunning = WasServerMutexOpen(serverMutexName); var timeout = wasServerRunning ? timeoutExistingProcess : timeoutNewProcess; if (wasServerRunning || createServerFunc(clientDir, pipeName)) { pipeTask = TryConnectToServerAsync(pipeName, timeout, cancellationToken); } } finally { clientMutex?.Dispose(); } if (pipeTask != null) { var pipe = await pipeTask.ConfigureAwait(false); if (pipe != null) { var request = BuildRequest.Create(language, buildPaths.WorkingDirectory, buildPaths.TempDirectory, BuildProtocolConstants.GetCommitHash(), arguments, keepAlive, libEnvVariable); return(await TryCompile(pipe, request, cancellationToken).ConfigureAwait(false)); } } return(new RejectedBuildResponse()); }
protected override void ValidateBuildRequest(BuildRequest request) { ValidateBuildRequestFunc?.Invoke(request); }
internal static async Task<BuildResponse> Send(string pipeName, BuildRequest request) { using (var client = new NamedPipeClientStream(pipeName)) { await client.ConnectAsync(); await request.WriteAsync(client); return await BuildResponse.ReadAsync(client); } }
internal static async Task <BuildResponse> RunServerCompilationCoreAsync( Guid requestId, RequestLanguage language, List <string> arguments, BuildPathsAlt buildPaths, string?pipeName, string?keepAlive, string?libDirectory, int?timeoutOverride, CreateServerFunc createServerFunc, ICompilerServerLogger logger, CancellationToken cancellationToken ) { if (pipeName is null) { throw new ArgumentException(nameof(pipeName)); } if (buildPaths.TempDirectory == null) { throw new ArgumentException(nameof(buildPaths)); } // early check for the build hash. If we can't find it something is wrong; no point even trying to go to the server if (string.IsNullOrWhiteSpace(BuildProtocolConstants.GetCommitHash())) { return(new IncorrectHashBuildResponse()); } var pipeTask = tryConnectToServer( pipeName, buildPaths, timeoutOverride, createServerFunc, logger, cancellationToken ); if (pipeTask is null) { return(new RejectedBuildResponse("Failed to connect to server")); } else { using var pipe = await pipeTask.ConfigureAwait(false); if (pipe is null) { return(new RejectedBuildResponse("Failed to connect to server")); } else { var request = BuildRequest.Create( language, arguments, workingDirectory: buildPaths.WorkingDirectory, tempDirectory: buildPaths.TempDirectory, compilerHash: BuildProtocolConstants.GetCommitHash() ?? "", requestId: requestId, keepAlive: keepAlive, libDirectory: libDirectory ); return(await TryCompileAsync(pipe, request, logger, cancellationToken) .ConfigureAwait(false)); } }