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();
                }
            }
        }
Exemple #2
0
        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);
        }
Exemple #4
0
        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));
        }
Exemple #5
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());
        }
Exemple #9
0
        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());
        }