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();
                }
            }
        }
示例#2
0
        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);
            }
        }
示例#4
0
            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);
                }
            }
示例#5
0
        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);
        }
示例#6
0
        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));
        }
示例#7
0
        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();
            }
        }
示例#8
0
            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);
                    }
            }
示例#9
0
        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);
        }
示例#10
0
            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));
        }
示例#12
0
        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));
        }
示例#13
0
        /// <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())));
        }
示例#14
0
        protected override bool?WasServerRunning(string pipeName)
        {
            string mutexName = BuildServerConnection.GetServerMutexName(pipeName);

            return(BuildServerConnection.WasServerMutexOpen(mutexName));
        }
示例#15
0
        /// <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));
        }