Skip to content

Implement async handle support on Unix for FileStream#125377

Draft
Copilot wants to merge 3 commits intocopilot/extend-safe-file-handle-pipefrom
copilot/implement-async-handle-support-unix
Draft

Implement async handle support on Unix for FileStream#125377
Copilot wants to merge 3 commits intocopilot/extend-safe-file-handle-pipefrom
copilot/implement-async-handle-support-unix

Conversation

Copy link
Contributor

Copilot AI commented Mar 10, 2026

Description

FileStream previously rejected non-blocking (O_NONBLOCK) handles on Unix. This PR lifts that restriction by adding proper poll-then-read/write support for non-blocking file descriptors, enabling FileStream to wrap async pipe/socket handles on Unix.

Native layer (pal_io.c, pal_io.h, entrypoints.c)

  • Added SystemNative_ReadFromNonblocking and SystemNative_WriteToNonblocking
  • Each attempts a read/write; on EAGAIN/EWOULDBLOCK, polls (indefinitely, on a thread pool thread) via Common_Poll until data/space is available or the pipe is closed, then retries the I/O
  • EOF on read returns 0; closed pipe on write returns -1 with EPIPE

Interop (Interop.Read.cs, Interop.Write.cs)

  • Added ReadFromNonblocking and WriteToNonblocking P/Invoke declarations

RandomAccess.Unix.cs

  • ReadAtOffset and WriteAtOffset now check handle.IsAsync first and dispatch to the new non-blocking variants; the existing SupportsRandomAccess/pread/pwrite path is unchanged for synchronous handles
if (handle.IsAsync)
{
    result = Interop.Sys.ReadFromNonblocking(handle, bufPtr, buffer.Length);
}
else if (handle.SupportsRandomAccess)
{
    result = Interop.Sys.PRead(handle, bufPtr, buffer.Length, fileOffset);
    // ... ESPIPE fallback unchanged
}
else
{
    result = Interop.Sys.Read(handle, bufPtr, buffer.Length);
}

FileStream / tests

  • Removed the ValidateHandle guard that threw ArgumentException for async handles on Unix
  • Replaced the AsyncHandleOnUnix_FileStream_ctor_Throws test with SafeFileHandle_CreateAnonymousPipe_FileStream_SetsIsAsyncAndTransfersData, which verifies all four asyncRead/asyncWrite combinations work end-to-end using FileStream directly (no AnonymousPipeClientStream shim needed)

Changes

  • src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs
  • src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs
  • src/libraries/Common/src/Interop/Unix/System.Native/Interop.Read.cs
  • src/libraries/Common/src/Interop/Unix/System.Native/Interop.Write.cs
  • src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/OpenHandle.cs
  • src/native/libs/System.Native/pal_io.c
  • src/native/libs/System.Native/pal_io.h
  • src/native/libs/System.Native/entrypoints.c

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement async handle support on Unix for FileStream Implement async handle support on Unix for FileStream Mar 10, 2026
@adamsitnik
Copy link
Member

@stephentoub regarding #125220 (comment) this is what it would take to get FileStream support O_NONBLOCK on Unix.

@stephentoub
Copy link
Member

If we move all of the async infrastructure from System.Net.Sockets into ThreadPool (support for synchronously and asynchronously waiting on arbitrary file descriptors), this would come "for free", right?

@adamsitnik
Copy link
Member

If we move all of the async infrastructure from System.Net.Sockets into ThreadPool (support for synchronously and asynchronously waiting on arbitrary file descriptors), this would come "for free", right?

Then the synchronous implementation remains the same, but the asynchronous gains cancellation support and improved throughput (we use one global thread for polling rather than one thread per each read).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants