/// <inheritdoc />
public override void Complete(Exception? exception = null)
{
+ if (CompleteAndGetNeedsDispose())
+ {
+ InnerStream.Dispose();
+ }
+ }
+
+#if NETCOREAPP
+ public override ValueTask CompleteAsync(Exception? exception = null) =>
+ CompleteAndGetNeedsDispose() ? InnerStream.DisposeAsync() : default;
+#endif
+
+ private bool CompleteAndGetNeedsDispose()
+ {
if (_isReaderCompleted)
{
- return;
+ return false;
}
_isReaderCompleted = true;
returnSegment.ResetMemory();
}
- if (!LeaveOpen)
- {
- InnerStream.Dispose();
- }
+ return !LeaveOpen;
}
/// <inheritdoc />
await Assert.ThrowsAsync<OperationCanceledException>(async () => await reader.ReadAsync());
}
+ [Fact]
+ public async Task CompleteCallsAppropriateDisposeMethodOnUnderlyingStream()
+ {
+ DisposalTrackingStream stream = new();
+ PipeReader reader = PipeReader.Create(stream);
+ reader.Complete();
+ Assert.True(stream.DisposeCalled);
+ Assert.False(stream.DisposeAsyncCalled);
+
+ stream = new();
+ reader = PipeReader.Create(stream);
+ await reader.CompleteAsync();
+#if NETCOREAPP
+ Assert.False(stream.DisposeCalled);
+ Assert.True(stream.DisposeAsyncCalled);
+#else
+ Assert.True(stream.DisposeCalled);
+ Assert.False(stream.DisposeAsyncCalled);
+#endif
+ }
+
private static async Task<string> ReadFromPipeAsString(PipeReader reader)
{
ReadResult readResult = await reader.ReadAsync();
}
#endif
}
+
+ private class DisposalTrackingStream : MemoryStream
+ {
+ public bool DisposeCalled { get; private set; }
+ public bool DisposeAsyncCalled { get; private set; }
+
+ protected override void Dispose(bool disposing)
+ {
+ DisposeCalled = true;
+ }
+
+#if NETCOREAPP
+ public override ValueTask DisposeAsync()
+ {
+ DisposeAsyncCalled = true;
+ return default;
+ }
+#endif
+ }
}
}