public void ServerStartup_MutexAlreadyAcquired_Fails() { // Arrange var pipeName = Guid.NewGuid().ToString("N"); var mutexName = MutexName.GetServerMutexName(pipeName); var compilerHost = new Mock <CompilerHost>(MockBehavior.Strict); var host = new Mock <ConnectionHost>(MockBehavior.Strict); // Act & Assert using (var mutex = new Mutex(initiallyOwned: true, name: mutexName, createdNew: out var holdsMutex)) { Assert.True(holdsMutex); try { var result = ServerUtilities.RunServer(pipeName, host.Object, compilerHost.Object); // Assert failure Assert.Equal(1, result); } finally { mutex.ReleaseMutex(); } } }
protected override Task <int> ExecuteCoreAsync() { // Make sure there's only one server with the same identity at a time. using (var mutex = new Mutex(initiallyOwned: true, name: MutexName.GetServerMutexName(Pipe.Value()), createdNew: out var holdsMutex)) { if (!holdsMutex) { // Another server is running, just exit. Error.Write("Another server already running..."); return(Task.FromResult(1)); } try { var host = ConnectionHost.Create(Pipe.Value()); var compilerHost = CompilerHost.Create(); var dispatcher = RequestDispatcher.Create(host, compilerHost, Cancelled); dispatcher.Run(); } finally { mutex.ReleaseMutex(); } } return(Task.FromResult(0)); }
private bool IsServerRunning() { if (Mutex.TryOpenExisting(MutexName.GetServerMutexName(Pipe.Value()), out var mutex)) { mutex.Dispose(); return(true); } return(false); }
protected override Task <int> ExecuteCoreAsync() { // Make sure there's only one server with the same identity at a time. var serverMutexName = MutexName.GetServerMutexName(Pipe.Value()); Mutex serverMutex = null; var holdsMutex = false; try { serverMutex = new Mutex(initiallyOwned: true, name: serverMutexName, createdNew: out holdsMutex); } catch (Exception ex) { // 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 Error.Write($"Server mutex creation failed. {ex.Message}"); return(Task.FromResult(-1)); } if (!holdsMutex) { // Another server is running, just exit. Error.Write("Another server already running..."); return(Task.FromResult(1)); } try { TimeSpan?keepAlive = null; if (KeepAlive.HasValue() && int.TryParse(KeepAlive.Value(), out var result)) { // Keep alive times are specified in seconds keepAlive = TimeSpan.FromSeconds(result); } var host = ConnectionHost.Create(Pipe.Value()); var compilerHost = CompilerHost.Create(); ExecuteServerCore(host, compilerHost, Cancelled, eventBus: null, keepAlive: keepAlive); } finally { serverMutex.ReleaseMutex(); serverMutex.Dispose(); } return(Task.FromResult(0)); }
internal static ServerData CreateServer( string pipeName = null, CompilerHost compilerHost = null, ConnectionHost connectionHost = null, Action <object, EventArgs> onListening = null) { pipeName = pipeName ?? Guid.NewGuid().ToString(); compilerHost = compilerHost ?? CompilerHost.Create(); connectionHost = connectionHost ?? ConnectionHost.Create(pipeName); var serverStatsSource = new TaskCompletionSource <ServerStats>(); var serverListenSource = new TaskCompletionSource <bool>(); var cts = new CancellationTokenSource(); var mutexName = MutexName.GetServerMutexName(pipeName); var thread = new Thread(_ => { var eventBus = new TestableEventBus(); eventBus.Listening += (sender, e) => { serverListenSource.TrySetResult(true); }; if (onListening != null) { eventBus.Listening += (sender, e) => onListening(sender, e); } try { RunServer( pipeName, connectionHost, compilerHost, cts.Token, eventBus, Timeout.InfiniteTimeSpan); } finally { var serverStats = new ServerStats(connections: eventBus.ConnectionCount, completedConnections: eventBus.CompletedCount); serverStatsSource.SetResult(serverStats); } }); thread.Start(); // The contract of this function is that it will return once the server has started. Spin here until // we can verify the server has started or simply failed to start. while (ServerConnection.WasServerMutexOpen(mutexName) != true && thread.IsAlive) { Thread.Yield(); } return(new ServerData(cts, pipeName, serverStatsSource.Task, serverListenSource.Task)); }
public void ServerStartup_SuccessfullyAcquiredMutex() { // Arrange, Act & Assert var pipeName = Guid.NewGuid().ToString("N"); var mutexName = MutexName.GetServerMutexName(pipeName); var compilerHost = new Mock <CompilerHost>(MockBehavior.Strict); var host = new Mock <ConnectionHost>(MockBehavior.Strict); host .Setup(x => x.WaitForConnectionAsync(It.IsAny <CancellationToken>())) .Returns(() => { // Use a thread instead of Task to guarantee this code runs on a different // thread and we can validate the mutex state. var source = new TaskCompletionSource <bool>(); var thread = new Thread(_ => { Mutex mutex = null; try { Assert.True(Mutex.TryOpenExisting(mutexName, out mutex)); Assert.False(mutex.WaitOne(millisecondsTimeout: 0)); source.SetResult(true); } catch (Exception ex) { source.SetException(ex); throw; } finally { mutex?.Dispose(); } }); // Synchronously wait here. Don't returned a Task value because we need to // ensure the above check completes before the server hits a timeout and // releases the mutex. thread.Start(); source.Task.Wait(); return(new TaskCompletionSource <Connection>().Task); }); var result = ServerUtilities.RunServer(pipeName, host.Object, compilerHost.Object, keepAlive: TimeSpan.FromSeconds(1)); Assert.Equal(0, result); }
protected override Task <int> ExecuteCoreAsync() { // Make sure there's only one server with the same identity at a time. using (var mutex = new Mutex(initiallyOwned: true, name: MutexName.GetServerMutexName(Pipe.Value()), createdNew: out var holdsMutex)) { if (!holdsMutex) { // Another server is running, just exit. Error.Write("Another server already running..."); return(Task.FromResult(1)); } try { TimeSpan?keepAlive = null; if (KeepAlive.HasValue()) { var value = KeepAlive.Value(); if (int.TryParse(value, out var result)) { // Keep alive times are specified in seconds keepAlive = TimeSpan.FromSeconds(result); } } var host = ConnectionHost.Create(Pipe.Value()); var compilerHost = CompilerHost.Create(); ExecuteServerCore(host, compilerHost, Cancelled, eventBus: null, keepAlive: keepAlive); } finally { mutex.ReleaseMutex(); } } return(Task.FromResult(0)); }
private static async Task <ServerResponse> RunOnServerCore( IList <string> arguments, ServerPaths serverPaths, string pipeName, string keepAlive, int?timeoutOverride, TryCreateServerCoreDelegate <string, string, int?, bool, bool> tryCreateServerFunc, CancellationToken cancellationToken, bool debug) { if (pipeName == null) { return(new RejectedServerResponse()); } if (serverPaths.TempDirectory == null) { return(new RejectedServerResponse()); } var clientDir = serverPaths.ClientDirectory; var timeoutNewProcess = timeoutOverride ?? TimeOutMsNewProcess; var timeoutExistingProcess = timeoutOverride ?? TimeOutMsExistingProcess; var clientMutexName = MutexName.GetClientMutexName(pipeName); Task <Client> pipeTask = null; Mutex clientMutex = null; var holdsMutex = false; try { try { clientMutex = new Mutex(initiallyOwned: true, name: clientMutexName, createdNew: out holdsMutex); } catch (Exception ex) { // 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 ServerLogger.LogException(ex, "Client mutex creation failed."); return(new RejectedServerResponse()); } if (!holdsMutex) { try { holdsMutex = clientMutex.WaitOne(timeoutNewProcess); if (!holdsMutex) { return(new RejectedServerResponse()); } } catch (AbandonedMutexException) { holdsMutex = true; } } // Check for an already running server var serverMutexName = MutexName.GetServerMutexName(pipeName); var wasServerRunning = WasServerMutexOpen(serverMutexName); var timeout = wasServerRunning ? timeoutExistingProcess : timeoutNewProcess; if (wasServerRunning || tryCreateServerFunc(clientDir, pipeName, out var _, debug)) { pipeTask = Client.ConnectAsync(pipeName, TimeSpan.FromMilliseconds(timeout), cancellationToken); } } finally { if (holdsMutex) { clientMutex?.ReleaseMutex(); } clientMutex?.Dispose(); } if (pipeTask != null) { var client = await pipeTask.ConfigureAwait(false); if (client != null) { var request = ServerRequest.Create( serverPaths.WorkingDirectory, serverPaths.TempDirectory, arguments, keepAlive); return(await TryProcessRequest(client, request, cancellationToken).ConfigureAwait(false)); } } return(new RejectedServerResponse()); }
private static async Task <ServerResponse> RunOnServerCore( IList <string> arguments, ServerPaths buildPaths, string pipeName, string keepAlive, int?timeoutOverride, Func <string, string, bool, bool> tryCreateServerFunc, CancellationToken cancellationToken, bool debug) { if (pipeName == null) { return(new RejectedServerResponse()); } if (buildPaths.TempDirectory == null) { return(new RejectedServerResponse()); } var clientDir = buildPaths.ClientDirectory; var timeoutNewProcess = timeoutOverride ?? TimeOutMsNewProcess; var timeoutExistingProcess = timeoutOverride ?? TimeOutMsExistingProcess; var clientMutexName = MutexName.GetClientMutexName(pipeName); Task <Client> 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 RejectedServerResponse()); } } catch (AbandonedMutexException) { holdsMutex = true; } } // Check for an already running server var serverMutexName = MutexName.GetServerMutexName(pipeName); var wasServerRunning = WasServerMutexOpen(serverMutexName); var timeout = wasServerRunning ? timeoutExistingProcess : timeoutNewProcess; if (wasServerRunning || tryCreateServerFunc(clientDir, pipeName, debug)) { pipeTask = Client.ConnectAsync(pipeName, TimeSpan.FromMilliseconds(timeout), cancellationToken); } } finally { if (holdsMutex) { clientMutex.ReleaseMutex(); } } } if (pipeTask != null) { var client = await pipeTask.ConfigureAwait(false); if (client != null) { var request = ServerRequest.Create( buildPaths.WorkingDirectory, buildPaths.TempDirectory, arguments, keepAlive); return(await TryProcessRequest(client, request, cancellationToken).ConfigureAwait(false)); } } return(new RejectedServerResponse()); }