public static Task <GenerationResponse> RunServerGeneration( string sharedCompilationId, List <string> arguments, GenerationsPathsInfo buildPaths, string keepAlive, CancellationToken cancellationToken) { var pipeNameOpt = sharedCompilationId ?? GetPipeNameForPathOpt(buildPaths.ClientDirectory); return(RunServerGenerationCore( arguments, buildPaths, pipeNameOpt, keepAlive, timeoutOverride: null, tryCreateServerFunc: TryCreateServerCore, cancellationToken: cancellationToken)); }
internal static async Task <GenerationResponse> RunServerGenerationCore( List <string> arguments, GenerationsPathsInfo buildPaths, string pipeName, string keepAlive, int?timeoutOverride, Func <string, string, bool, bool> tryCreateServerFunc, CancellationToken cancellationToken) { if (pipeName == null) { return(new RejectedGenerationResponse()); } if (buildPaths.TempDirectory == null) { return(new RejectedGenerationResponse()); } // 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(GenerationProtocolConstants.GetCommitHash())) { return(new IncorrectHashGenerationResponse()); } 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 RejectedGenerationResponse()); } if (!holdsMutex) { try { holdsMutex = clientMutex.WaitOne(timeoutNewProcess); if (!holdsMutex) { return(new RejectedGenerationResponse()); } } 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, false)) { 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 = GenerationRequest.Create(buildPaths.WorkingDirectory, buildPaths.TempDirectory, GenerationProtocolConstants.GetCommitHash(), arguments, keepAlive); return(await TryGeneration(pipe, request, cancellationToken).ConfigureAwait(false)); } } return(new RejectedGenerationResponse()); }
internal bool TryShutdownGenerationServer(string pipeName, GenerationsPathsInfo buildPaths) { return(TryCreateServerCore(buildPaths.ClientDirectory, pipeName, isShutdown: true)); }