Return Completed ReadResult after stream returns 0 bytes (#7337)
This commit is contained in:
parent
b2f850aa82
commit
3bd5f2c2ab
|
|
@ -208,6 +208,11 @@ namespace System.IO.Pipelines
|
||||||
public override async ValueTask<ReadResult> ReadAsync(CancellationToken cancellationToken = default)
|
public override async ValueTask<ReadResult> ReadAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
// TODO ReadyAsync needs to throw if there are overlapping reads.
|
// TODO ReadyAsync needs to throw if there are overlapping reads.
|
||||||
|
if (_isWriterCompleted)
|
||||||
|
{
|
||||||
|
return new ReadResult(buffer: default, isCanceled: false, isCompleted: true);
|
||||||
|
}
|
||||||
|
|
||||||
ThrowIfCompleted();
|
ThrowIfCompleted();
|
||||||
|
|
||||||
// PERF: store InternalTokenSource locally to avoid querying it twice (which acquires a lock)
|
// PERF: store InternalTokenSource locally to avoid querying it twice (which acquires a lock)
|
||||||
|
|
|
||||||
|
|
@ -431,7 +431,7 @@ namespace System.IO.Pipelines.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task AsyncReadWorks()
|
public async Task AsyncReadWorks()
|
||||||
{
|
{
|
||||||
MemoryStream = new AsyncStream();
|
Stream = new AsyncStream();
|
||||||
CreateReader();
|
CreateReader();
|
||||||
|
|
||||||
WriteByteArray(2000);
|
WriteByteArray(2000);
|
||||||
|
|
@ -455,7 +455,7 @@ namespace System.IO.Pipelines.Tests
|
||||||
Write(Encoding.ASCII.GetBytes(new string('a', 8)));
|
Write(Encoding.ASCII.GetBytes(new string('a', 8)));
|
||||||
var readResult = await Reader.ReadAsync();
|
var readResult = await Reader.ReadAsync();
|
||||||
Reader.AdvanceTo(readResult.Buffer.GetPosition(4), readResult.Buffer.End);
|
Reader.AdvanceTo(readResult.Buffer.GetPosition(4), readResult.Buffer.End);
|
||||||
MemoryStream.Position = 0;
|
Stream.Position = 0;
|
||||||
|
|
||||||
readResult = await Reader.ReadAsync();
|
readResult = await Reader.ReadAsync();
|
||||||
var resultString = Encoding.ASCII.GetString(readResult.Buffer.ToArray());
|
var resultString = Encoding.ASCII.GetString(readResult.Buffer.ToArray());
|
||||||
|
|
@ -471,11 +471,11 @@ namespace System.IO.Pipelines.Tests
|
||||||
Write(Encoding.ASCII.GetBytes(new string('a', 8)));
|
Write(Encoding.ASCII.GetBytes(new string('a', 8)));
|
||||||
var readResult = await Reader.ReadAsync();
|
var readResult = await Reader.ReadAsync();
|
||||||
Reader.AdvanceTo(readResult.Buffer.GetPosition(4), readResult.Buffer.End);
|
Reader.AdvanceTo(readResult.Buffer.GetPosition(4), readResult.Buffer.End);
|
||||||
MemoryStream.Position = 0;
|
Stream.Position = 0;
|
||||||
|
|
||||||
readResult = await Reader.ReadAsync();
|
readResult = await Reader.ReadAsync();
|
||||||
Reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);
|
Reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);
|
||||||
MemoryStream.Position = 0;
|
Stream.Position = 0;
|
||||||
|
|
||||||
readResult = await Reader.ReadAsync();
|
readResult = await Reader.ReadAsync();
|
||||||
var resultString = Encoding.ASCII.GetString(readResult.Buffer.ToArray());
|
var resultString = Encoding.ASCII.GetString(readResult.Buffer.ToArray());
|
||||||
|
|
@ -506,14 +506,14 @@ namespace System.IO.Pipelines.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SetMinimumReadThresholdOfZeroThrows()
|
public void SetMinimumReadThresholdOfZeroThrows()
|
||||||
{
|
{
|
||||||
Assert.Throws<ArgumentOutOfRangeException>(() => new StreamPipeReader(MemoryStream,
|
Assert.Throws<ArgumentOutOfRangeException>(() => new StreamPipeReader(Stream,
|
||||||
new StreamPipeReaderOptions(minimumSegmentSize: 4096, minimumReadThreshold: 0, new TestMemoryPool())));
|
new StreamPipeReaderOptions(minimumSegmentSize: 4096, minimumReadThreshold: 0, new TestMemoryPool())));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SetOptionsToNullThrows()
|
public void SetOptionsToNullThrows()
|
||||||
{
|
{
|
||||||
Assert.Throws<ArgumentNullException>(() => new StreamPipeReader(MemoryStream, null));
|
Assert.Throws<ArgumentNullException>(() => new StreamPipeReader(Stream, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -522,7 +522,7 @@ namespace System.IO.Pipelines.Tests
|
||||||
Write(new byte[8]);
|
Write(new byte[8]);
|
||||||
var buffer = new byte[4];
|
var buffer = new byte[4];
|
||||||
|
|
||||||
MemoryStream.Read(buffer, 0, buffer.Length);
|
Stream.Read(buffer, 0, buffer.Length);
|
||||||
var readResult = await Reader.ReadAsync();
|
var readResult = await Reader.ReadAsync();
|
||||||
|
|
||||||
Assert.Equal(buffer, readResult.Buffer.ToArray());
|
Assert.Equal(buffer, readResult.Buffer.ToArray());
|
||||||
|
|
@ -599,6 +599,18 @@ namespace System.IO.Pipelines.Tests
|
||||||
Assert.True(readResult.IsCompleted);
|
Assert.True(readResult.IsCompleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ReadAsyncAfterReceivingCompletedReadResultDoesNotThrow()
|
||||||
|
{
|
||||||
|
Stream = new ThrowAfterZeroByteReadStream();
|
||||||
|
Reader = new StreamPipeReader(Stream);
|
||||||
|
var readResult = await Reader.ReadAsync();
|
||||||
|
|
||||||
|
readResult = await Reader.ReadAsync();
|
||||||
|
Assert.True(readResult.Buffer.IsEmpty);
|
||||||
|
Assert.True(readResult.IsCompleted);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string> ReadFromPipeAsString()
|
private async Task<string> ReadFromPipeAsString()
|
||||||
{
|
{
|
||||||
var readResult = await Reader.ReadAsync();
|
var readResult = await Reader.ReadAsync();
|
||||||
|
|
@ -609,7 +621,7 @@ namespace System.IO.Pipelines.Tests
|
||||||
|
|
||||||
private string ReadFromStreamAsString(byte[] buffer)
|
private string ReadFromStreamAsString(byte[] buffer)
|
||||||
{
|
{
|
||||||
var res = MemoryStream.Read(buffer, 0, buffer.Length);
|
var res = Stream.Read(buffer, 0, buffer.Length);
|
||||||
return Encoding.ASCII.GetString(buffer);
|
return Encoding.ASCII.GetString(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -621,7 +633,7 @@ namespace System.IO.Pipelines.Tests
|
||||||
|
|
||||||
private void CreateReader(int minimumSegmentSize = 16, int minimumReadThreshold = 4, MemoryPool<byte> memoryPool = null)
|
private void CreateReader(int minimumSegmentSize = 16, int minimumReadThreshold = 4, MemoryPool<byte> memoryPool = null)
|
||||||
{
|
{
|
||||||
Reader = new StreamPipeReader(MemoryStream,
|
Reader = new StreamPipeReader(Stream,
|
||||||
new StreamPipeReaderOptions(
|
new StreamPipeReaderOptions(
|
||||||
minimumSegmentSize,
|
minimumSegmentSize,
|
||||||
minimumReadThreshold,
|
minimumReadThreshold,
|
||||||
|
|
@ -661,5 +673,28 @@ namespace System.IO.Pipelines.Tests
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ThrowAfterZeroByteReadStream : MemoryStream
|
||||||
|
{
|
||||||
|
private bool _throwOnNextCallToRead;
|
||||||
|
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return ReadAsync(new Memory<byte>(buffer, offset, count)).AsTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (_throwOnNextCallToRead)
|
||||||
|
{
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
var bytes = await base.ReadAsync(destination, cancellationToken);
|
||||||
|
if (bytes == 0)
|
||||||
|
{
|
||||||
|
_throwOnNextCallToRead = true;
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ namespace System.IO.Pipelines.Tests
|
||||||
|
|
||||||
protected const int MinimumSegmentSize = 4096;
|
protected const int MinimumSegmentSize = 4096;
|
||||||
|
|
||||||
public MemoryStream MemoryStream { get; set; }
|
public Stream Stream { get; set; }
|
||||||
|
|
||||||
public PipeWriter Writer { get; set; }
|
public PipeWriter Writer { get; set; }
|
||||||
|
|
||||||
|
|
@ -19,9 +19,9 @@ namespace System.IO.Pipelines.Tests
|
||||||
|
|
||||||
protected StreamPipeTest()
|
protected StreamPipeTest()
|
||||||
{
|
{
|
||||||
MemoryStream = new MemoryStream();
|
Stream = new MemoryStream();
|
||||||
Writer = new StreamPipeWriter(MemoryStream, MinimumSegmentSize, new TestMemoryPool());
|
Writer = new StreamPipeWriter(Stream, MinimumSegmentSize, new TestMemoryPool());
|
||||||
Reader = new StreamPipeReader(MemoryStream, new StreamPipeReaderOptions(MinimumSegmentSize, minimumReadThreshold: 256, new TestMemoryPool()));
|
Reader = new StreamPipeReader(Stream, new StreamPipeReaderOptions(MinimumSegmentSize, minimumReadThreshold: 256, new TestMemoryPool()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
@ -44,27 +44,27 @@ namespace System.IO.Pipelines.Tests
|
||||||
|
|
||||||
public void Write(byte[] data)
|
public void Write(byte[] data)
|
||||||
{
|
{
|
||||||
MemoryStream.Write(data, 0, data.Length);
|
Stream.Write(data, 0, data.Length);
|
||||||
MemoryStream.Position = 0;
|
Stream.Position = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteWithoutPosition(byte[] data)
|
public void WriteWithoutPosition(byte[] data)
|
||||||
{
|
{
|
||||||
MemoryStream.Write(data, 0, data.Length);
|
Stream.Write(data, 0, data.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Append(byte[] data)
|
public void Append(byte[] data)
|
||||||
{
|
{
|
||||||
var originalPosition = MemoryStream.Position;
|
var originalPosition = Stream.Position;
|
||||||
MemoryStream.Write(data, 0, data.Length);
|
Stream.Write(data, 0, data.Length);
|
||||||
MemoryStream.Position = originalPosition;
|
Stream.Position = originalPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] ReadWithoutFlush()
|
public byte[] ReadWithoutFlush()
|
||||||
{
|
{
|
||||||
MemoryStream.Position = 0;
|
Stream.Position = 0;
|
||||||
var buffer = new byte[MemoryStream.Length];
|
var buffer = new byte[Stream.Length];
|
||||||
var result = MemoryStream.Read(buffer, 0, (int)MemoryStream.Length);
|
var result = Stream.Read(buffer, 0, (int)Stream.Length);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ namespace System.IO.Pipelines.Tests
|
||||||
[InlineData(8000, 8000)]
|
[InlineData(8000, 8000)]
|
||||||
public async Task CanAdvanceWithPartialConsumptionOfFirstSegment(int firstWriteLength, int secondWriteLength)
|
public async Task CanAdvanceWithPartialConsumptionOfFirstSegment(int firstWriteLength, int secondWriteLength)
|
||||||
{
|
{
|
||||||
Writer = new StreamPipeWriter(MemoryStream, MinimumSegmentSize, new TestMemoryPool(maxBufferSize: 20000));
|
Writer = new StreamPipeWriter(Stream, MinimumSegmentSize, new TestMemoryPool(maxBufferSize: 20000));
|
||||||
await Writer.WriteAsync(Encoding.ASCII.GetBytes("a"));
|
await Writer.WriteAsync(Encoding.ASCII.GetBytes("a"));
|
||||||
|
|
||||||
var expectedLength = firstWriteLength + secondWriteLength + 1;
|
var expectedLength = firstWriteLength + secondWriteLength + 1;
|
||||||
|
|
@ -136,8 +136,8 @@ namespace System.IO.Pipelines.Tests
|
||||||
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/4621")]
|
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/4621")]
|
||||||
public async Task CancelPendingFlushBetweenWritesAllDataIsPreserved()
|
public async Task CancelPendingFlushBetweenWritesAllDataIsPreserved()
|
||||||
{
|
{
|
||||||
MemoryStream = new SingleWriteStream();
|
Stream = new SingleWriteStream();
|
||||||
Writer = new StreamPipeWriter(MemoryStream);
|
Writer = new StreamPipeWriter(Stream);
|
||||||
FlushResult flushResult = new FlushResult();
|
FlushResult flushResult = new FlushResult();
|
||||||
|
|
||||||
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
@ -174,8 +174,8 @@ namespace System.IO.Pipelines.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CancelPendingFlushAfterAllWritesAllDataIsPreserved()
|
public async Task CancelPendingFlushAfterAllWritesAllDataIsPreserved()
|
||||||
{
|
{
|
||||||
MemoryStream = new CannotFlushStream();
|
Stream = new CannotFlushStream();
|
||||||
Writer = new StreamPipeWriter(MemoryStream);
|
Writer = new StreamPipeWriter(Stream);
|
||||||
FlushResult flushResult = new FlushResult();
|
FlushResult flushResult = new FlushResult();
|
||||||
|
|
||||||
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
@ -211,8 +211,8 @@ namespace System.IO.Pipelines.Tests
|
||||||
{
|
{
|
||||||
var writeSize = 16;
|
var writeSize = 16;
|
||||||
var singleWriteStream = new SingleWriteStream();
|
var singleWriteStream = new SingleWriteStream();
|
||||||
MemoryStream = singleWriteStream;
|
Stream = singleWriteStream;
|
||||||
Writer = new StreamPipeWriter(MemoryStream, minimumSegmentSize: writeSize);
|
Writer = new StreamPipeWriter(Stream, minimumSegmentSize: writeSize);
|
||||||
|
|
||||||
for (var i = 0; i < 10; i++)
|
for (var i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -392,7 +392,7 @@ namespace System.IO.Pipelines.Tests
|
||||||
private void WriteStringToStream(string input)
|
private void WriteStringToStream(string input)
|
||||||
{
|
{
|
||||||
var buffer = Encoding.ASCII.GetBytes(input);
|
var buffer = Encoding.ASCII.GetBytes(input);
|
||||||
MemoryStream.Write(buffer, 0, buffer.Length);
|
Stream.Write(buffer, 0, buffer.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task WriteStringToPipeWriter(string input)
|
private async Task WriteStringToPipeWriter(string input)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue