コード例 #1
0
        private static async Task ListenCoreAsync(
            string pipeName,
            AsyncQueue <ListenResult> queue,
            Func <string> getClientLoggingIdentifier,
            CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                NamedPipeServerStream?pipeStream = null;

                try
                {
                    // Create the pipe and begin waiting for a connection. This
                    // doesn't block, but could fail in certain circumstances, such
                    // as Windows refusing to create the pipe for some reason
                    // (out of handles?), or the pipe was disconnected before we
                    // starting listening
                    CompilerServerLogger.Log($"Constructing pipe and waiting for connections '{pipeName}'");
                    pipeStream = NamedPipeUtil.CreateServer(pipeName);

                    // The WaitForConnectionAsync API does not fully respect the provided CancellationToken
                    // on all platforms:
                    //
                    //  https://github.com/dotnet/runtime/issues/40289
                    //
                    // To mitigate this we need to setup a cancellation Task and dispose the NamedPipeServerStream
                    // if it ever completes. Once all of the NamedPipeServerStream for the given pipe name are
                    // disposed they will all exit the WaitForConnectionAsync method
                    var connectTask = pipeStream.WaitForConnectionAsync(cancellationToken);
                    if (!PlatformInformation.IsWindows)
                    {
                        var cancelTask    = Task.Delay(TimeSpan.FromMilliseconds(-1), cancellationToken);
                        var completedTask = await Task.WhenAny(new[] { connectTask, cancelTask }).ConfigureAwait(false);

                        if (completedTask == cancelTask)
                        {
                            throw new OperationCanceledException();
                        }
                    }

                    await connectTask.ConfigureAwait(false);

                    CompilerServerLogger.Log("Pipe connection established.");
                    var connection = new NamedPipeClientConnection(pipeStream, getClientLoggingIdentifier());
                    queue.Enqueue(new ListenResult(connection: connection));
                }
                catch (OperationCanceledException)
                {
                    // Expected when the host is shutting down.
                    CompilerServerLogger.Log($"Pipe connection cancelled");
                    pipeStream?.Dispose();
                }
                catch (Exception ex)
                {
                    CompilerServerLogger.LogException(ex, $"Pipe connection error");
                    queue.Enqueue(new ListenResult(exception: ex));
                    pipeStream?.Dispose();
                }
            }
        }
コード例 #2
0
        /// <summary>
        /// Creates a Task that waits for a client connection to occur and returns the connected
        /// <see cref="NamedPipeServerStream"/> object.  Throws on any connection error.
        /// </summary>
        /// <param name="cancellationToken">Used to cancel the connection sequence.</param>
        private async Task <NamedPipeServerStream> CreateListenTaskCore(CancellationToken cancellationToken)
        {
            // Create the pipe and begin waiting for a connection. This
            // doesn't block, but could fail in certain circumstances, such
            // as Windows refusing to create the pipe for some reason
            // (out of handles?), or the pipe was disconnected before we
            // starting listening.
            CompilerServerLogger.Log("Constructing pipe '{0}'.", _pipeName);
            var pipeStream = NamedPipeUtil.CreateServer(_pipeName);

            CompilerServerLogger.Log("Successfully constructed pipe '{0}'.", _pipeName);

            CompilerServerLogger.Log("Waiting for new connection");
            await pipeStream.WaitForConnectionAsync(cancellationToken).ConfigureAwait(false);

            CompilerServerLogger.Log("Pipe connection detected.");

            if (Environment.Is64BitProcess || MemoryHelper.IsMemoryAvailable())
            {
                CompilerServerLogger.Log("Memory available - accepting connection");
                return(pipeStream);
            }

            pipeStream.Close();
            throw new Exception("Insufficient resources to process new connection.");
        }
コード例 #3
0
        private static async Task <(NamedPipeClientStream Client, NamedPipeServerStream Server)> CreateNamedPipePair()
        {
            var pipeName     = Guid.NewGuid().ToString("N").Substring(0, 10);
            var serverStream = NamedPipeUtil.CreateServer(pipeName);
            var clientStream = NamedPipeUtil.CreateClient(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
            var listenTask   = serverStream.WaitForConnectionAsync();
            await clientStream.ConnectAsync().ConfigureAwait(false);

            await listenTask.ConfigureAwait(false);

            return(clientStream, serverStream);
        }
コード例 #4
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);
                    }
            }
コード例 #5
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);
                    }
            }