Skip to content

Add SafeFileHandle.CreateAnonymousPipe with per-end async support, non-blocking I/O support, and Process.Windows adoption#125220

Merged
adamsitnik merged 61 commits intomainfrom
copilot/extend-safe-file-handle-pipe
Mar 12, 2026
Merged

Add SafeFileHandle.CreateAnonymousPipe with per-end async support, non-blocking I/O support, and Process.Windows adoption#125220
adamsitnik merged 61 commits intomainfrom
copilot/extend-safe-file-handle-pipe

Conversation

Copy link
Contributor

Copilot AI commented Mar 5, 2026

Description

Introduces a new SafeFileHandle.CreateAnonymousPipe(out readHandle, out writeHandle, bool asyncRead = false, bool asyncWrite = false) API and wires it through ref/CoreLib/native layers.
The change adds true per-end async semantics across platforms, including Unix IsAsync detection based on non-blocking state, and adds coverage for all asyncRead × asyncWrite combinations.

Additionally, Process.Windows.cs has been updated to adopt the new SafeFileHandle.CreateAnonymousPipe API, replacing the previous CreatePipeWithSecurityAttributes P/Invoke-based pipe creation, and standard handle access has been migrated from Interop.Kernel32.GetStdHandle to Console.OpenStandardInputHandle/OpenStandardOutputHandle/OpenStandardErrorHandle.

  • Public API surface

    • Added SafeFileHandle.CreateAnonymousPipe(...) to System.Runtime ref.
    • Added XML-documented entry point in SafeFileHandle.cs as a public static partial API with platform-specific implementations.
  • CoreLib platform implementations

    • Windows (SafeFileHandle.Windows.cs)
      • Reused named-pipe-based async implementation path for one/both async ends.
      • Preserved security/behavior comments from the referenced implementation.
      • Set cached file options on returned handles to keep IsAsync behavior consistent.
      • Updated implementation to assign out handles only after success, using temporary handles plus finally cleanup for consistent error handling.
      • Updated CreatePipe fast path to throw only on API failure and use asserts for unexpected invalid handles on success.
      • Used const for pipeMode value and simplified pipe name generation with interpolated string format.
    • Unix (SafeFileHandle.Unix.cs)
      • Implemented anonymous pipe creation via Interop.Sys.Pipe with new async flags.
      • Implemented SafeFileHandle.IsAsync by querying SystemNative_FcntlGetIsNonBlocking and caching result. This is a desired behavioral change: previously IsAsync always returned false on Unix (lying about the actual state).
      • Updated async cache field to use NullableBool for consistency with nearby cached-state fields.
      • Switched NullableBool backing type to sbyte and removed explicit field initialization.
      • Hardened pipe creation against OOM/error leaks by disposing temp handles and explicitly closing raw fds if handle construction fails.
      • Added IsClosed guard in IsAsync getter to prevent EBADF when querying closed/invalid handles.
      • Used unsafe block in method body rather than unsafe modifier on method signature, following the emerging coding guideline for the better-unsafe project.
  • Non-blocking I/O support in native layer and RandomAccess

    • Added ReadFromNonblocking and WriteToNonblocking native helpers in pal_io.c that use poll() in a loop to wait for readability/writability before performing the I/O operation on non-blocking file descriptors. Both helpers loop on EAGAIN/EWOULDBLOCK to handle spurious wakeups from poll().
    • Extended RandomAccess.Unix to detect non-blocking file descriptors and route reads/writes through the new poll-based native helpers, enabling FileStream to work correctly with async (non-blocking) pipe handles on Unix.
  • SendPacketsElement IsAsync check fix

    • The truthful IsAsync reporting on Unix (previously always false) caused SendPacketsElement(FileStream, ...) constructor to reject non-Windows FileStreams opened with FileOptions.Asynchronous, since FileStream.IsAsync now correctly returns false on Unix for regular files. Fixed the SendPacketsElement constructor to only require fileStream.IsAsync on Windows (if (!fileStream.IsAsync && OperatingSystem.IsWindows())), since the async requirement is specific to Windows socket send behavior. Added a comment explaining why the check is platform-specific (async file I/O for regular files is only supported on Windows).
  • Collateral test fixes for IsAsync behavioral change on Unix

    • The FileStreamCtorSynchronous_ArgumentException test in SendPacketsElementTest expects an ArgumentException when constructing SendPacketsElement with a synchronous FileStream, but this exception is now only thrown on Windows (due to the SendPacketsElement constructor fix above). Annotated with [PlatformSpecific(TestPlatforms.Windows)] since FileStream.IsAsync is always false on Unix for regular files.
  • In-repo adoption: Process.Windows.cs

    • Updated CreatePipe method to use SafeFileHandle.CreateAnonymousPipe instead of the previous CreatePipeWithSecurityAttributes P/Invoke wrapper, eliminating direct Interop.Kernel32.CreatePipe usage.
    • Since CreateAnonymousPipe creates non-inheritable handles by default, the DuplicateHandle call for the child-side handle now passes bInheritHandle: true (using explicit named parameter) to make the duplicated child handle inheritable (previously the parent handle was duplicated as non-inheritable from an inheritable original).
    • Simplified CreatePipe error handling: removed redundant null check and finally block. On DuplicateHandle failure, the last error is stored, both handles are disposed, and a Win32Exception is thrown using the stored error code.
    • Removed the CreatePipeWithSecurityAttributes method entirely.
    • Replaced Interop.Kernel32.GetStdHandle calls with Console.OpenStandardInputHandle(), Console.OpenStandardOutputHandle(), and Console.OpenStandardErrorHandle() for standard handle access.
    • Removed Interop.Kernel32.CreatePipe, Interop.Kernel32.GetStdHandle, and Interop.Kernel32.HandleTypes.HandleFlags from the System.Diagnostics.Process.csproj file.
    • Added System.Console project reference to System.Diagnostics.Process.csproj.
  • Bug fix: swapped variable names in FileStreamConformanceTests.cs

    • Fixed swapped readStream/writeStream variable names in the CreateConnectedStreamsAsync override for AnonymousPipeFileStreamConnectedStreamConformanceTests, where readStream was incorrectly wrapping writeHandle with FileAccess.Write and vice versa. The variable names now correctly match the stream direction.
  • Interop/project structure updates

    • Extended Interop.Sys.PipeFlags with:
      • O_NONBLOCK_READ = 0x0400
      • O_NONBLOCK_WRITE = 0x0800
    • Extended SystemNative_Pipe (pal_io.c/.h) to:
      • accept new PAL_O_NONBLOCK_READ/PAL_O_NONBLOCK_WRITE flags
      • set O_NONBLOCK independently on read/write ends by reusing the existing SystemNative_FcntlSetIsNonBlocking function
      • preserve failure cleanup semantics
    • Moved Windows CreateNamedPipe overload returning SafeFileHandle into a dedicated file:
      • Interop.CreateNamedPipe_SafeFileHandle.cs
        to avoid introducing SafePipeHandle dependencies in CoreLib compilation.
    • Updated CoreLib shared projitems to include the new dedicated interop file.
  • Tests

    • Added coverage in existing System.IO.FileSystem.Tests/File/OpenHandle.cs:
      • validates IsAsync on both ends using a simple bool flag
      • validates end-to-end data transfer for created handles across all four asyncRead × asyncWrite combinations (using AnonymousPipeClientStream on Unix for async ends, FileStream on Windows)
      • uses ReadExactlyAsync for reliable reads on pipes
      • skips pipe tests on browser-wasm (pipes not supported on that platform)
    • Added pipe conformance tests for all async read/write combinations in FileStreamConformanceTests.cs.
    • Simplified test flow per feedback:
      • removed unnecessary flush
      • uses ReadExactly
      • uses async read/write paths with await (no blocking GetAwaiter().GetResult())
      • uses bool parameters instead of FileOptions for pipe-specific tests
