Skip to content

Optimize stream adapters with new span/memory overrides#2316

Open
Sergio0694 wants to merge 17 commits intostaging/3.0from
user/sergiopedri/stream-optimize2
Open

Optimize stream adapters with new span/memory overrides#2316
Sergio0694 wants to merge 17 commits intostaging/3.0from
user/sergiopedri/stream-optimize2

Conversation

@Sergio0694
Copy link
Member

This pull request introduces comprehensive support for span and memory-based operations on streams, particularly focusing on InMemoryRandomAccessStream adapters. 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

  • Added extensive functional tests in src/Tests/FunctionalTests/Async/Program.cs to validate Read, Write, ReadAsync, and WriteAsync operations using Span<byte> and Memory<byte>, including cancellation and empty buffer cases.
  • Introduced numerous unit tests in src/Tests/UnitTest/TestComponentCSharp_Tests.cs covering synchronous and asynchronous span/memory stream operations, partial reads/writes, round-trip scenarios, and cancellation handling.

Buffer and memory enhancements

  • Added a new managed buffer type, WindowsRuntimePinnedMemoryBuffer, which allows safe access to pinned memory and explicit invalidation, improving support for advanced memory scenarios.
  • Enhanced WindowsRuntimeBufferHelpers to support retrieving spans and pointers from pinned memory buffers, including a new TryGetManagedData method for unified access to underlying buffer data. [1] [2]

Stream adapter improvements

  • Updated WindowsRuntimeManagedStreamAdapter.Implementation.Read.cs to optimize async read operations, including early return for empty reads and improved cancellation handling.
  • Added support for System.Buffers and System.Runtime.InteropServices in stream adapter implementations to facilitate new span/memory features.

Test infrastructure updates

  • Included necessary using statements for System.Buffers in 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.

Sergio0694 and others added 9 commits March 6, 2026 15:02
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>
@Sergio0694 Sergio0694 requested review from Copilot and manodasanW March 6, 2026 23:42
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.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 WindowsRuntimeManagedStreamAdapter read/write paths, with fast-path array handling and a pinned-memory slow path.
  • Introduced WindowsRuntimePinnedMemoryBuffer plus 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.

Sergio0694 and others added 2 commits March 6, 2026 16:45
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>
@Sergio0694 Sergio0694 force-pushed the user/sergiopedri/stream-optimize2 branch from ab070f7 to 5a1b96e Compare March 7, 2026 05:27
Sergio0694 and others added 5 commits March 7, 2026 19:32
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants