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; var clientMutexName = GetClientMutexName(pipeName); Task <NamedPipeClientStream> pipeTask = null; using (var clientMutex = new Mutex(initiallyOwned: true, name: clientMutexName, createdNew: out var holdsMutex)) { try { 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 = true; var timeout = wasServerRunning ? timeoutExistingProcess : timeoutNewProcess; if (wasServerRunning || tryCreateServerFunc(clientDir, pipeName)) { pipeTask = TryConnectToServerAsync(pipeName, timeout, cancellationToken); } } finally { if (holdsMutex) { clientMutex.ReleaseMutex(); } } } 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()); }
private static Task <BuildResponse> RunServerCompilationCore( RequestLanguage language, List <string> arguments, BuildPaths buildPaths, string pipeName, string keepAlive, string libEnvVariable, Func <string, string, bool> tryCreateServerFunc, CancellationToken cancellationToken) { var clientDir = buildPaths.ClientDirectory; var clientMutexName = BuildProtocolConstants.GetClientMutexName(pipeName); bool holdsMutex; using (var clientMutex = new Mutex(initiallyOwned: true, name: clientMutexName, createdNew: out holdsMutex)) { try { if (!holdsMutex) { try { holdsMutex = clientMutex.WaitOne(TimeOutMsNewProcess); if (!holdsMutex) { return(Task.FromResult <BuildResponse>(null)); } } catch (AbandonedMutexException) { holdsMutex = true; } } // Check for an already running server var serverMutexName = BuildProtocolConstants.GetServerMutexName(pipeName); Mutex mutexIgnore; bool wasServerRunning = Mutex.TryOpenExisting(serverMutexName, out mutexIgnore); var timeout = wasServerRunning ? TimeOutMsExistingProcess : TimeOutMsNewProcess; NamedPipeClientStream pipe = null; if (wasServerRunning || tryCreateServerFunc(clientDir, pipeName)) { pipe = TryConnectToServer(pipeName, timeout, cancellationToken); } if (pipe != null) { var request = BuildRequest.Create(language, buildPaths.WorkingDirectory, arguments, keepAlive, libEnvVariable); return(TryCompile(pipe, request, cancellationToken)); } } finally { if (holdsMutex) { clientMutex.ReleaseMutex(); } } } return(null); }
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()); } // 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; 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, BuildProtocolConstants.GetCommitHash(), arguments, keepAlive, libEnvVariable); return(await TryCompile(pipe, request, cancellationToken).ConfigureAwait(false)); } } return(new RejectedBuildResponse()); }
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)); } }