|
internal interface IReadWriteAdapter |
|
{ |
|
static abstract ValueTask<int> ReadAsync(Stream stream, Memory<byte> buffer, CancellationToken cancellationToken); |
|
static abstract ValueTask<int> ReadAtLeastAsync(Stream stream, Memory<byte> buffer, int minimumBytes, bool throwOnEndOfStream, CancellationToken cancellationToken); |
|
static abstract ValueTask WriteAsync(Stream stream, ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken); |
|
static abstract Task FlushAsync(Stream stream, CancellationToken cancellationToken); |
|
static abstract Task WaitAsync(TaskCompletionSource<bool> waiter); |
|
static abstract Task WaitAsync(Task task); |
|
static abstract ValueTask<T> WaitAsync<T>(ValueTask<T> task); |
|
} |
|
|
|
internal readonly struct AsyncReadWriteAdapter : IReadWriteAdapter |
|
{ |
|
public static ValueTask<int> ReadAsync(Stream stream, Memory<byte> buffer, CancellationToken cancellationToken) => |
|
stream.ReadAsync(buffer, cancellationToken); |
|
|
|
public static ValueTask<int> ReadAtLeastAsync(Stream stream, Memory<byte> buffer, int minimumBytes, bool throwOnEndOfStream, CancellationToken cancellationToken) => |
|
stream.ReadAtLeastAsync(buffer, minimumBytes, throwOnEndOfStream, cancellationToken); |
|
|
|
public static ValueTask WriteAsync(Stream stream, ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken) => |
|
stream.WriteAsync(buffer, cancellationToken); |
|
|
|
public static Task FlushAsync(Stream stream, CancellationToken cancellationToken) => stream.FlushAsync(cancellationToken); |
|
|
|
public static Task WaitAsync(TaskCompletionSource<bool> waiter) => waiter.Task; |
|
public static Task WaitAsync(Task task) => task; |
|
public static ValueTask<T> WaitAsync<T>(ValueTask<T> task) => task; |
|
} |
|
|
|
internal readonly struct SyncReadWriteAdapter : IReadWriteAdapter |
|
{ |
|
public static ValueTask<int> ReadAsync(Stream stream, Memory<byte> buffer, CancellationToken cancellationToken) => |
|
new ValueTask<int>(stream.Read(buffer.Span)); |
|
|
|
public static ValueTask<int> ReadAtLeastAsync(Stream stream, Memory<byte> buffer, int minimumBytes, bool throwOnEndOfStream, CancellationToken cancellationToken) => |
|
new ValueTask<int>(stream.ReadAtLeast(buffer.Span, minimumBytes, throwOnEndOfStream)); |
|
|
|
public static ValueTask WriteAsync(Stream stream, ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken) |
|
{ |
|
stream.Write(buffer.Span); |
|
return default; |
|
} |
|
|
|
public static Task FlushAsync(Stream stream, CancellationToken cancellationToken) |
|
{ |
|
stream.Flush(); |
|
return Task.CompletedTask; |
|
} |
|
|
|
public static Task WaitAsync(TaskCompletionSource<bool> waiter) |
|
{ |
|
waiter.Task.GetAwaiter().GetResult(); |
|
return Task.CompletedTask; |
|
} |
|
|
|
public static Task WaitAsync(Task task) |
|
{ |
|
task.GetAwaiter().GetResult(); |
|
return Task.CompletedTask; |
|
} |
|
|
|
public static ValueTask<T> WaitAsync<T>(ValueTask<T> task) |
|
{ |
|
return ValueTask.FromResult(task.AsTask().GetAwaiter().GetResult()); |
|
} |
|
} |
Current TarReader/Writer and ZipArchive contain lots of logic, that is duplicated across the sync and async API implementations. See e.g.
runtime/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarReader.cs
Lines 199 to 262 in 6f2ad2d
Similarly, when introducing new tests, the developers often resort to duplicating the same test across sync and async variants, see e.g.
TarWriter_WriteEntryAsync_File_TestsandTarWriter_WriteEntry_File_Tests. This risks increased regressions where bugs may be fixed in one code path but not the other (and not caught because only one variant of test was added). Deduplicating the logic would decrease developer toil and reduce the risk of regressions.Since the code generallly boils down to calling a sync/async variant of a Read or Write method on a Stream, we can use the same technique of deduplicating the implementation as we did in SslStream and SmtpClient, see e.g. #115366.
IReadWriteAdapter
runtime/src/libraries/Common/src/System/Net/ReadWriteAdapter.cs
Lines 10 to 75 in 6f2ad2d
Usage example:
runtime/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpReplyReaderFactory.cs
Lines 251 to 281 in 6f2ad2d
runtime/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs
Lines 806 to 813 in 6f2ad2d
runtime/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs
Lines 890 to 895 in 6f2ad2d