From 1ecd5cc7c066b5e171beb55b164e443488f1481e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 19 Aug 2021 10:39:12 +0200 Subject: [PATCH] extend the NO_BUFFERING tests and cover all scenarios: - sync operations on async handle - async operations on async handle - sync operations on sync handle - async operations on sync handle --- .../tests/RandomAccess/NoBuffering.Windows.cs | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/NoBuffering.Windows.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/NoBuffering.Windows.cs index d897cc7b8be25b..bd54fe44a872e7 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/NoBuffering.Windows.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/NoBuffering.Windows.cs @@ -15,18 +15,24 @@ public class RandomAccess_NoBuffering : FileSystemTest { private const FileOptions NoBuffering = (FileOptions)0x20000000; + public static IEnumerable AllAsyncSyncCombinations() + { + yield return new object[] { false, false }; + yield return new object[] { false, true }; + yield return new object[] { true, true }; + yield return new object[] { true, false }; + } + [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task ReadUsingSingleBuffer(bool async) + [MemberData(nameof(AllAsyncSyncCombinations))] + public async Task ReadUsingSingleBuffer(bool asyncOperation, bool asyncHandle) { const int fileSize = 1_000_000; // 1 MB string filePath = GetTestFilePath(); byte[] expected = RandomNumberGenerator.GetBytes(fileSize); File.WriteAllBytes(filePath, expected); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, - options: FileOptions.Asynchronous | NoBuffering)) // to use Scatter&Gather APIs on Windows the handle MUST be opened for async IO + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: GetFileOptions(asyncHandle))) using (SectorAlignedMemory buffer = SectorAlignedMemory.Allocate(Environment.SystemPageSize)) { int current = 0; @@ -43,7 +49,7 @@ public async Task ReadUsingSingleBuffer(bool async) // It's possible to get 0 if we are lucky and file size is a multiple of physical sector size. do { - current = async + current = asyncOperation ? await RandomAccess.ReadAsync(handle, buffer.Memory, fileOffset: total) : RandomAccess.Read(handle, buffer.GetSpan(), fileOffset: total); @@ -58,16 +64,15 @@ public async Task ReadUsingSingleBuffer(bool async) } [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task ReadAsyncUsingMultipleBuffers(bool async) + [MemberData(nameof(AllAsyncSyncCombinations))] + public async Task ReadAsyncUsingMultipleBuffers(bool asyncOperation, bool asyncHandle) { const int fileSize = 1_000_000; // 1 MB string filePath = GetTestFilePath(); byte[] expected = RandomNumberGenerator.GetBytes(fileSize); File.WriteAllBytes(filePath, expected); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: FileOptions.Asynchronous | NoBuffering)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: GetFileOptions(asyncHandle))) using (SectorAlignedMemory buffer_1 = SectorAlignedMemory.Allocate(Environment.SystemPageSize)) using (SectorAlignedMemory buffer_2 = SectorAlignedMemory.Allocate(Environment.SystemPageSize)) { @@ -82,7 +87,7 @@ public async Task ReadAsyncUsingMultipleBuffers(bool async) do { - current = async + current = asyncOperation ? await RandomAccess.ReadAsync(handle, buffers, fileOffset: total) : RandomAccess.Read(handle, buffers, fileOffset: total); @@ -99,16 +104,15 @@ public async Task ReadAsyncUsingMultipleBuffers(bool async) } [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task WriteUsingSingleBuffer(bool async) + [MemberData(nameof(AllAsyncSyncCombinations))] + public async Task WriteUsingSingleBuffer(bool asyncOperation, bool asyncHandle) { string filePath = GetTestFilePath(); int bufferSize = Environment.SystemPageSize; int fileSize = bufferSize * 10; byte[] content = RandomNumberGenerator.GetBytes(fileSize); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, FileOptions.Asynchronous | NoBuffering)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, GetFileOptions(asyncHandle))) using (SectorAlignedMemory buffer = SectorAlignedMemory.Allocate(bufferSize)) { int total = 0; @@ -118,7 +122,7 @@ public async Task WriteUsingSingleBuffer(bool async) int take = Math.Min(content.Length - total, bufferSize); content.AsSpan(total, take).CopyTo(buffer.GetSpan()); - if (async) + if (asyncOperation) { await RandomAccess.WriteAsync(handle, buffer.Memory, fileOffset: total); } @@ -135,16 +139,15 @@ public async Task WriteUsingSingleBuffer(bool async) } [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task WriteAsyncUsingMultipleBuffers(bool async) + [MemberData(nameof(AllAsyncSyncCombinations))] + public async Task WriteAsyncUsingMultipleBuffers(bool asyncOperation, bool asyncHandle) { string filePath = GetTestFilePath(); int bufferSize = Environment.SystemPageSize; int fileSize = bufferSize * 10; byte[] content = RandomNumberGenerator.GetBytes(fileSize); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, FileOptions.Asynchronous | NoBuffering)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, GetFileOptions(asyncHandle))) using (SectorAlignedMemory buffer_1 = SectorAlignedMemory.Allocate(bufferSize)) using (SectorAlignedMemory buffer_2 = SectorAlignedMemory.Allocate(bufferSize)) { @@ -161,7 +164,7 @@ public async Task WriteAsyncUsingMultipleBuffers(bool async) content.AsSpan((int)total, bufferSize).CopyTo(buffer_1.GetSpan()); content.AsSpan((int)total + bufferSize, bufferSize).CopyTo(buffer_2.GetSpan()); - if (async) + if (asyncOperation) { await RandomAccess.WriteAsync(handle, buffers, fileOffset: total); } @@ -176,5 +179,8 @@ public async Task WriteAsyncUsingMultipleBuffers(bool async) Assert.Equal(content, File.ReadAllBytes(filePath)); } + + // when using FileOptions.Asynchronous we are testing Scatter&Gather APIs on Windows (FILE_FLAG_OVERLAPPED requirement) + private static FileOptions GetFileOptions(bool asyncHandle) => (asyncHandle ? FileOptions.Asynchronous : FileOptions.None) | NoBuffering; } }