Skip to content

Commit 7907830

Browse files
github-actions[bot]teo-tsirpanisstephentoub
authored
[release/6.0] Enforce scatter/gather file I/O Windows API requirements et. al. (#58423)
* Move checking and pinning Windows vectored I/O buffers to a dedicated method. * Refactor the scatter/gather APIs to use the common checking method. And use pinned GCHandles and IntPtrs instead of MemoryHandles when passing the segment array to the bottom-most method. * Shorten the name of the buffer-checking method. * Directly get the pinned array's address instead of calling GCHandle.AddrOfPinnedObject. * Refactor the error handling logic in TryPrepareScatterGatherBuffers. * Allocate the segment array from native memory and at TryPrepareScatterGatherBuffers. * Cache the page size on a static readonly field and add a couple of TODOs. * Make the memory handlers readonly structs. * Add a test. * Reorder some methods with PR feedback taken into consideration. * Stop special-casing scatter/gather operations with zero or one buffer. * Factor the cleaning-up of the segment buffers into a separate method. * Follow up on Scatter/Gather API changes (#58447) * Allocate an array of memory handles only if needed. * Remove an unnecessary variable in the multiple-syscall write gather. * Actually verify the content read by the read scatter operation. * Delay allocating native memory. * Verify that the whole file was read in the scatter/gather test. * Test the case when the scatter/gather buffers are acceptable by the Windows API. * Avoid null pointer dereferences when passing an empty segment array. * Test performing scatter/gather I/O with an empty segment array. Co-authored-by: Stephen Toub <[email protected]> Co-authored-by: Theodore Tsirpanis <[email protected]> Co-authored-by: Theodore Tsirpanis <[email protected]> Co-authored-by: Stephen Toub <[email protected]>
1 parent 0879ce8 commit 7907830

File tree

2 files changed

+195
-114
lines changed

2 files changed

+195
-114
lines changed

src/libraries/System.IO.FileSystem/tests/RandomAccess/NoBuffering.Windows.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,5 +176,47 @@ public async Task WriteAsyncUsingMultipleBuffers(bool async)
176176

177177
Assert.Equal(content, File.ReadAllBytes(filePath));
178178
}
179+
180+
[Theory]
181+
[InlineData(true)]
182+
[InlineData(false)]
183+
public async Task ReadWriteAsyncUsingMultipleBuffers(bool memoryPageSized)
184+
{
185+
string filePath = GetTestFilePath();
186+
// We test with buffers both one and two memory pages long. In the former case,
187+
// the I/O operations will issue one scatter/gather API call, and in the latter
188+
// case they will issue multiple calls; one per buffer. The buffers must still
189+
// be aligned to comply with FILE_FLAG_NO_BUFFERING's requirements.
190+
int bufferSize = Environment.SystemPageSize * (memoryPageSized ? 1 : 2);
191+
int fileSize = bufferSize * 2;
192+
byte[] content = RandomNumberGenerator.GetBytes(fileSize);
193+
194+
using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, FileOptions.Asynchronous | NoBuffering))
195+
using (SectorAlignedMemory<byte> buffer = SectorAlignedMemory<byte>.Allocate(fileSize))
196+
{
197+
Memory<byte> firstHalf = buffer.Memory.Slice(0, bufferSize);
198+
Memory<byte> secondHalf = buffer.Memory.Slice(bufferSize);
199+
200+
content.AsSpan().CopyTo(buffer.GetSpan());
201+
await RandomAccess.WriteAsync(handle, new ReadOnlyMemory<byte>[] { firstHalf, secondHalf }, 0);
202+
203+
buffer.GetSpan().Clear();
204+
long nRead = await RandomAccess.ReadAsync(handle, new Memory<byte>[] { firstHalf, secondHalf }, 0);
205+
206+
Assert.Equal(buffer.GetSpan().Length, nRead);
207+
AssertExtensions.SequenceEqual(buffer.GetSpan(), content.AsSpan());
208+
}
209+
}
210+
211+
[Fact]
212+
public async Task ReadWriteAsyncUsingEmptyBuffers()
213+
{
214+
string filePath = GetTestFilePath();
215+
using SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, FileOptions.Asynchronous | NoBuffering);
216+
217+
long nRead = await RandomAccess.ReadAsync(handle, Array.Empty<Memory<byte>>(), 0);
218+
Assert.Equal(0, nRead);
219+
await RandomAccess.WriteAsync(handle, Array.Empty<ReadOnlyMemory<byte>>(), 0);
220+
}
179221
}
180222
}

0 commit comments

Comments
 (0)