Skip to content

Commit e0dbd21

Browse files
authored
Merge pull request #632 from AArnott/testChannelCompletionOnStreamDisposal
Avoid throwing ObjectDisposedException from Channel.Completion
2 parents 48cd9f0 + 1ce11fb commit e0dbd21

File tree

7 files changed

+142
-97
lines changed

7 files changed

+142
-97
lines changed

src/Nerdbank.Streams/MultiplexingStream.Options.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ public class Options
6060
/// </summary>
6161
private bool startSuspended;
6262

63+
/// <summary>
64+
/// Backing field for the <see cref="FaultOpenChannelsOnStreamDisposal"/> property.
65+
/// </summary>
66+
private bool faultOpenChannelsOnStreamDisposal;
67+
6368
/// <summary>
6469
/// Initializes a new instance of the <see cref="Options"/> class.
6570
/// </summary>
@@ -83,6 +88,7 @@ public Options(Options copyFrom)
8388
this.defaultChannelTraceSourceFactory = copyFrom.defaultChannelTraceSourceFactory;
8489
this.defaultChannelTraceSourceFactoryWithQualifier = copyFrom.defaultChannelTraceSourceFactoryWithQualifier;
8590
this.startSuspended = copyFrom.startSuspended;
91+
this.faultOpenChannelsOnStreamDisposal = copyFrom.faultOpenChannelsOnStreamDisposal;
8692
this.SeededChannels = copyFrom.SeededChannels.ToList();
8793
}
8894

@@ -226,6 +232,20 @@ public bool StartSuspended
226232
/// </remarks>
227233
public IList<ChannelOptions> SeededChannels { get; private set; }
228234

235+
/// <summary>
236+
/// Gets or sets a value indicating whether any open channels should be faulted (i.e. their <see cref="Channel.Completion"/> task will be faulted)
237+
/// when the <see cref="MultiplexingStream"/> is disposed.
238+
/// </summary>
239+
public bool FaultOpenChannelsOnStreamDisposal
240+
{
241+
get => this.faultOpenChannelsOnStreamDisposal;
242+
set
243+
{
244+
this.ThrowIfFrozen();
245+
this.faultOpenChannelsOnStreamDisposal = value;
246+
}
247+
}
248+
229249
/// <summary>
230250
/// Gets a value indicating whether this instance is frozen.
231251
/// </summary>

src/Nerdbank.Streams/MultiplexingStream.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ public partial class MultiplexingStream : IDisposableObservable, System.IAsyncDi
104104
/// </summary>
105105
private readonly int protocolMajorVersion;
106106

107+
/// <summary>
108+
/// A value indicating whether any open channels should be faulted (i.e. their <see cref="Channel.Completion"/> task will be faulted)
109+
/// when the <see cref="MultiplexingStream"/> is disposed.
110+
/// </summary>
111+
private readonly bool faultOpenChannelsOnStreamDisposal;
112+
107113
/// <summary>
108114
/// The last number assigned to a channel.
109115
/// Each use of this should increment by two, if <see cref="isOdd"/> has a value.
@@ -131,6 +137,7 @@ private MultiplexingStream(Formatter formatter, bool? isOdd, Options options)
131137
}
132138

133139
this.TraceSource = options.TraceSource;
140+
this.faultOpenChannelsOnStreamDisposal = options.FaultOpenChannelsOnStreamDisposal;
134141

