From 2c00709478fcf1bd459b61b0ce3b14300e08da39 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 17:18:07 +0000 Subject: [PATCH 1/5] Initial plan From 743265b0c4b02cf456592a02a52f332902e3477e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 17:22:53 +0000 Subject: [PATCH 2/5] Add breaking change article for SafeFileHandle.IsAsync Unix behavior change in .NET 11 Agent-Logs-Url: https://github.com/dotnet/docs/sessions/8ae53bcd-ed7a-4368-898a-433576b68e76 Co-authored-by: gewarren <24882762+gewarren@users.noreply.github.com> --- docs/core/compatibility/11.md | 1 + .../11/safefilehandle-isasync-unix.md | 80 +++++++++++++++++++ docs/core/compatibility/toc.yml | 2 + 3 files changed, 83 insertions(+) create mode 100644 docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md diff --git a/docs/core/compatibility/11.md b/docs/core/compatibility/11.md index 0fd9eb43a4d92..43df77939bb2d 100644 --- a/docs/core/compatibility/11.md +++ b/docs/core/compatibility/11.md @@ -29,6 +29,7 @@ See [Breaking changes in ASP.NET Core 10](/aspnet/core/breaking-changes/10/overv | [Environment.TickCount made consistent with Windows timeout behavior](core-libraries/11/environment-tickcount-windows-behavior.md) | Behavioral change | | [MemoryStream maximum capacity updated and exception behavior changed](core-libraries/11/memorystream-max-capacity.md) | Behavioral change | | [API obsoletions with non-default diagnostic IDs (.NET 11)](core-libraries/11/obsolete-apis.md) | Source incompatible | +| [SafeFileHandle.IsAsync and FileStream.IsAsync accurately reflect non-blocking state on Unix](core-libraries/11/safefilehandle-isasync-unix.md) | Behavioral change | | [TAR-reading APIs verify header checksums when reading](core-libraries/11/tar-checksum-validation.md) | Behavioral change | | [ZipArchive.CreateAsync eagerly loads ZIP archive entries](core-libraries/11/ziparchive-createasync-eager-load.md) | Behavioral change | diff --git a/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md b/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md new file mode 100644 index 0000000000000..74c2d7d06ea46 --- /dev/null +++ b/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md @@ -0,0 +1,80 @@ +--- +title: "Breaking change: SafeFileHandle.IsAsync and FileStream.IsAsync accurately reflect non-blocking state on Unix" +description: "Learn about the breaking change in .NET 11 where SafeFileHandle.IsAsync and FileStream.IsAsync on Unix now accurately reflect whether the underlying file descriptor is in non-blocking mode." +ms.date: 04/07/2026 +ai-usage: ai-assisted +--- + +# SafeFileHandle.IsAsync and FileStream.IsAsync accurately reflect non-blocking state on Unix + +On Unix, and now accurately reflect whether the underlying file descriptor has the `O_NONBLOCK` flag set. Previously, `IsAsync` unconditionally returned `true` for regular files opened with , even though regular file I/O on Unix is inherently synchronous at the kernel level. + +## Version introduced + +.NET 11 Preview 3 + +## Previous behavior + +Previously, `SafeFileHandle.IsAsync` always returned `true` on Unix for regular files opened with `FileOptions.Asynchronous`, regardless of whether the underlying file descriptor had the `O_NONBLOCK` flag set. + +```csharp +using Microsoft.Win32.SafeHandles; + +// On Unix, IsAsync was true for regular files opened with FileOptions.Asynchronous, +// even though the file descriptor had no O_NONBLOCK set. +using SafeFileHandle handle = File.OpenHandle("myfile.txt", options: FileOptions.Asynchronous); +Console.WriteLine(handle.IsAsync); // true (misleading; no O_NONBLOCK on regular file) +``` + +Conversely, for file descriptors that were genuinely non-blocking (for example, pipes or sockets with `O_NONBLOCK` set), `IsAsync` incorrectly returned `false`. + +On non-Windows platforms, constructing a `SendPacketsElement` with a non-async `FileStream` threw an `ArgumentException`. + +## New behavior + +Starting in .NET 11, `SafeFileHandle.IsAsync` on Unix returns `true` only when the underlying file descriptor has the `O_NONBLOCK` flag set, which is possible for pipes and sockets. For regular files, it returns `false`. + +```csharp +using Microsoft.Win32.SafeHandles; + +// On Unix, IsAsync now reflects the actual non-blocking state of the file descriptor. +SafeFileHandle.CreateAnonymousPipe( + out SafeFileHandle readHandle, + out SafeFileHandle writeHandle, + asyncRead: true, + asyncWrite: false); + +Console.WriteLine(readHandle.IsAsync); // true (O_NONBLOCK set on read end) +Console.WriteLine(writeHandle.IsAsync); // false (blocking write end) +``` + +For regular files opened with `FileOptions.Asynchronous`, `IsAsync` correctly returns `false` on Unix because regular file I/O is inherently synchronous at the kernel level. + +`FileStream.IsAsync` reflects the updated value of the underlying `SafeFileHandle.IsAsync`. + +Additionally, on non-Windows platforms, constructing a `SendPacketsElement` with a `FileStream` no longer throws `ArgumentException` regardless of whether the stream is async. + +## Type of breaking change + +This change is a [behavioral change](../../categories.md#behavioral-change). + +## Reason for change + +The previous behavior was incorrect and misleading. `SafeFileHandle.IsAsync` reported `true` on Unix for regular files that aren't truly non-blocking, and `false` for file descriptors (such as pipes or sockets) that genuinely had `O_NONBLOCK` set. This caused APIs and user code that relied on this property to make incorrect decisions. Accurate `IsAsync` reporting was also a prerequisite for the new `SafeFileHandle.CreateAnonymousPipe` API to correctly expose per-end async semantics on Unix. For more information, see [dotnet/runtime#125220](https://github.com/dotnet/runtime/pull/125220). + +## Recommended action + +Review any code that checks `SafeFileHandle.IsAsync` or `FileStream.IsAsync` on Unix and takes action based on the result: + +- If your code assumed `IsAsync` was always `true` on Unix for files opened with `FileOptions.Asynchronous`, update it to account for the fact that regular file handles now return `false`. + +- If you wrap a non-blocking `SafeFileHandle` in a `FileStream` (for example, one created via `SafeFileHandle.CreateAnonymousPipe` with `asyncRead: true` or `asyncWrite: true`), `FileStream.IsAsync` might now return `true` where it previously returned `false`. Adjust downstream code accordingly. + +- If you construct `SendPacketsElement` with a `FileStream` on a non-Windows platform and previously expected an `ArgumentException` to be thrown for non-async streams, note that the exception is no longer thrown. Guard any such expectation with `OperatingSystem.IsWindows()`. + +## Affected APIs + +- +- +- +- diff --git a/docs/core/compatibility/toc.yml b/docs/core/compatibility/toc.yml index a4a5373dd4248..4604fd02e5602 100644 --- a/docs/core/compatibility/toc.yml +++ b/docs/core/compatibility/toc.yml @@ -20,6 +20,8 @@ items: href: core-libraries/11/memorystream-max-capacity.md - name: API obsoletions with non-default diagnostic IDs href: core-libraries/11/obsolete-apis.md + - name: SafeFileHandle.IsAsync and FileStream.IsAsync accurately reflect non-blocking state on Unix + href: core-libraries/11/safefilehandle-isasync-unix.md - name: TAR-reading APIs verify header checksums when reading href: core-libraries/11/tar-checksum-validation.md - name: ZipArchive.CreateAsync eagerly loads ZIP archive entries From b90f9cb346f0f4bc30aa245d631ed646c04ea665 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:19:04 -0700 Subject: [PATCH 3/5] Fix SafeFileHandle.IsAsync behavior on Unix Corrected the behavior of SafeFileHandle.IsAsync on Unix to accurately reflect the non-blocking state of file descriptors. Updated documentation to clarify the changes and their implications. --- .../core-libraries/11/safefilehandle-isasync-unix.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md b/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md index 74c2d7d06ea46..0c55ae078f99b 100644 --- a/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md +++ b/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md @@ -26,8 +26,6 @@ using SafeFileHandle handle = File.OpenHandle("myfile.txt", options: FileOptions Console.WriteLine(handle.IsAsync); // true (misleading; no O_NONBLOCK on regular file) ``` -Conversely, for file descriptors that were genuinely non-blocking (for example, pipes or sockets with `O_NONBLOCK` set), `IsAsync` incorrectly returned `false`. - On non-Windows platforms, constructing a `SendPacketsElement` with a non-async `FileStream` threw an `ArgumentException`. ## New behavior @@ -37,7 +35,8 @@ Starting in .NET 11, `SafeFileHandle.IsAsync` on Unix returns `true` only when t ```csharp using Microsoft.Win32.SafeHandles; -// On Unix, IsAsync now reflects the actual non-blocking state of the file descriptor. +// On Unix, IsAsync now reflects the actual +// non-blocking state of the file descriptor. SafeFileHandle.CreateAnonymousPipe( out SafeFileHandle readHandle, out SafeFileHandle writeHandle, @@ -50,8 +49,6 @@ Console.WriteLine(writeHandle.IsAsync); // false (blocking write end) For regular files opened with `FileOptions.Asynchronous`, `IsAsync` correctly returns `false` on Unix because regular file I/O is inherently synchronous at the kernel level. -`FileStream.IsAsync` reflects the updated value of the underlying `SafeFileHandle.IsAsync`. - Additionally, on non-Windows platforms, constructing a `SendPacketsElement` with a `FileStream` no longer throws `ArgumentException` regardless of whether the stream is async. ## Type of breaking change @@ -60,7 +57,7 @@ This change is a [behavioral change](../../categories.md#behavioral-change). ## Reason for change -The previous behavior was incorrect and misleading. `SafeFileHandle.IsAsync` reported `true` on Unix for regular files that aren't truly non-blocking, and `false` for file descriptors (such as pipes or sockets) that genuinely had `O_NONBLOCK` set. This caused APIs and user code that relied on this property to make incorrect decisions. Accurate `IsAsync` reporting was also a prerequisite for the new `SafeFileHandle.CreateAnonymousPipe` API to correctly expose per-end async semantics on Unix. For more information, see [dotnet/runtime#125220](https://github.com/dotnet/runtime/pull/125220). +The previous behavior was incorrect and misleading. `SafeFileHandle.IsAsync` reported `false` for file descriptors (such as pipes or sockets) that genuinely had `O_NONBLOCK` set. This caused APIs and user code that relied on this property to make incorrect decisions. Accurate `IsAsync` reporting was also a prerequisite for the new `SafeFileHandle.CreateAnonymousPipe` API to correctly expose per-end async semantics on Unix. For more information, see [dotnet/runtime#125220](https://github.com/dotnet/runtime/pull/125220). ## Recommended action From 2a1f4e0f01fc356d0fb641c4fe39dd1a0d687235 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 20:28:19 +0000 Subject: [PATCH 4/5] Clarify Previous behavior section: cover pipe/socket O_NONBLOCK case Agent-Logs-Url: https://github.com/dotnet/docs/sessions/106a7ea2-8628-4d51-9f8a-e8a93476426d Co-authored-by: gewarren <24882762+gewarren@users.noreply.github.com> --- .../core-libraries/11/safefilehandle-isasync-unix.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md b/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md index 0c55ae078f99b..7d65646bfb0d2 100644 --- a/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md +++ b/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md @@ -15,7 +15,10 @@ On Unix, Date: Tue, 7 Apr 2026 13:40:19 -0700 Subject: [PATCH 5/5] Update documentation for SafeFileHandle.IsAsync behavior Clarified the behavior of SafeFileHandle.IsAsync on Unix regarding asynchronous and non-blocking file descriptors. --- .../core-libraries/11/safefilehandle-isasync-unix.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md b/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md index 7d65646bfb0d2..96291a2aae680 100644 --- a/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md +++ b/docs/core/compatibility/core-libraries/11/safefilehandle-isasync-unix.md @@ -15,10 +15,7 @@ On Unix,