Optimize stream adapters with new span/memory overrides#2316
Open
Sergio0694 wants to merge 17 commits intostaging/3.0from
Open
Optimize stream adapters with new span/memory overrides#2316Sergio0694 wants to merge 17 commits intostaging/3.0from
Sergio0694 wants to merge 17 commits intostaging/3.0from
Conversation
Introduce WindowsRuntimePinnedMemoryBuffer (IBuffer) to wrap a pinned pointer without owning the memory and support invalidation to prevent use-after-free. Add Span/ReadOnlySpan-based Read and Write overrides in WindowsRuntimeManagedStreamAdapter that pin spans, wrap them with the pinned buffer, and drive WinRT async operations. Add helper ThrowIfBufferIsInvalidated and an exception message constant for invalidated-buffer errors. Methods are attributed with SupportedOSPlatform and include comments explaining intent and safety considerations.
Implement Memory<byte>/ReadOnlyMemory<byte> async overloads for ReadAsync and WriteAsync on WindowsRuntimeManagedStreamAdapter. Adds fast paths for array-backed Memory via MemoryMarshal.TryGetArray and slow paths that pin memory with MemoryHandle and use a WindowsRuntimePinnedMemoryBuffer for WinRT I/O. Includes disposal/cancellation checks, result copying for reads (EnsureResultsInUserBuffer), exception propagation, and cleanup (Invalidate). Also adds using directives for System.Buffers and System.Runtime.InteropServices and marks the APIs with SupportedOSPlatform attribute.
Add coverage for Span/Memory-based Stream APIs on the WinRT InMemoryRandomAccessStream adapter. Changes include: adding System.Threading to Program.cs and a runtime functional test that exercises Write(ReadOnlySpan), Read(Span), WriteAsync(ReadOnlyMemory), ReadAsync(Memory), ReadByte/WriteByte, empty-span/memory ops, and cancellation behavior. Expand unit tests (TestComponentCSharp_Tests.cs) with many TestMethod cases for sync/async span and memory reads/writes, partial/empty reads, cancellation checks, byte/span interoperability, round-trip scenarios, and an UnmanagedMemoryManager (MemoryManager<byte>) to exercise the pinned/unmanaged memory code paths. Also add System.Buffers using and integrate the unmanaged memory tests to validate pinning and slow-path behavior.
Add consistent count == 0 early return checks to the four array-based Read/Write stream overrides in WindowsRuntimeManagedStreamAdapter, matching the existing pattern in the span/memory-based overrides. Also move argument validation into Write(byte[], int, int) to match Read(byte[], int, int). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add pragma to disable CS1573 and refactor BeginWrite overloads: the public BeginWrite now forwards to a private BeginWrite implementation (with usedByBlockingWrapper=false). The concrete implementation was moved to the bottom of the file, consolidated duplicated validation/commented logic, and adds XML doc for the usedByBlockingWrapper parameter. Behavior is preserved; the change reduces duplication and centralizes the async write logic.
Replace manual index-based loops with SequenceEqual checks when comparing data to spanRead and memoryRead.Span in src/Tests/FunctionalTests/Async/Program.cs. This simplifies the test code, improves readability, and preserves the existing return codes (119 and 121) on mismatch.
Update TryGetManagedSpanForCapacity and add TryGetManagedData helper in WindowsRuntimeBufferHelpers to handle WindowsRuntimePinnedMemoryBuffer alongside the existing array-backed buffer types. Simplify WindowsRuntimeBufferMarshal.TryGetDataUnsafe to use the new helper. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace Volatile.Read/Write calls with a volatile nint field declaration, simplifying the read/write sites to plain field access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a GetSpanForCapacity() method consistent with the other managed buffer types. Update the call site in WindowsRuntimeBufferHelpers to use it instead of manually constructing a Span from Buffer() and Capacity, which also avoids the checked uint-to-int conversion since the capacity is stored as int. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the explicit literal 0 with the default literal when clearing the _data field in Invalidate(). This is a stylistic/clarity change that uses language-appropriate default initialization for the pointer/field and does not alter runtime behavior.
There was a problem hiding this comment.
Pull request overview
This PR modernizes the WinRT stream adapters to support Span<byte> / Memory<byte>-based stream operations efficiently (including pinned-memory scenarios), and expands unit + functional tests to validate the new behaviors.
Changes:
- Added span/memory overrides to
WindowsRuntimeManagedStreamAdapterread/write paths, with fast-path array handling and a pinned-memory slow path. - Introduced
WindowsRuntimePinnedMemoryBufferplus helper updates to enable safe pointer/span access to pinned managed memory buffers. - Added extensive functional and unit tests covering sync/async span/memory I/O, partial reads/writes, cancellation, and empty-buffer behavior.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/WinRT.Runtime2/Properties/WindowsRuntimeExceptionMessages.cs | Adds a new exception message for invalidated buffer access. |
| src/WinRT.Runtime2/Properties/WindowsRuntimeExceptionExtensions.cs | Adds a helper to throw consistently when a pinned buffer has been invalidated. |
| src/WinRT.Runtime2/InteropServices/WindowsRuntimeBufferMarshal.cs | Centralizes managed-buffer pointer extraction via TryGetManagedData. |
| src/WinRT.Runtime2/InteropServices/Streams/Adapters/WindowsRuntimeManagedStreamAdapter.Implementation.Write.cs | Adds ReadOnlySpan/ReadOnlyMemory write support, pinned-memory path, and empty-write fast-path. |
| src/WinRT.Runtime2/InteropServices/Streams/Adapters/WindowsRuntimeManagedStreamAdapter.Implementation.Read.cs | Adds Span/Memory read support, pinned-memory path, and empty-read fast-path. |
| src/WinRT.Runtime2/InteropServices/Buffers/WindowsRuntimePinnedMemoryBuffer.cs | New managed-only IBuffer implementation backed by a pinned pointer with explicit invalidation. |
| src/WinRT.Runtime2/InteropServices/Buffers/WindowsRuntimeBufferHelpers.cs | Adds support for span/pointer retrieval from the new pinned memory buffer. |
| src/Tests/UnitTest/TestComponentCSharp_Tests.cs | Adds unit tests for span/memory stream APIs, including unmanaged-memory scenarios to exercise the pinning path. |
| src/Tests/FunctionalTests/Async/Program.cs | Adds functional coverage for span/memory stream adapter operations, including cancellation and empty buffers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
.../InteropServices/Streams/Adapters/WindowsRuntimeManagedStreamAdapter.Implementation.Write.cs
Show resolved
Hide resolved
In src/Tests/FunctionalTests/Async/Program.cs, explicitly assign the awaited adaptedStream.ReadAsync call to the discard variable (`_`) so the returned value is intentionally ignored. This clarifies intent and silences warnings about unused/ignored return values from the async read.
Use explicit try/catch for OperationCanceledException instead of ThrowsExactly, since the actual exception may be TaskCanceledException (a subclass) depending on whether the CsWinRT adapter or the base Stream implementation handles the call. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ab070f7 to
5a1b96e
Compare
manodasanW
approved these changes
Mar 7, 2026
Add the missing ABI CCW (COM Callable Wrapper) support file for WindowsRuntimePinnedMemoryBuffer, following the same pattern as WindowsRuntimeExternalArrayBuffer and WindowsRuntimePinnedArrayBuffer. This includes TypeMapAssociation registration, interface entries for IBuffer, IBufferByteAccess, IStringable, IWeakReferenceSource, IMarshal, IAgileObject, IInspectable, and IUnknown, plus the custom marshaller attribute and IBufferByteAccess vtable implementation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add tests that exercise CCW (COM Callable Wrapper) interop for both WindowsRuntimeExternalArrayBuffer and WindowsRuntimePinnedArrayBuffer. The existing stream adapter span/memory tests already exercise WindowsRuntimePinnedMemoryBuffer through the pinned memory code path. Managed tests: - Extend TestCCWMarshaler to also verify PinnedArrayBuffer CCW creation and IMarshal QI (via WindowsRuntimeBuffer.Create()) - Add TestWriteBufferPinnedArrayBuffer that writes a PinnedArrayBuffer to a native InMemoryRandomAccessStream and reads the data back Functional tests: - Add explicit CCW creation and IBuffer QI test for PinnedArrayBuffer - Add write-to-native-stream tests for both ExternalArrayBuffer and PinnedArrayBuffer to verify end-to-end CCW interop Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Make the WindowsRuntimeComWrappersMarshallerAttribute derivatives for all three managed buffer types file-scoped instead of public, since they are only referenced within their own file. This also removes the now-unnecessary [Obsolete] and [EditorBrowsable] attributes and the unused System.ComponentModel using directive. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a test case that writes data to a native InMemoryRandomAccessStream via the stream adapter's span-based Write override, which internally creates a WindowsRuntimePinnedMemoryBuffer and passes it as an IBuffer CCW to the native WinRT stream's WriteAsync. The data is then read back directly from the native stream to verify correctness. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use a local async method instead of a separate private helper method, matching the pattern used by the other stream adapter tests in this file (e.g. TestStreamReadAsyncMemory, TestStreamWriteAsyncMemory). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request introduces comprehensive support for span and memory-based operations on streams, particularly focusing on
InMemoryRandomAccessStreamadapters. It adds new tests to validate these operations, enhances buffer handling to support pinned memory, and introduces a new managed buffer type for pinned memory scenarios. The changes improve both functional and unit test coverage and extend the runtime's buffer and stream adapter implementations for modern .NET memory abstractions.Stream span/memory operation support
src/Tests/FunctionalTests/Async/Program.csto validateRead,Write,ReadAsync, andWriteAsyncoperations usingSpan<byte>andMemory<byte>, including cancellation and empty buffer cases.src/Tests/UnitTest/TestComponentCSharp_Tests.cscovering synchronous and asynchronous span/memory stream operations, partial reads/writes, round-trip scenarios, and cancellation handling.Buffer and memory enhancements
WindowsRuntimePinnedMemoryBuffer, which allows safe access to pinned memory and explicit invalidation, improving support for advanced memory scenarios.WindowsRuntimeBufferHelpersto support retrieving spans and pointers from pinned memory buffers, including a newTryGetManagedDatamethod for unified access to underlying buffer data. [1] [2]Stream adapter improvements
WindowsRuntimeManagedStreamAdapter.Implementation.Read.csto optimize async read operations, including early return for empty reads and improved cancellation handling.System.BuffersandSystem.Runtime.InteropServicesin stream adapter implementations to facilitate new span/memory features.Test infrastructure updates
System.Buffersin test files to support new memory manager and span-based tests. [1] [2]These changes collectively modernize the runtime's stream and buffer handling, ensuring robust support for .NET's memory abstractions and improving reliability and test coverage for advanced buffer scenarios.