Repository navigation
Closed
Labels
feature request 📬A request for new changes to improve functionalityA request for new changes to improve functionality
Description
Overview
Currently, the stream returned by AsStream() implements CopyToAsync which calls the provided CopyTo implementation, which calls destination.Write. This causes unexpected synchronous I/O.
Due to the synchronous I/O, an exception is thrown if this method is used in ASP.NET Core to write to the response stream:
System.InvalidOperationException: Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead.
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseStream.Write(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.Stream.Write(ReadOnlySpan`1 buffer)
at Microsoft.Toolkit.HighPerformance.Streams.MemoryStream`1.CopyTo(Stream destination, Int32 bufferSize)
at Microsoft.Toolkit.HighPerformance.Streams.MemoryStream`1.CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
API breakdown
Note: These are internal APIs
In order to implement this in a performant way, I'm thinking of adding a property Memory to ISpanOwner:
internal interface ISpanOwner
{
/// <summary>
/// Gets a <see cref="Memory{T}"/> instance wrapping the underlying memory area.
/// </summary>
Memory<byte> Memory { get; }
}And then implementing CopyToAsync like this:
partial class MemoryStream<TSource>
{
/// <inheritdoc/>
public sealed override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled(cancellationToken);
}
try
{
MemoryStream.ValidateDisposed(this.disposed);
Memory<byte> source = this.source.Memory.Slice(this.position);
this.position += source.Length;
return destination.WriteAsync(source, cancellationToken).AsTask();
}
catch (OperationCanceledException e)
{
return Task.FromCanceled(e.CancellationToken);
}
catch (Exception e)
{
return Task.FromException(e);
}
}
}Usage example
public async Task ProcessRequestAsync(HttpContext context)
{
var memory = await _service.GetDataAsync();
var stream = memory.AsStream();
await stream.CopyToAsync(context.Response.Body);
}Breaking change?
No
Alternatives
Not applicable
Additional context
Wasn't sure at first if this counted as a bug, upon closer inspection it looks a bit more likely, so I may have chosen the wrong template.
Help us help you
Yes, I'd like to be assigned to work on this item
Metadata
Metadata
Assignees
Labels
feature request 📬A request for new changes to improve functionalityA request for new changes to improve functionality
Activity
[-]Add `CopyToAsync` implementation to `MemorySource<TSource>`[/-][+]Asynchronous implementation of `MemorySource<TSource>.CopyToAsync`[/+]