135142
this.DefaultChannelTraceSourceFactory =
136143
options.DefaultChannelTraceSourceFactoryWithQualifier
@@ -689,7 +696,7 @@ public async ValueTask DisposeAsync()
689696
{
690697
foreach (KeyValuePair<QualifiedChannelId, Channel> entry in this.openChannels)
691698
{
692-
entry.Value.Dispose(new ObjectDisposedException(nameof(MultiplexingStream)));
699+
entry.Value.Dispose(this.faultOpenChannelsOnStreamDisposal ? new ObjectDisposedException(nameof(MultiplexingStream)) : null);
693700
}
694701

695702
foreach (KeyValuePair<string, Queue<TaskCompletionSource<Channel>>> entry in this.acceptingChannels)

src/Nerdbank.Streams/netstandard2.0/PublicAPI.Shipped.txt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,3 +279,61 @@ Nerdbank.Streams.MultiplexingStream.QualifiedChannelId.QualifiedChannelId() -> v
279279
Nerdbank.Streams.MultiplexingStream.StartListening() -> void
280280
static Nerdbank.Streams.PipeExtensions.AsPrebufferedStreamAsync(this System.IO.Pipelines.PipeReader! pipeReader, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.IO.Stream!>!
281281
static Nerdbank.Streams.StreamExtensions.AsStream(this System.Buffers.ReadOnlySequence<byte> readOnlySequence, System.Action<object?>? disposeAction, object? disposeActionArg) -> System.IO.Stream!
282+
Nerdbank.Streams.BufferWriterExtensions
283+
Nerdbank.Streams.MultiplexingStream.Options.FaultOpenChannelsOnStreamDisposal.get -> bool
284+
Nerdbank.Streams.MultiplexingStream.Options.FaultOpenChannelsOnStreamDisposal.set -> void
285+
Nerdbank.Streams.ReadOnlySequenceExtensions
286+
Nerdbank.Streams.StreamPipeReader
287+
Nerdbank.Streams.StreamPipeReader.Read() -> System.IO.Pipelines.ReadResult
288+
Nerdbank.Streams.StreamPipeReader.StreamPipeReader(System.IO.Stream! stream) -> void
289+
Nerdbank.Streams.StreamPipeReader.StreamPipeReader(System.IO.Stream! stream, int bufferSize, bool leaveOpen) -> void
290+
override Nerdbank.Streams.StreamPipeReader.AdvanceTo(System.SequencePosition consumed) -> void
291+
override Nerdbank.Streams.StreamPipeReader.AdvanceTo(System.SequencePosition consumed, System.SequencePosition examined) -> void
292+
override Nerdbank.Streams.StreamPipeReader.CancelPendingRead() -> void
293+
override Nerdbank.Streams.StreamPipeReader.Complete(System.Exception? exception = null) -> void
294+
override Nerdbank.Streams.StreamPipeReader.OnWriterCompleted(System.Action<System.Exception?, object?>! callback, object? state) -> void
295+
override Nerdbank.Streams.StreamPipeReader.ReadAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask<System.IO.Pipelines.ReadResult>
296+
override Nerdbank.Streams.StreamPipeReader.TryRead(out System.IO.Pipelines.ReadResult result) -> bool
297+
static Nerdbank.Streams.BufferWriterExtensions.Write<T>(this System.Buffers.IBufferWriter<T>! writer, System.Buffers.ReadOnlySequence<T> sequence) -> void
298+
static Nerdbank.Streams.ReadOnlySequenceExtensions.Clone<T>(this System.Buffers.ReadOnlySequence<T> template) -> System.Buffers.ReadOnlySequence<T>
299+
static System.Buffers.SequenceReaderExtensions.TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out int value) -> bool
300+
static System.Buffers.SequenceReaderExtensions.TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out long value) -> bool
301+
static System.Buffers.SequenceReaderExtensions.TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out short value) -> bool
302+
static System.Buffers.SequenceReaderExtensions.TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out int value) -> bool
303+
static System.Buffers.SequenceReaderExtensions.TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out long value) -> bool
304+
static System.Buffers.SequenceReaderExtensions.TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out short value) -> bool
305+
System.Buffers.SequenceReader<T>
306+
System.Buffers.SequenceReader<T>.Advance(long count) -> void
307+
System.Buffers.SequenceReader<T>.AdvancePast(T value) -> long
308+
System.Buffers.SequenceReader<T>.AdvancePastAny(System.ReadOnlySpan<T> values) -> long
309+
System.Buffers.SequenceReader<T>.AdvancePastAny(T value0, T value1) -> long
310+
System.Buffers.SequenceReader<T>.AdvancePastAny(T value0, T value1, T value2) -> long
311+
System.Buffers.SequenceReader<T>.AdvancePastAny(T value0, T value1, T value2, T value3) -> long
312+
System.Buffers.SequenceReader<T>.Consumed.get -> long
313+
System.Buffers.SequenceReader<T>.CurrentSpan.get -> System.ReadOnlySpan<T>
314+
System.Buffers.SequenceReader<T>.CurrentSpanIndex.get -> int
315+
System.Buffers.SequenceReader<T>.End.get -> bool
316+
System.Buffers.SequenceReader<T>.IsNext(System.ReadOnlySpan<T> next, bool advancePast = false) -> bool
317+
System.Buffers.SequenceReader<T>.IsNext(T next, bool advancePast = false) -> bool
318+
System.Buffers.SequenceReader<T>.Length.get -> long
319+
System.Buffers.SequenceReader<T>.Position.get -> System.SequencePosition
320+
System.Buffers.SequenceReader<T>.Remaining.get -> long
321+
System.Buffers.SequenceReader<T>.Rewind(long count) -> void
322+
System.Buffers.SequenceReader<T>.Sequence.get -> System.Buffers.ReadOnlySequence<T>
323+
System.Buffers.SequenceReader<T>.SequenceReader() -> void
324+
System.Buffers.SequenceReader<T>.SequenceReader(System.Buffers.ReadOnlySequence<T> sequence) -> void
325+
System.Buffers.SequenceReader<T>.TryAdvanceTo(T delimiter, bool advancePastDelimiter = true) -> bool
326+
System.Buffers.SequenceReader<T>.TryAdvanceToAny(System.ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true) -> bool
327+
System.Buffers.SequenceReader<T>.TryCopyTo(System.Span<T> destination) -> bool
328+
System.Buffers.SequenceReader<T>.TryPeek(out T value) -> bool
329+
System.Buffers.SequenceReader<T>.TryRead(out T value) -> bool
330+
System.Buffers.SequenceReader<T>.TryReadExact(int count, out System.Buffers.ReadOnlySequence<T> sequence) -> bool
331+
System.Buffers.SequenceReader<T>.TryReadTo(out System.Buffers.ReadOnlySequence<T> sequence, System.ReadOnlySpan<T> delimiter, bool advancePastDelimiter = true) -> bool
332+
System.Buffers.SequenceReader<T>.TryReadTo(out System.Buffers.ReadOnlySequence<T> sequence, T delimiter, bool advancePastDelimiter = true) -> bool
333+
System.Buffers.SequenceReader<T>.TryReadTo(out System.Buffers.ReadOnlySequence<T> sequence, T delimiter, T delimiterEscape, bool advancePastDelimiter = true) -> bool
334+
System.Buffers.SequenceReader<T>.TryReadTo(out System.ReadOnlySpan<T> span, T delimiter, bool advancePastDelimiter = true) -> bool
335+
System.Buffers.SequenceReader<T>.TryReadTo(out System.ReadOnlySpan<T> span, T delimiter, T delimiterEscape, bool advancePastDelimiter = true) -> bool
336+
System.Buffers.SequenceReader<T>.TryReadToAny(out System.Buffers.ReadOnlySequence<T> sequence, System.ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true) -> bool
337+
System.Buffers.SequenceReader<T>.TryReadToAny(out System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true) -> bool
338+
System.Buffers.SequenceReader<T>.UnreadSpan.get -> System.ReadOnlySpan<T>
339+
System.Buffers.SequenceReaderExtensions
Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +0,0 @@
1-
Nerdbank.Streams.BufferWriterExtensions
2-
Nerdbank.Streams.ReadOnlySequenceExtensions
3-
Nerdbank.Streams.StreamPipeReader
4-
Nerdbank.Streams.StreamPipeReader.Read() -> System.IO.Pipelines.ReadResult
5-
Nerdbank.Streams.StreamPipeReader.StreamPipeReader(System.IO.Stream! stream) -> void
6-
Nerdbank.Streams.StreamPipeReader.StreamPipeReader(System.IO.Stream! stream, int bufferSize, bool leaveOpen) -> void
7-
override Nerdbank.Streams.StreamPipeReader.AdvanceTo(System.SequencePosition consumed) -> void
8-
override Nerdbank.Streams.StreamPipeReader.AdvanceTo(System.SequencePosition consumed, System.SequencePosition examined) -> void
9-
override Nerdbank.Streams.StreamPipeReader.CancelPendingRead() -> void
10-
override Nerdbank.Streams.StreamPipeReader.Complete(System.Exception? exception = null) -> void
11-
override Nerdbank.Streams.StreamPipeReader.OnWriterCompleted(System.Action<System.Exception?, object?>! callback, object? state) -> void
12-
override Nerdbank.Streams.StreamPipeReader.ReadAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask<System.IO.Pipelines.ReadResult>
13-
override Nerdbank.Streams.StreamPipeReader.TryRead(out System.IO.Pipelines.ReadResult result) -> bool
14-
static Nerdbank.Streams.BufferWriterExtensions.Write<T>(this System.Buffers.IBufferWriter<T>! writer, System.Buffers.ReadOnlySequence<T> sequence) -> void
15-
static Nerdbank.Streams.ReadOnlySequenceExtensions.Clone<T>(this System.Buffers.ReadOnlySequence<T> template) -> System.Buffers.ReadOnlySequence<T>
16-
static System.Buffers.SequenceReaderExtensions.TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out int value) -> bool
17-
static System.Buffers.SequenceReaderExtensions.TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out long value) -> bool
18-
static System.Buffers.SequenceReaderExtensions.TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out short value) -> bool
19-
static System.Buffers.SequenceReaderExtensions.TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out int value) -> bool
20-
static System.Buffers.SequenceReaderExtensions.TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out long value) -> bool
21-
static System.Buffers.SequenceReaderExtensions.TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out short value) -> bool
22-
System.Buffers.SequenceReader<T>
23-
System.Buffers.SequenceReader<T>.Advance(long count) -> void
24-
System.Buffers.SequenceReader<T>.AdvancePast(T value) -> long
25-
System.Buffers.SequenceReader<T>.AdvancePastAny(System.ReadOnlySpan<T> values) -> long
26-
System.Buffers.SequenceReader<T>.AdvancePastAny(T value0, T value1) -> long
27-
System.Buffers.SequenceReader<T>.AdvancePastAny(T value0, T value1, T value2) -> long
28-
System.Buffers.SequenceReader<T>.AdvancePastAny(T value0, T value1, T value2, T value3) -> long
29-
System.Buffers.SequenceReader<T>.Consumed.get -> long
30-
System.Buffers.SequenceReader<T>.CurrentSpan.get -> System.ReadOnlySpan<T>
31-
System.Buffers.SequenceReader<T>.CurrentSpanIndex.get -> int
32-
System.Buffers.SequenceReader<T>.End.get -> bool
33-
System.Buffers.SequenceReader<T>.IsNext(System.ReadOnlySpan<T> next, bool advancePast = false) -> bool
34-
System.Buffers.SequenceReader<T>.IsNext(T next, bool advancePast = false) -> bool
35-
System.Buffers.SequenceReader<T>.Length.get -> long
36-
System.Buffers.SequenceReader<T>.Position.get -> System.SequencePosition
37-
System.Buffers.SequenceReader<T>.Remaining.get -> long
38-
System.Buffers.SequenceReader<T>.Rewind(long count) -> void
39-
System.Buffers.SequenceReader<T>.Sequence.get -> System.Buffers.ReadOnlySequence<T>
40-
System.Buffers.SequenceReader<T>.SequenceReader() -> void
41-
System.Buffers.SequenceReader<T>.SequenceReader(System.Buffers.ReadOnlySequence<T> sequence) -> void
42-
System.Buffers.SequenceReader<T>.TryAdvanceTo(T delimiter, bool advancePastDelimiter = true) -> bool
43-
System.Buffers.SequenceReader<T>.TryAdvanceToAny(System.ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true) -> bool
44-
System.Buffers.SequenceReader<T>.TryCopyTo(System.Span<T> destination) -> bool
45-
System.Buffers.SequenceReader<T>.TryPeek(out T value) -> bool
46-
System.Buffers.SequenceReader<T>.TryRead(out T value) -> bool
47-
System.Buffers.SequenceReader<T>.TryReadExact(int count, out System.Buffers.ReadOnlySequence<T> sequence) -> bool
48-
System.Buffers.SequenceReader<T>.TryReadTo(out System.Buffers.ReadOnlySequence<T> sequence, System.ReadOnlySpan<T> delimiter, bool advancePastDelimiter = true) -> bool
49-
System.Buffers.SequenceReader<T>.TryReadTo(out System.Buffers.ReadOnlySequence<T> sequence, T delimiter, bool advancePastDelimiter = true) -> bool
50-
System.Buffers.SequenceReader<T>.TryReadTo(out System.Buffers.ReadOnlySequence<T> sequence, T delimiter, T delimiterEscape, bool advancePastDelimiter = true) -> bool
51-
System.Buffers.SequenceReader<T>.TryReadTo(out System.ReadOnlySpan<T> span, T delimiter, bool advancePastDelimiter = true) -> bool
52-
System.Buffers.SequenceReader<T>.TryReadTo(out System.ReadOnlySpan<T> span, T delimiter, T delimiterEscape, bool advancePastDelimiter = true) -> bool
53-
System.Buffers.SequenceReader<T>.TryReadToAny(out System.Buffers.ReadOnlySequence<T> sequence, System.ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true) -> bool
54-
System.Buffers.SequenceReader<T>.TryReadToAny(out System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true) -> bool
55-
System.Buffers.SequenceReader<T>.UnreadSpan.get -> System.ReadOnlySpan<T>
56-
System.Buffers.SequenceReaderExtensions

src/Nerdbank.Streams/netstandard2.1/PublicAPI.Shipped.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,3 +286,20 @@ Nerdbank.Streams.MultiplexingStream.QualifiedChannelId.QualifiedChannelId() -> v
286286
Nerdbank.Streams.MultiplexingStream.StartListening() -> void
287287
static Nerdbank.Streams.PipeExtensions.AsPrebufferedStreamAsync(this System.IO.Pipelines.PipeReader! pipeReader, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.IO.Stream!>!
288288
static Nerdbank.Streams.StreamExtensions.AsStream(this System.Buffers.ReadOnlySequence<byte> readOnlySequence, System.Action<object?>? disposeAction, object? disposeActionArg) -> System.IO.Stream!
289+
Nerdbank.Streams.BufferWriterExtensions
290+
Nerdbank.Streams.MultiplexingStream.Options.FaultOpenChannelsOnStreamDisposal.get -> bool
291+
Nerdbank.Streams.MultiplexingStream.Options.FaultOpenChannelsOnStreamDisposal.set -> void
292+
Nerdbank.Streams.ReadOnlySequenceExtensions
293+
Nerdbank.Streams.StreamPipeReader
294+
Nerdbank.Streams.StreamPipeReader.Read() -> System.IO.Pipelines.ReadResult
295+
Nerdbank.Streams.StreamPipeReader.StreamPipeReader(System.IO.Stream! stream) -> void
296+
Nerdbank.Streams.StreamPipeReader.StreamPipeReader(System.IO.Stream! stream, int bufferSize, bool leaveOpen) -> void
297+
override Nerdbank.Streams.StreamPipeReader.AdvanceTo(System.SequencePosition consumed) -> void
298+
override Nerdbank.Streams.StreamPipeReader.AdvanceTo(System.SequencePosition consumed, System.SequencePosition examined) -> void
299+
override Nerdbank.Streams.StreamPipeReader.CancelPendingRead() -> void
300+
override Nerdbank.Streams.StreamPipeReader.Complete(System.Exception? exception = null) -> void
301+
override Nerdbank.Streams.StreamPipeReader.OnWriterCompleted(System.Action<System.Exception?, object?>! callback, object? state) -> void
302+
override Nerdbank.Streams.StreamPipeReader.ReadAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask<System.IO.Pipelines.ReadResult>
303+
override Nerdbank.Streams.StreamPipeReader.TryRead(out System.IO.Pipelines.ReadResult result) -> bool
304+
static Nerdbank.Streams.BufferWriterExtensions.Write<T>(this System.Buffers.IBufferWriter<T>! writer, System.Buffers.ReadOnlySequence<T> sequence) -> void
305+
static Nerdbank.Streams.ReadOnlySequenceExtensions.Clone<T>(this System.Buffers.ReadOnlySequence<T> template) -> System.Buffers.ReadOnlySequence<T>
Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +0,0 @@
1-
Nerdbank.Streams.BufferWriterExtensions
2-
Nerdbank.Streams.ReadOnlySequenceExtensions
3-
Nerdbank.Streams.StreamPipeReader
4-
Nerdbank.Streams.StreamPipeReader.Read() -> System.IO.Pipelines.ReadResult
5-
Nerdbank.Streams.StreamPipeReader.StreamPipeReader(System.IO.Stream! stream) -> void
6-
Nerdbank.Streams.StreamPipeReader.StreamPipeReader(System.IO.Stream! stream, int bufferSize, bool leaveOpen) -> void
7-
override Nerdbank.Streams.StreamPipeReader.AdvanceTo(System.SequencePosition consumed) -> void
8-
override Nerdbank.Streams.StreamPipeReader.AdvanceTo(System.SequencePosition consumed, System.SequencePosition examined) -> void
9-
override Nerdbank.Streams.StreamPipeReader.CancelPendingRead() -> void
10-
override Nerdbank.Streams.StreamPipeReader.Complete(System.Exception? exception = null) -> void
11-
override Nerdbank.Streams.StreamPipeReader.OnWriterCompleted(System.Action<System.Exception?, object?>! callback, object? state) -> void
12-
override Nerdbank.Streams.StreamPipeReader.ReadAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask<System.IO.Pipelines.ReadResult>
13-
override Nerdbank.Streams.StreamPipeReader.TryRead(out System.IO.Pipelines.ReadResult result) -> bool
14-
static Nerdbank.Streams.BufferWriterExtensions.Write<T>(this System.Buffers.IBufferWriter<T>! writer, System.Buffers.ReadOnlySequence<T> sequence) -> void
15-
static Nerdbank.Streams.ReadOnlySequenceExtensions.Clone<T>(this System.Buffers.ReadOnlySequence<T> template) -> System.Buffers.ReadOnlySequence<T>

0 commit comments

Comments
 (0)