Skip to content

Fix mmap ReadOnlyMemory copy and add release notes - #20271

Open
xperiandri wants to merge 1 commit into
dotnet:mainfrom
xperiandri:fix-NotSupportedException-in-memory-mapped-file-optimization-when-copying-ReadOnlyMemory-into-MemoryMappedFileViewStream
Open

Fix mmap ReadOnlyMemory copy and add release notes#20271
xperiandri wants to merge 1 commit into
dotnet:mainfrom
xperiandri:fix-NotSupportedException-in-memory-mapped-file-optimization-when-copying-ReadOnlyMemory-into-MemoryMappedFileViewStream

Conversation

@xperiandri

Copy link
Copy Markdown
Contributor

Summary

Fixes #20263 by avoiding the unsupported PositionPointer access in the memory-mapped-file optimization path when copying ReadOnlyMemory<byte> into MemoryMappedFileViewStream.

The fix preserves the optimization while falling back to a safe Stream.Write path when the underlying memory is not a plain array-backed ReadOnlyMemory.

Changes

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

❗ Release notes required

You can open this PR in browser to add release notes: open in github.dev


✅ Found changes and release notes in following paths:

Change path Release notes path Description
`src/Compiler` docs/release-notes/.FSharp.Compiler.Service/11.0.100.md

@github-actions github-actions Bot added the AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files label Aug 17, 2026

@T-Gro T-Gro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice, focused fix — thanks for chasing this one down and writing such a clear issue + PR description. 🙌 I read through it, and the diagnosis in #20263 is spot on: MemoryMappedFile.CreateViewStream returns a SafeBuffer-backed MemoryMappedFileViewStream, and UnmanagedMemoryStream.PositionPointer throws NotSupportedException on that stream type. So the old zero-copy path was fundamentally unsound for this stream, not just slow.

What I like about the fix

  • Correct and minimal. Dropping PositionPointer and going through Stream.Write is exactly the supported mechanism the runtime expects for a SafeBuffer-backed view. The stream starts at position 0 and the view length equals bytes.Length, so a single Write fills the mapping exactly — and the position auto-advances, so the old manual stream.Position <- stream.Position + length is no longer needed.
  • Keeps the fast path. MemoryMarshal.TryGetArray means the common array-backed ReadOnlyMemory<byte> still copies with no extra managed allocation (Stream.Write(array, offset, count) is a direct memcpy into the SafeBuffer). The ToArray() fallback only allocates in the genuinely rare non-array-backed case (e.g. a MemoryManager<byte>-backed memory), which is the case that was crashing before — so behaviorally it's strictly better everywhere.
  • !!segment.Array is right. When TryGetArray returns true, Array is non-null, so the null-forgiving operator is justified and it matches the existing nullness style already used elsewhere in FileSystem.fs.
  • Portable across both TFMs. Using the array-based Stream.Write overload (rather than Stream.Write(ReadOnlySpan<byte>)) keeps this compiling on netstandard2.0 as well as net11.0.

Verification

I built FSharp.Compiler.Service locally in Debug — clean, 0 warnings / 0 errors for both netstandard2.0 and net11.0. The change compiles and the nullness annotations check out.

Minor, non-blocking nits

  1. Release-note placement. The changed code lives in src/Compiler/Utilities/FileSystem.fs, i.e. FSharp.Compiler.Service. It might fit more naturally under docs/release-notes/.FSharp.Compiler.Service/11.0.100.md's ### Fixed section rather than .VisualStudio/18.vNext.md — even though the symptom was observed while debugging in VS, the fix itself is in the compiler/service layer. Totally a judgment call, feel free to leave it.
  2. Optional regression test. Since the failing path was specifically a non-array-backed ReadOnlyMemory<byte> reaching TryFromMemory, a tiny test that drives that branch (e.g. a MemoryManager<byte>-backed ReadOnlyMemory) would lock in the fix. Not required for such a targeted change, but it would guard against a future refactor reintroducing a PositionPointer-style shortcut.

Overall this is a clean, well-reasoned fix. LGTM. 👍

@T-Gro T-Gro added the AI-reviewed PR reviewed by AI review council label Aug 17, 2026
@T-Gro
T-Gro self-requested a review August 17, 2026 08:37
@xperiandri
xperiandri force-pushed the fix-NotSupportedException-in-memory-mapped-file-optimization-when-copying-ReadOnlyMemory-into-MemoryMappedFileViewStream branch 2 times, most recently from b05b9fd to 397d44c Compare August 18, 2026 13:25
@xperiandri
xperiandri force-pushed the fix-NotSupportedException-in-memory-mapped-file-optimization-when-copying-ReadOnlyMemory-into-MemoryMappedFileViewStream branch from 397d44c to 507fb66 Compare August 19, 2026 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-reviewed PR reviewed by AI review council AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

NotSupportedException in memory-mapped-file optimization when copying ReadOnlyMemory into MemoryMappedFileViewStream

2 participants