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: DesktopBuildClient.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); } }
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 = DesktopBuildClient.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(); } } }
private static int MainCore(string[] args) { #if NET46 return(DesktopBuildClient.Run(args, RequestLanguage.CSharpCompile, Csc.Run, new DesktopAnalyzerAssemblyLoader())); #else return(CoreClrBuildClient.Run(args, RequestLanguage.CSharpCompile, Csc.Run, new CoreClrAnalyzerAssemblyLoader())); #endif }
public static int MainInternal(string[] args) { #if DESKTOP return(DesktopBuildClient.Run(args, Array.Empty <string>(), RequestLanguage.CSharpCompile, new CompileFunc(Csc.Run), new DesktopAnalyzerAssemblyLoader())); #else return(CoreClrBuildClient.Run(args, RequestLanguage.CSharpCompile, new CompileFunc(Csc.Run))); #endif }
public void GetBasePipeNameSlashes() { var path = string.Format(@"q:{0}the{0}path", Path.DirectorySeparatorChar); var name = DesktopBuildClient.GetBasePipeName(path); Assert.Equal(name, DesktopBuildClient.GetBasePipeName(path)); Assert.Equal(name, DesktopBuildClient.GetBasePipeName(path + Path.DirectorySeparatorChar)); Assert.Equal(name, DesktopBuildClient.GetBasePipeName(path + Path.DirectorySeparatorChar + Path.DirectorySeparatorChar)); }
private static int MainCore(string[] args) { #if NET472 var loader = new DesktopAnalyzerAssemblyLoader(); #else var loader = new CoreClrAnalyzerAssemblyLoader(); #endif return(DesktopBuildClient.Run(args, RequestLanguage.StarkCompile, Skc.Run, loader)); }
private static int MainCore(string[] args) { #if BOOTSTRAP ExitingTraceListener.Install(); #endif #if NET472 var loader = new DesktopAnalyzerAssemblyLoader(); #else var loader = new CoreClrAnalyzerAssemblyLoader(); #endif return(DesktopBuildClient.Run(args, RequestLanguage.VisualBasicCompile, Vbc.Run, loader)); }
public static Task <BuildResponse> RunServerCompilation( RequestLanguage language, List <string> arguments, BuildPaths buildPaths, string keepAlive, string libEnvVariable, CancellationToken cancellationToken) => DesktopBuildClient.RunServerCompilation( language, arguments, buildPaths, keepAlive, libEnvVariable, cancellationToken);
internal static ServerData CreateServer( string pipeName = null, TimeSpan?timeout = null, ICompilerServerHost compilerServerHost = null, IClientConnectionHost clientConnectionHost = null) { pipeName = pipeName ?? Guid.NewGuid().ToString(); compilerServerHost = compilerServerHost ?? new DesktopCompilerServerHost(DefaultClientDirectory, DefaultSdkDirectory); var serverStatsSource = new TaskCompletionSource <ServerStats>(); var serverListenSource = new TaskCompletionSource <bool>(); var cts = new CancellationTokenSource(); var mutexName = DesktopBuildClient.GetServerMutexName(pipeName); var thread = new Thread(_ => { var listener = new TestableDiagnosticListener(); listener.Listening += (sender, e) => { serverListenSource.TrySetResult(true); }; try { clientConnectionHost = clientConnectionHost ?? new NamedPipeClientConnectionHost(compilerServerHost, pipeName); DesktopBuildServerController.RunServer( pipeName, clientConnectionHost, listener, timeout ?? TimeSpan.FromMilliseconds(-1), 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 (DesktopBuildClient.WasServerMutexOpen(mutexName) != true && thread.IsAlive) { Thread.Yield(); } return(new ServerData(cts, pipeName, serverStatsSource.Task, serverListenSource.Task)); }
public void MutexAcquiredWhenRunningServer() { var pipeName = Guid.NewGuid().ToString("N"); var mutexName = DesktopBuildClient.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, host.Object, keepAlive: TimeSpan.FromSeconds(1)); Assert.Equal(CommonCompiler.Succeeded, result); }
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 = DesktopBuildClient.GetServerMutexName(pipeName); bool created = false; bool connected = false; var thread = new Thread(() => { using (var stream = new NamedPipeServerStream(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 async Task NoServerConnection() { using (var readyMre = new ManualResetEvent(initialState: false)) using (var doneMre = new ManualResetEvent(initialState: false)) { var pipeName = Guid.NewGuid().ToString(); var mutexName = DesktopBuildClient.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 = new NamedPipeServerStream(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); } }
/// <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 = DesktopBuildClient.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()))); }
public void MutexStopsServerStarting() { var pipeName = Guid.NewGuid().ToString("N"); var mutexName = DesktopBuildClient.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 = DesktopBuildServerController.RunServer(pipeName, host.Object, keepAlive: null); Assert.Equal(CommonCompiler.Failed, result); } finally { mutex.ReleaseMutex(); } } }
/// <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); } }
protected override string GetDefaultPipeName() { var clientDirectory = AppDomain.CurrentDomain.BaseDirectory; return(DesktopBuildClient.GetPipeNameForPath(clientDirectory)); }
protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands) { if (ProvideCommandLineArgs) { CommandLineArgs = GetArguments(commandLineCommands, responseFileCommands) .Select(arg => new TaskItem(arg)).ToArray(); } if (SkipCompilerExecution) { return(0); } if (!UseSharedCompilation || !string.IsNullOrEmpty(ToolPath) || !Utilities.IsCompilerServerSupported) { return(base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands)); } using (_sharedCompileCts = new CancellationTokenSource()) { try { CompilerServerLogger.Log($"CommandLine = '{commandLineCommands}'"); CompilerServerLogger.Log($"BuildResponseFile = '{responseFileCommands}'"); // Try to get the location of the user-provided build client and server, // which should be located next to the build task. If not, fall back to // "pathToTool", which is the compiler in the MSBuild default bin directory. var clientDir = TryGetClientDir() ?? Path.GetDirectoryName(pathToTool); pathToTool = Path.Combine(clientDir, ToolExe); // Note: we can't change the "tool path" printed to the console when we run // the Csc/Vbc task since MSBuild logs it for us before we get here. Instead, // we'll just print our own message that contains the real client location Log.LogMessage(ErrorString.UsingSharedCompilation, clientDir); var buildPaths = new BuildPaths( clientDir: clientDir, // MSBuild doesn't need the .NET SDK directory sdkDir: null, workingDir: CurrentDirectoryToUse()); var responseTask = DesktopBuildClient.RunServerCompilation( Language, GetArguments(commandLineCommands, responseFileCommands).ToList(), buildPaths, keepAlive: null, libEnvVariable: LibDirectoryToUse(), cancellationToken: _sharedCompileCts.Token); responseTask.Wait(_sharedCompileCts.Token); var response = responseTask.Result; if (response != null) { ExitCode = HandleResponse(response, pathToTool, responseFileCommands, commandLineCommands); } else { Log.LogMessage(ErrorString.SharedCompilationFallback, pathToTool); ExitCode = base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands); } } catch (OperationCanceledException) { ExitCode = 0; } catch (Exception e) { Log.LogErrorWithCodeFromResources("Compiler_UnexpectedException"); LogErrorOutput(e.ToString()); ExitCode = -1; } } return(ExitCode); }
private static int MainCore(string[] args) => DesktopBuildClient.Run(args, RequestLanguage.VisualBasicCompile, Vbc.Run, new DesktopAnalyzerAssemblyLoader());
protected override bool?WasServerRunning(string pipeName) { string mutexName = DesktopBuildClient.GetServerMutexName(pipeName); return(DesktopBuildClient.WasServerMutexOpen(mutexName)); }
public static int Main(string[] args, string[] extraArgs) => DesktopBuildClient.Run(args, extraArgs, RequestLanguage.VisualBasicCompile, Vbc.Run, new SimpleAnalyzerAssemblyLoader());
public static int Main(string[] args, string[] extraArgs) => DesktopBuildClient.Run(args, extraArgs, RequestLanguage.CSharpCompile, Csc.Run, new DesktopAnalyzerAssemblyLoader());