Skip to content

Commit

Permalink
[release/6.0] Enforce scatter/gather file I/O Windows API requirement…
Browse files Browse the repository at this point in the history
…s 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 <stoub@microsoft.com>

Co-authored-by: Theodore Tsirpanis <teo.tsirpanis.718@gmail.com>
Co-authored-by: Theodore Tsirpanis <12659251+teo-tsirpanis@users.noreply.github.com>
Co-authored-by: Stephen Toub <stoub@microsoft.com>
  • Loading branch information
4 people authored Oct 19, 2021
1 parent 0879ce8 commit 7907830
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,47 @@ public async Task WriteAsyncUsingMultipleBuffers(bool async)

Assert.Equal(content, File.ReadAllBytes(filePath));
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task ReadWriteAsyncUsingMultipleBuffers(bool memoryPageSized)
{
string filePath = GetTestFilePath();
// We test with buffers both one and two memory pages long. In the former case,
// the I/O operations will issue one scatter/gather API call, and in the latter
// case they will issue multiple calls; one per buffer. The buffers must still
// be aligned to comply with FILE_FLAG_NO_BUFFERING's requirements.
int bufferSize = Environment.SystemPageSize * (memoryPageSized ? 1 : 2);
int fileSize = bufferSize * 2;
byte[] content = RandomNumberGenerator.GetBytes(fileSize);

using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, FileOptions.Asynchronous | NoBuffering))
using (SectorAlignedMemory<byte> buffer = SectorAlignedMemory<byte>.Allocate(fileSize))
{
Memory<byte> firstHalf = buffer.Memory.Slice(0, bufferSize);
Memory<byte> secondHalf = buffer.Memory.Slice(bufferSize);

content.AsSpan().CopyTo(buffer.GetSpan());
await RandomAccess.WriteAsync(handle, new ReadOnlyMemory<byte>[] { firstHalf, secondHalf }, 0);

buffer.GetSpan().Clear();
long nRead = await RandomAccess.ReadAsync(handle, new Memory<byte>[] { firstHalf, secondHalf }, 0);

Assert.Equal(buffer.GetSpan().Length, nRead);
AssertExtensions.SequenceEqual(buffer.GetSpan(), content.AsSpan());
}
}

[Fact]
public async Task ReadWriteAsyncUsingEmptyBuffers()
{
string filePath = GetTestFilePath();
using SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, FileOptions.Asynchronous | NoBuffering);

long nRead = await RandomAccess.ReadAsync(handle, Array.Empty<Memory<byte>>(), 0);
Assert.Equal(0, nRead);
await RandomAccess.WriteAsync(handle, Array.Empty<ReadOnlyMemory<byte>>(), 0);
}
}
}
Loading

0 comments on commit 7907830

Please sign in to comment.