Пример #1
0
        private static async Task FillRequest(PipeWriter writer, CancellationToken cancellation, Func <Memory <byte>, ValueTask <int> > write)
        {
            while (true)
            {
                var buffer = writer.GetMemory();

                var bytesRead = await write(buffer);

                if (bytesRead == 0)
                {
                    break;
                }

                writer.Advance(bytesRead);

                var result = await writer.FlushAsync(cancellation);

                if (result.IsCompleted)
                {
                    break;
                }

                if (cancellation.IsCancellationRequested)
                {
                    writer.CancelPendingFlush();
                    break;
                }
            }

            writer.Complete();
        }
            /// <summary>
            /// Closes this channel and releases all resources associated with it.
            /// Pending reads and writes may be abandoned if the channel was created with an <see cref="ChannelOptions.ExistingPipe"/>.
            /// </summary>
            /// <remarks>
            /// Because this method may terminate the channel immediately and thus can cause previously queued content to not actually be received by the remote party,
            /// consider this method a "break glass" way of terminating a channel. The preferred method is that both sides "complete writing" and let the channel dispose itself.
            /// </remarks>
            public void Dispose()
            {
                if (!this.IsDisposed)
                {
                    // The code in this delegate needs to happen in several branches including possibly asynchronously.
                    // We carefully define it here with no closure so that the C# compiler generates a static field for the delegate
                    // thus avoiding any extra allocations from reusing code in this way.
                    Action <object?, object> finalDisposalAction = (exOrAntecedent, state) =>
                    {
                        var self = (Channel)state;
                        self.completionSource.TrySetResult(null);
                        self.MultiplexingStream.OnChannelDisposed(self);
                    };

                    this.acceptanceSource.TrySetCanceled();
                    this.optionsAppliedTaskSource?.TrySetCanceled();

                    PipeWriter?mxStreamIOWriter;
                    lock (this.SyncObject)
                    {
                        this.isDisposed  = true;
                        mxStreamIOWriter = this.mxStreamIOWriter;
                    }

                    // Complete writing so that the mxstream cannot write to this channel any more.
                    // We must also cancel a pending flush since no one is guaranteed to be reading this any more
                    // and we don't want to deadlock on a full buffer in a disposed channel's pipe.
                    mxStreamIOWriter?.Complete();
                    mxStreamIOWriter?.CancelPendingFlush();
                    this.mxStreamIOWriterCompleted.Set();

                    if (this.channelIO != null)
                    {
                        // We're using our own Pipe to relay user messages, so we can shutdown writing and allow for our reader to propagate what was already written
                        // before actually shutting down.
                        this.channelIO.Output.Complete();
                    }
                    else
                    {
                        // We don't own the user's PipeWriter to complete it (so they can't write anything more to this channel).
                        // We can't know whether there is or will be more bytes written to the user's PipeWriter,
                        // but we need to terminate our reader for their writer as part of reclaiming resources.
                        // We want to complete reading immediately and cancel any pending read.
                        this.mxStreamIOReader?.Complete();
                        this.mxStreamIOReader?.CancelPendingRead();
                    }

                    // As a minor perf optimization, avoid allocating a continuation task if the antecedent is already completed.
                    if (this.mxStreamIOReaderCompleted?.IsCompleted ?? true)
                    {
                        finalDisposalAction(null, this);
                    }
                    else
                    {
                        this.mxStreamIOReaderCompleted !.ContinueWith(finalDisposalAction, this, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default).Forget();
                    }
                }
            }
Пример #3
0
        protected override void Dispose(bool disposing)
        {
            if (!disposing)
            {
                return;
            }

            _writer.CancelPendingFlush();
            _writer.Complete();
            _networkStream.Dispose();
        }
Пример #4
0
        public void CancelPendingFlush()
        {
            lock (_dataWriterLock)
            {
                if (_completed)
                {
                    return;
                }

                _pipeWriter.CancelPendingFlush();
            }
        }
Пример #5
0
        public static async Task CopyToUntilCanceledOrCompletedAsync(this PipeReader reader, PipeWriter writer, CancellationToken cancel)
        {
            using var cancelRegistration = cancel.Register(delegate
            {
                // If we get canceled, indicate operation cancellation on both pipes to break out of the loop.
                // The purpose of this here is to avoid throwing exceptions for cancellation, instead using the graceful signal.
                // Just because exceptions cost extra CPU time that we want to avoid when handling load spikes (such as mass disconnects).
                reader.CancelPendingRead();
                writer.CancelPendingFlush();
            });

            // We copy until we encounter either a read cancellation (upstream reached end of stream) or a write
            // completion (downstream reached end of stream) or a write cancellation (downstream requested graceful stop).

            while (true)
            {
                var readResult = await reader.ReadAsync(CancellationToken.None);

                if (readResult.IsCanceled)
                {
                    break;
                }

                try
                {
                    if (!readResult.Buffer.IsEmpty)
                    {
                        foreach (var segment in readResult.Buffer)
                        {
                            var memory = writer.GetMemory(segment.Length);
                            segment.CopyTo(memory);
                            writer.Advance(segment.Length);
                        }

                        var flushResult = await writer.FlushAsync(CancellationToken.None);

                        if (flushResult.IsCanceled || flushResult.IsCompleted)
                        {
                            break;
                        }
                    }

                    if (readResult.IsCompleted)
                    {
                        break;
                    }
                }
                finally
                {
                    reader.AdvanceTo(readResult.Buffer.End);
                }
            }
        }
Пример #6
0
 public static void CancelPendingFlushEvenIfClosed(this PipeWriter writer)
 {
     // CancelPendingFlush() will throw if the pipe is already closed.
     // This behavior is not useful to us, so eat the exception.
     try
     {
         writer.CancelPendingFlush();
     }
     catch
     {
     }
 }
Пример #7
0
 private async Task CloseChannelAsync()
 {
     using (await _pipeLock.EnterAsync())
     {
         try
         {
             _channelPipeWriter?.CancelPendingFlush();
             _channelPipeWriter?.Complete();
             _channelPipeWriter = null;
         }
         catch
         {
             // Ignore exceptions when trying to close a pipe
         }
     }
 }
        public async Task CanCancelFlushAsyncWithCancelPendingFlushStreamWriteAsyncThrows()
        {
            var stream = new CancelledWritesStream();

            stream.WaitForFlushTask.TrySetResult(null);

            PipeWriter writer = PipeWriter.Create(stream);

            writer.WriteEmpty(10);
            ValueTask <FlushResult> task = writer.FlushAsync();

            Assert.False(task.IsCompleted);

            writer.CancelPendingFlush();

            stream.WaitForWriteTask.TrySetResult(null);

            FlushResult result = await task;

            Assert.True(result.IsCanceled);
            writer.Complete();
        }
Пример #9
0
 public override void CancelPendingFlush()
 => _underlyingPipeWriter.CancelPendingFlush();
Пример #10
0
 public override void CancelPendingFlush()
 => _writer.CancelPendingFlush();
 public override void CancelPendingFlush() => _output.CancelPendingFlush();
Пример #12
0
 public override void CancelPendingFlush()
 {
     _inner.CancelPendingFlush();
 }
Пример #13
0
 public void CancelPendingFlush()
 {
     _pipeWriter.CancelPendingFlush();
 }
Пример #14
0
 public override void CancelPendingFlush()
 {
     _pipeWriter.CancelPendingFlush();
 }