Description
In .NET 11, SafeFileHandle.IsAsync on Unix no longer unconditionally returns true for regular files opened with FileOptions.Asynchronous. It now accurately reflects whether the underlying file descriptor is in non-blocking mode (O_NONBLOCK is set), which is possible only for pipes and sockets. The same applies to FileStream.IsAsync.
Related PR: dotnet/runtime#125220
Version
.NET 11 Preview 3
Previous behavior
SafeFileHandle.IsAsync always returned true on Unix for regular files opened with FileOptions.Asynchronous, even when the underlying file descriptor had no O_NONBLOCK set (non-blocking/async mode).
// On Unix, IsAsync was true for regular files opened with FileOptions.Asynchronous, regardless of the actual fd state
using SafeFileHandle handle = File.OpenHandle("myfile.txt", options: FileOptions.Asynchronous);
Console.WriteLine(handle.IsAsync);
New behavior
SafeFileHandle.IsAsync on Unix now returns true when the underlying file descriptor is in non-blocking mode (O_NONBLOCK is set), and false otherwise.
// On Unix, IsAsync now reflects the actual non-blocking state of the fd
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 on Unix opened with FileOptions.Asynchronous, IsAsync correctly returns false because regular file I/O on Unix is inherently synchronous at the kernel level.
Type of breaking change
Reason for change
The previous behavior was incorrect and misleading. SafeFileHandle.IsAsync reported false on Unix even for handles that were genuinely non-blocking, causing APIs and user code that relied on this property to make incorrect decisions. Accurate IsAsync reporting was a prerequisite for the new SafeFileHandle.CreateAnonymousPipe API to correctly expose per-end async semantics on Unix (also introduced in dotnet/runtime#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 false on Unix, that assumption no longer holds for non-blocking file descriptors. Update any logic that unconditionally expects IsAsync == false on non-Windows.
-
If you wrap a non-blocking SafeFileHandle in a FileStream (for example, one created via the new SafeFileHandle.CreateAnonymousPipe with asyncRead: true or asyncWrite: true), FileStream.IsAsync may 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 ArgumentException to be thrown for non-async streams, note that this exception is no longer thrown on non-Windows. Guard any such expectation with OperatingSystem.IsWindows().
Feature area
Core .NET libraries
Affected APIs
Microsoft.Win32.SafeHandles.SafeFileHandle.IsAsync
System.IO.FileStream.IsAsync
System.Net.Sockets.SendPacketsElement.SendPacketsElement(System.IO.FileStream, System.Int64, System.Int32) (behavioral change on non-Windows)
System.Net.Sockets.SendPacketsElement.SendPacketsElement(System.IO.FileStream, System.Int64, System.Int32, System.Boolean) (behavioral change on non-Windows)
Associated WorkItem - 568521
Description
In .NET 11,
SafeFileHandle.IsAsyncon Unix no longer unconditionally returnstruefor regular files opened withFileOptions.Asynchronous. It now accurately reflects whether the underlying file descriptor is in non-blocking mode (O_NONBLOCKis set), which is possible only for pipes and sockets. The same applies toFileStream.IsAsync.Related PR: dotnet/runtime#125220
Version
.NET 11 Preview 3
Previous behavior
SafeFileHandle.IsAsyncalways returnedtrueon Unix for regular files opened withFileOptions.Asynchronous, even when the underlying file descriptor had noO_NONBLOCKset (non-blocking/async mode).New behavior
SafeFileHandle.IsAsyncon Unix now returnstruewhen the underlying file descriptor is in non-blocking mode (O_NONBLOCKis set), andfalseotherwise.For regular files on Unix opened with
FileOptions.Asynchronous,IsAsynccorrectly returnsfalsebecause regular file I/O on Unix is inherently synchronous at the kernel level.Type of breaking change
Reason for change
The previous behavior was incorrect and misleading.
SafeFileHandle.IsAsyncreportedfalseon Unix even for handles that were genuinely non-blocking, causing APIs and user code that relied on this property to make incorrect decisions. AccurateIsAsyncreporting was a prerequisite for the newSafeFileHandle.CreateAnonymousPipeAPI to correctly expose per-end async semantics on Unix (also introduced in dotnet/runtime#125220).Recommended action
Review any code that checks
SafeFileHandle.IsAsyncorFileStream.IsAsyncon Unix and takes action based on the result:If your code assumed
IsAsyncwas alwaysfalseon Unix, that assumption no longer holds for non-blocking file descriptors. Update any logic that unconditionally expectsIsAsync == falseon non-Windows.If you wrap a non-blocking
SafeFileHandlein aFileStream(for example, one created via the newSafeFileHandle.CreateAnonymousPipewithasyncRead: trueorasyncWrite: true),FileStream.IsAsyncmay now returntruewhere it previously returnedfalse. Adjust downstream code accordingly.If you construct
SendPacketsElementwith aFileStreamon a non-Windows platform and previously expectedArgumentExceptionto be thrown for non-async streams, note that this exception is no longer thrown on non-Windows. Guard any such expectation withOperatingSystem.IsWindows().Feature area
Core .NET libraries
Affected APIs
Microsoft.Win32.SafeHandles.SafeFileHandle.IsAsyncSystem.IO.FileStream.IsAsyncSystem.Net.Sockets.SendPacketsElement.SendPacketsElement(System.IO.FileStream, System.Int64, System.Int32)(behavioral change on non-Windows)System.Net.Sockets.SendPacketsElement.SendPacketsElement(System.IO.FileStream, System.Int64, System.Int32, System.Boolean)(behavioral change on non-Windows)Associated WorkItem - 568521