/// <summary> /// Shutting down the server is an inherently racy operation. The server can be started or stopped by /// external parties at any time. /// /// This function will return success if at any time in the function the server is determined to no longer /// be running. /// </summary> internal async Task <int> RunShutdownAsync(string pipeName, bool waitForProcess = true, TimeSpan?timeout = null, CancellationToken cancellationToken = default) { if (WasServerRunning(pipeName) == false) { // The server holds the mutex whenever it is running, if it's not open then the // server simply isn't running. return(CommonCompiler.Succeeded); } try { var realTimeout = timeout != null ? (int)timeout.Value.TotalMilliseconds : Timeout.Infinite; using var client = await ConnectForShutdownAsync(pipeName, realTimeout).ConfigureAwait(false); if (client is object) { var request = BuildRequest.CreateShutdown(); await request.WriteAsync(client, cancellationToken).ConfigureAwait(false); var response = await BuildResponse.ReadAsync(client, cancellationToken).ConfigureAwait(false); var shutdownResponse = (ShutdownBuildResponse)response; if (waitForProcess) { try { var process = Process.GetProcessById(shutdownResponse.ServerProcessId); process.WaitForExit(); } catch (Exception) { // There is an inherent race here with the server process. If it has already shutdown // by the time we try to access it then the operation has succeed. } } } return(CommonCompiler.Succeeded); } catch (Exception) { if (WasServerRunning(pipeName) == false) { // If the server was in the process of shutting down when we connected then it's reasonable // for an exception to happen. If the mutex has shutdown at this point then the server // is shut down. return(CommonCompiler.Succeeded); } return(CommonCompiler.Failed); } }
/// <summary> /// Shutting down the server is an inherently racy operation. The server can be started or stopped by /// external parties at any time. /// /// This function will return success if at any time in the function the server is determined to no longer /// be running. /// </summary> internal static async Task <int> RunShutdownAsync(string pipeName, bool waitForProcess = true, TimeSpan?timeout = null, CancellationToken cancellationToken = default(CancellationToken)) { if (string.IsNullOrEmpty(pipeName)) { var clientDirectory = AppDomain.CurrentDomain.BaseDirectory; pipeName = DesktopBuildClient.GetPipeNameFromFileInfo(clientDirectory); } var mutexName = BuildProtocolConstants.GetServerMutexName(pipeName); if (!DesktopBuildClient.WasServerMutexOpen(mutexName)) { // The server holds the mutex whenever it is running, if it's not open then the // server simply isn't running. return(CommonCompiler.Succeeded); } try { using (var client = new NamedPipeClientStream(pipeName)) { var realTimeout = timeout != null ? (int)timeout.Value.TotalMilliseconds : Timeout.Infinite; client.Connect(realTimeout); var request = BuildRequest.CreateShutdown(); await request.WriteAsync(client, cancellationToken).ConfigureAwait(false); var response = await BuildResponse.ReadAsync(client, cancellationToken).ConfigureAwait(false); var shutdownResponse = (ShutdownBuildResponse)response; if (waitForProcess) { try { var process = Process.GetProcessById(shutdownResponse.ServerProcessId); process.WaitForExit(); } catch (Exception) { // There is an inherent race here with the server process. If it has already shutdown // by the time we try to access it then the operation has succeed. } } } return(CommonCompiler.Succeeded); } catch (Exception) { if (!DesktopBuildClient.WasServerMutexOpen(mutexName)) { // If the server was in the process of shutting down when we connected then it's reasonable // for an exception to happen. If the mutex has shutdown at this point then the server // is shut down. return(CommonCompiler.Succeeded); } return(CommonCompiler.Failed); } }