SafeFileHandle.CreateAnonymousPipe(
    out SafeFileHandle readHandle,
    out SafeFileHandle writeHandle,
    asyncRead: true,
    asyncWrite: false);

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 30 commits February 18, 2026 17:06
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
…API in CanSeek

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
…kFifo

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
…fiers

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
…operty

- Renamed enum System.IO.FileType to System.IO.FileHandleType
- Changed SafeFileHandle.GetFileType() method to Type property
- Updated all references in implementation and tests
- Updated project file references

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
…RIBUTE_TAG_INFO to separate file

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
…k reparse points

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
- don't use fully qualified names when using System.IO.FileHandleType
- fix GetPipeOrSocketType and describe the reasoning in the comments
- fix hallucinations (big thanks to @jkotas who spotted the bug)
- use the new API across the repo
Co-authored-by: Stephen Toub <stoub@microsoft.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
Copy link
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

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

LGTM, the CI failures are unrelated.

@jkotas @stephentoub the PR is ready for review.

Copilot AI changed the title Add SafeFileHandle.CreateAnonymousPipe with per-end async support, non-Windows FileStream async-handle guard, and Process.Windows adoption Add SafeFileHandle.CreateAnonymousPipe with per-end async support, non-blocking I/O support, and Process.Windows adoption Mar 12, 2026
Copy link
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

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

@copilot please address my feedback

… handling, remove unsafe from signatures, apply pipe name format, add const, add comment

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
@adamsitnik adamsitnik enabled auto-merge (squash) March 12, 2026 15:04
@adamsitnik
Copy link
Member

/ba-g the failures (timeouts and dead letter) are unrelated

@adamsitnik adamsitnik merged commit 8f762a2 into main Mar 12, 2026
166 of 173 checks passed
@adamsitnik adamsitnik deleted the copilot/extend-safe-file-handle-pipe branch March 12, 2026 18:30
@github-actions
Copy link
Contributor

📋 Breaking Change Documentation Required

Create a breaking change issue with AI-generated content

Generated by Breaking Change Documentation Tool - 2026-03-12 18:33:36

Copilot AI added a commit that referenced this pull request Mar 13, 2026
…non-blocking I/O support, and `Process.Windows` adoption (#125220)

fixes #122806

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: Adam Sitnik <adam.sitnik@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Stephen Toub <stoub@microsoft.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.IO breaking-change Issue or PR that represents a breaking API or functional change over a previous release. needs-breaking-change-doc-created Breaking changes need an issue opened with https://github.com/dotnet/docs/issues/new?template=dotnet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants