public void MutexStopsServerStarting() { var pipeName = Guid.NewGuid().ToString("N"); var mutexName = BuildServerConnection.GetServerMutexName(pipeName); bool holdsMutex; using (var mutex = new Mutex(initiallyOwned: true, name: mutexName, createdNew: out holdsMutex)) { Assert.True(holdsMutex); try { var host = new Mock <IClientConnectionHost>(MockBehavior.Strict); var result = BuildServerController.CreateAndRunServer( pipeName, clientConnectionHost: host.Object, keepAlive: null); Assert.Equal(CommonCompiler.Failed, result); } finally { mutex.ReleaseMutex(); } } }
protected override int RunServerCore(string pipeName, IClientConnectionHost connectionHost, IDiagnosticListener listener, TimeSpan?keepAlive, CancellationToken cancellationToken) { // Grab the server mutex to prevent multiple servers from starting with the same // pipename and consuming excess resources. If someone else holds the mutex // exit immediately with a non-zero exit code var mutexName = BuildServerConnection.GetServerMutexName(pipeName); bool holdsMutex; using (var serverMutex = new Mutex(initiallyOwned: true, name: mutexName, createdNew: out holdsMutex)) { if (!holdsMutex) { return(CommonCompiler.Failed); } try { return(base.RunServerCore(pipeName, connectionHost, listener, keepAlive, cancellationToken)); } finally { serverMutex.ReleaseMutex(); } } }
internal int RunServer( string pipeName, ICompilerServerHost?compilerServerHost = null, IClientConnectionHost?clientConnectionHost = null, IDiagnosticListener?listener = null, TimeSpan?keepAlive = null, CancellationToken cancellationToken = default) { keepAlive ??= GetKeepAliveTimeout(); listener ??= new EmptyDiagnosticListener(); clientConnectionHost ??= CreateClientConnectionHost(pipeName); compilerServerHost ??= CreateCompilerServerHost(); // Grab the server mutex to prevent multiple servers from starting with the same // pipename and consuming excess resources. If someone else holds the mutex // exit immediately with a non-zero exit code var mutexName = BuildServerConnection.GetServerMutexName(pipeName); bool createdNew; using (var serverMutex = BuildServerConnection.OpenOrCreateMutex(name: mutexName, createdNew: out createdNew)) { if (!createdNew) { return(CommonCompiler.Failed); } CompilerServerLogger.Log("Keep alive timeout is: {0} milliseconds.", keepAlive?.TotalMilliseconds ?? 0); FatalError.Handler = FailFast.OnFatalException; var dispatcher = new ServerDispatcher(compilerServerHost, clientConnectionHost, listener); dispatcher.ListenAndDispatchConnections(keepAlive, cancellationToken); return(CommonCompiler.Succeeded); } }
public void ConnectToServerFails() { // Create and grab the mutex for the server. This should make // the client believe that a server is active and it will try // to connect. When it fails it should fall back to in-proc // compilation. bool holdsMutex; using (var serverMutex = new Mutex(initiallyOwned: true, name: BuildServerConnection.GetServerMutexName(_pipeName), createdNew: out holdsMutex)) { Assert.True(holdsMutex); var ranLocal = false; // Note: Connecting to a server can take up to a second to time out var client = CreateClient( compileFunc: delegate { ranLocal = true; return(0); }); var exitCode = client.RunCompilation(new[] { "/shared" }, _buildPaths).ExitCode; Assert.Equal(0, exitCode); Assert.True(ranLocal); } }
public void MutexAcquiredWhenRunningServer() { var pipeName = Guid.NewGuid().ToString("N"); var mutexName = BuildServerConnection.GetServerMutexName(pipeName); var host = new TestableClientConnectionHost(); bool?wasServerMutexOpen = null; host.Add( () => { // Use a thread instead of Task to guarantee this code runs on a different // thread and we can validate the mutex state. var tcs = new TaskCompletionSource <IClientConnection>(); var thread = new Thread( _ => { wasServerMutexOpen = BuildServerConnection.WasServerMutexOpen( mutexName ); var client = new TestableClientConnection() { ReadBuildRequestFunc = _ => Task.FromResult(ProtocolUtil.EmptyCSharpBuildRequest), WriteBuildResponseFunc = (r, _) => Task.CompletedTask, }; tcs.SetResult(client); } ); thread.Start(); return(tcs.Task); } ); host.Add( () => { var client = new TestableClientConnection() { ReadBuildRequestFunc = _ => Task.FromResult(BuildRequest.CreateShutdown()), WriteBuildResponseFunc = (r, _) => Task.CompletedTask, }; return(Task.FromResult <IClientConnection>(client)); } ); var result = BuildServerController.CreateAndRunServer( pipeName, clientConnectionHost: host, keepAlive: TimeSpan.FromMilliseconds(-1) ); Assert.Equal(CommonCompiler.Succeeded, result); Assert.True(wasServerMutexOpen); }
internal static async Task <ServerData> CreateServer( string pipeName = null, ICompilerServerHost compilerServerHost = null, bool failingServer = false, string tempPath = null) { // The total pipe path must be < 92 characters on Unix, so trim this down to 10 chars pipeName = pipeName ?? Guid.NewGuid().ToString().Substring(0, 10); compilerServerHost = compilerServerHost ?? BuildServerController.CreateCompilerServerHost(); tempPath = tempPath ?? Path.GetTempPath(); var clientConnectionHost = BuildServerController.CreateClientConnectionHostForServerHost(compilerServerHost, pipeName); if (failingServer) { clientConnectionHost = new FailingClientConnectionHost(clientConnectionHost); } var serverStatsSource = new TaskCompletionSource <ServerStats>(); var serverListenSource = new TaskCompletionSource <bool>(); var cts = new CancellationTokenSource(); var mutexName = BuildServerConnection.GetServerMutexName(pipeName); var listener = new TestableDiagnosticListener(); var task = Task.Run(() => { listener.Listening += (sender, e) => { serverListenSource.TrySetResult(true); }; try { BuildServerController.CreateAndRunServer( pipeName, tempPath, clientConnectionHost, listener, keepAlive: TimeSpan.FromMilliseconds(-1), cancellationToken: cts.Token); } finally { var serverStats = new ServerStats(connections: listener.ConnectionCount, completedConnections: listener.CompletedCount); serverStatsSource.SetResult(serverStats); } }); // 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 (BuildServerConnection.WasServerMutexOpen(mutexName) != true && !task.IsCompleted) { await Task.Yield(); } if (task.IsFaulted) { throw task.Exception; } return(new ServerData(cts, pipeName, serverStatsSource.Task, serverListenSource.Task, listener.ConnectionCompletedCollection)); }
internal async Task WaitForServerAsync() { // 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. var mutexName = BuildServerConnection.GetServerMutexName(PipeName); while (BuildServerConnection.WasServerMutexOpen(mutexName) != true && !ServerTask.IsCompleted) { await Task.Yield(); } }
public async Task ServerShutdownsDuringProcessing() { using (var readyMre = new ManualResetEvent(initialState: false)) using (var doneMre = new ManualResetEvent(initialState: false)) { var pipeName = Guid.NewGuid().ToString(); var mutexName = BuildServerConnection.GetServerMutexName(pipeName); bool created = false; bool connected = false; var thread = new Thread( () => { using (var stream = NamedPipeUtil.CreateServer(pipeName)) { var mutex = new Mutex( initiallyOwned: true, name: mutexName, createdNew: out created ); readyMre.Set(); stream.WaitForConnection(); connected = true; // Client is waiting for a response. Close the mutex now. Then close the connection // so the client gets an error. mutex.ReleaseMutex(); mutex.Dispose(); stream.Close(); doneMre.WaitOne(); } } ); // Block until the mutex and named pipe is setup. thread.Start(); readyMre.WaitOne(); var exitCode = await RunShutdownAsync(pipeName, waitForProcess : false); // Let the fake server exit. doneMre.Set(); thread.Join(); Assert.Equal(CommonCompiler.Succeeded, exitCode); Assert.True(connected); Assert.True(created); } }
public void MutexAcquiredWhenRunningServer() { var pipeName = Guid.NewGuid().ToString("N"); var mutexName = BuildServerConnection.GetServerMutexName(pipeName); var host = new Mock <IClientConnectionHost>(MockBehavior.Strict); host .Setup(x => x.CreateListenTask(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 <IClientConnection>().Task); }); var result = DesktopBuildServerController.RunServer( pipeName, Path.GetTempPath(), host.Object, keepAlive: TimeSpan.FromSeconds(1)); Assert.Equal(CommonCompiler.Succeeded, result); }
public async Task NoServerConnection() { using (var readyMre = new ManualResetEvent(initialState: false)) using (var doneMre = new ManualResetEvent(initialState: false)) { var pipeName = Guid.NewGuid().ToString(); var mutexName = BuildServerConnection.GetServerMutexName(pipeName); bool created = false; bool connected = false; var thread = new Thread( () => { using ( var mutex = new Mutex( initiallyOwned: true, name: mutexName, createdNew: out created ) ) using (var stream = NamedPipeUtil.CreateServer(pipeName)) { readyMre.Set(); // Get a client connection and then immediately close it. Don't give any response. stream.WaitForConnection(); connected = true; stream.Close(); doneMre.WaitOne(); mutex.ReleaseMutex(); } } ); // Block until the mutex and named pipe is setup. thread.Start(); readyMre.WaitOne(); var exitCode = await RunShutdownAsync(pipeName, waitForProcess : false); // Let the fake server exit. doneMre.Set(); thread.Join(); Assert.Equal(CommonCompiler.Failed, exitCode); Assert.True(connected); Assert.True(created); } }
internal static ServerData CreateServer( string pipeName = null, ICompilerServerHost compilerServerHost = null, bool failingServer = false) { pipeName = pipeName ?? Guid.NewGuid().ToString(); compilerServerHost = compilerServerHost ?? DesktopBuildServerController.CreateCompilerServerHost(); var clientConnectionHost = DesktopBuildServerController.CreateClientConnectionHostForServerHost(compilerServerHost, pipeName); if (failingServer) { clientConnectionHost = new FailingClientConnectionHost(clientConnectionHost); } var serverStatsSource = new TaskCompletionSource <ServerStats>(); var serverListenSource = new TaskCompletionSource <bool>(); var cts = new CancellationTokenSource(); var mutexName = BuildServerConnection.GetServerMutexName(pipeName); var thread = new Thread(_ => { var listener = new TestableDiagnosticListener(); listener.Listening += (sender, e) => { serverListenSource.TrySetResult(true); }; try { DesktopBuildServerController.RunServer( pipeName, clientConnectionHost, listener, keepAlive: TimeSpan.FromMilliseconds(-1), cancellationToken: cts.Token); } finally { var serverStats = new ServerStats(connections: listener.ConnectionCount, completedConnections: listener.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 (BuildServerConnection.WasServerMutexOpen(mutexName) != true && thread.IsAlive) { Thread.Yield(); } return(new ServerData(cts, pipeName, serverStatsSource.Task, serverListenSource.Task)); }
internal static async Task <ServerData> CreateServer( string pipeName = null, ICompilerServerHost compilerServerHost = null, IClientConnectionHost clientConnectionHost = null, TimeSpan?keepAlive = null) { // The total pipe path must be < 92 characters on Unix, so trim this down to 10 chars pipeName ??= Guid.NewGuid().ToString().Substring(0, 10); compilerServerHost ??= BuildServerController.CreateCompilerServerHost(); clientConnectionHost ??= BuildServerController.CreateClientConnectionHost(pipeName); keepAlive ??= TimeSpan.FromMilliseconds(-1); var listener = new TestableDiagnosticListener(); var listenerTaskCompletionSource = new TaskCompletionSource <TestableDiagnosticListener>(); var serverListenSource = new TaskCompletionSource <bool>(); var cts = new CancellationTokenSource(); var mutexName = BuildServerConnection.GetServerMutexName(pipeName); var task = Task.Run(() => { try { BuildServerController.CreateAndRunServer( pipeName, compilerServerHost, clientConnectionHost, listener, keepAlive: keepAlive, cancellationToken: cts.Token); } finally { listenerTaskCompletionSource.SetResult(listener); } }); // 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 (BuildServerConnection.WasServerMutexOpen(mutexName) != true && !task.IsCompleted) { await Task.Yield(); } if (task.IsFaulted) { throw task.Exception; } return(new ServerData(cts, pipeName, listenerTaskCompletionSource.Task)); }
/// <summary> /// Create a compiler server that fails all connections. /// </summary> internal static ServerData CreateServerFailsConnection(string pipeName = null) { pipeName = pipeName ?? Guid.NewGuid().ToString(); var taskSource = new TaskCompletionSource <ServerStats>(); var cts = new CancellationTokenSource(); using (var mre = new ManualResetEvent(initialState: false)) { var thread = new Thread(_ => { var mutexName = BuildServerConnection.GetServerMutexName(pipeName); bool holdsMutex; using (var serverMutex = new Mutex(initiallyOwned: true, name: mutexName, createdNew: out holdsMutex)) { mre.Set(); if (!holdsMutex) { throw new InvalidOperationException("Mutex should be unique"); } var connections = CreateServerFailsConnectionCore(pipeName, cts.Token).Result; taskSource.SetResult(new ServerStats(connections: connections, completedConnections: 0)); } }); thread.Start(); // Can't exit until the mutex is acquired. Otherwise the client can end up in a race // condition trying to start the server. mre.WaitOne(); } return(new ServerData(cts, pipeName, taskSource.Task, Task.FromException(new Exception()))); }
protected override bool?WasServerRunning(string pipeName) { string mutexName = BuildServerConnection.GetServerMutexName(pipeName); return(BuildServerConnection.WasServerMutexOpen(mutexName)); }
/// <summary> /// Was a server running with the specified session key during the execution of this call? /// </summary> private static bool?WasServerRunning(string pipeName) { string mutexName = BuildServerConnection.GetServerMutexName(pipeName); return(BuildServerConnection.WasServerMutexOpen(mutexName)); }